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"
44#include "llvm/IR/LLVMContext.h"
45#include "llvm/IR/MDBuilder.h"
46#include "llvm/IR/Metadata.h"
48#include "llvm/IR/PassManager.h"
50#include "llvm/IR/Value.h"
53#include "llvm/Support/Error.h"
64
65#include <cstdint>
66#include <optional>
67
68#define DEBUG_TYPE "openmp-ir-builder"
69
70using namespace llvm;
71using namespace omp;
72
73static cl::opt<bool>
74 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
75 cl::desc("Use optimistic attributes describing "
76 "'as-if' properties of runtime calls."),
77 cl::init(false));
78
80 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
81 cl::desc("Factor for the unroll threshold to account for code "
82 "simplifications still taking place"),
83 cl::init(1.5));
84
86 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
87 cl::desc("Use a default max threads if none is provided."), cl::init(true));
88
89#ifndef NDEBUG
90/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
91/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
92/// an InsertPoint stores the instruction before something is inserted. For
93/// instance, if both point to the same instruction, two IRBuilders alternating
94/// creating instruction will cause the instructions to be interleaved.
97 if (!IP1.isValid() || !IP2.isValid())
98 return false;
99 return IP1 == IP2;
100}
101
103 // Valid ordered/unordered and base algorithm combinations.
104 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
105 case OMPScheduleType::UnorderedStaticChunked:
106 case OMPScheduleType::UnorderedStatic:
107 case OMPScheduleType::UnorderedDynamicChunked:
108 case OMPScheduleType::UnorderedGuidedChunked:
109 case OMPScheduleType::UnorderedRuntime:
110 case OMPScheduleType::UnorderedAuto:
111 case OMPScheduleType::UnorderedTrapezoidal:
112 case OMPScheduleType::UnorderedGreedy:
113 case OMPScheduleType::UnorderedBalanced:
114 case OMPScheduleType::UnorderedGuidedIterativeChunked:
115 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
116 case OMPScheduleType::UnorderedSteal:
117 case OMPScheduleType::UnorderedStaticBalancedChunked:
118 case OMPScheduleType::UnorderedGuidedSimd:
119 case OMPScheduleType::UnorderedRuntimeSimd:
120 case OMPScheduleType::OrderedStaticChunked:
121 case OMPScheduleType::OrderedStatic:
122 case OMPScheduleType::OrderedDynamicChunked:
123 case OMPScheduleType::OrderedGuidedChunked:
124 case OMPScheduleType::OrderedRuntime:
125 case OMPScheduleType::OrderedAuto:
126 case OMPScheduleType::OrderdTrapezoidal:
127 case OMPScheduleType::NomergeUnorderedStaticChunked:
128 case OMPScheduleType::NomergeUnorderedStatic:
129 case OMPScheduleType::NomergeUnorderedDynamicChunked:
130 case OMPScheduleType::NomergeUnorderedGuidedChunked:
131 case OMPScheduleType::NomergeUnorderedRuntime:
132 case OMPScheduleType::NomergeUnorderedAuto:
133 case OMPScheduleType::NomergeUnorderedTrapezoidal:
134 case OMPScheduleType::NomergeUnorderedGreedy:
135 case OMPScheduleType::NomergeUnorderedBalanced:
136 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
137 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
138 case OMPScheduleType::NomergeUnorderedSteal:
139 case OMPScheduleType::NomergeOrderedStaticChunked:
140 case OMPScheduleType::NomergeOrderedStatic:
141 case OMPScheduleType::NomergeOrderedDynamicChunked:
142 case OMPScheduleType::NomergeOrderedGuidedChunked:
143 case OMPScheduleType::NomergeOrderedRuntime:
144 case OMPScheduleType::NomergeOrderedAuto:
145 case OMPScheduleType::NomergeOrderedTrapezoidal:
146 case OMPScheduleType::OrderedDistributeChunked:
147 case OMPScheduleType::OrderedDistribute:
148 break;
149 default:
150 return false;
151 }
152
153 // Must not set both monotonicity modifiers at the same time.
154 OMPScheduleType MonotonicityFlags =
155 SchedType & OMPScheduleType::MonotonicityMask;
156 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
157 return false;
158
159 return true;
160}
161#endif
162
163/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
164/// debug location when the insert point is at the end of a block. It picks a
165/// location scoped to the current function: the block's last instruction
166/// location if the block is non-empty, otherwise a location synthesized from
167/// the function's subprogram (when the function has debug info).
170 Builder.restoreIP(IP);
171 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
172 // set the debug location from that instruction, so leave it alone.
173 llvm::BasicBlock *BB = Builder.GetInsertBlock();
174 if (Builder.GetInsertPoint() != BB->end())
175 return;
176
177 // At the end of a block, pick a location guaranteed to belong to the current
178 // insertion function's subprogram. Prefer the block's own last instruction;
179 // otherwise synthesize a location from the function's subprogram.
180 if (!BB->empty())
181 Builder.SetCurrentDebugLocation(BB->back().getDebugLoc());
182 else if (llvm::DISubprogram *FSP =
183 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
184 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
185 Builder.SetCurrentDebugLocation(
186 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
187 }
188}
189
190static bool hasGridValue(const Triple &T) {
191 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
192}
193
194static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
195 if (T.isAMDGPU()) {
196 StringRef Features =
197 Kernel->getFnAttribute("target-features").getValueAsString();
198 if (Features.count("+wavefrontsize64"))
201 }
202 if (T.isNVPTX())
204 if (T.isSPIRV())
206 llvm_unreachable("No grid value available for this architecture!");
207}
208
209/// Determine which scheduling algorithm to use, determined from schedule clause
210/// arguments.
211static OMPScheduleType
212getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
213 bool HasSimdModifier, bool HasDistScheduleChunks) {
214 // Currently, the default schedule it static.
215 switch (ClauseKind) {
216 case OMP_SCHEDULE_Default:
217 case OMP_SCHEDULE_Static:
218 return HasChunks ? OMPScheduleType::BaseStaticChunked
219 : OMPScheduleType::BaseStatic;
220 case OMP_SCHEDULE_Dynamic:
221 return OMPScheduleType::BaseDynamicChunked;
222 case OMP_SCHEDULE_Guided:
223 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
224 : OMPScheduleType::BaseGuidedChunked;
225 case OMP_SCHEDULE_Auto:
227 case OMP_SCHEDULE_Runtime:
228 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
229 : OMPScheduleType::BaseRuntime;
230 case OMP_SCHEDULE_Distribute:
231 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
232 : OMPScheduleType::BaseDistribute;
233 }
234 llvm_unreachable("unhandled schedule clause argument");
235}
236
237/// Adds ordering modifier flags to schedule type.
238static OMPScheduleType
240 bool HasOrderedClause) {
241 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
242 OMPScheduleType::None &&
243 "Must not have ordering nor monotonicity flags already set");
244
245 OMPScheduleType OrderingModifier = HasOrderedClause
246 ? OMPScheduleType::ModifierOrdered
247 : OMPScheduleType::ModifierUnordered;
248 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
249
250 // Unsupported combinations
251 if (OrderingScheduleType ==
252 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
253 return OMPScheduleType::OrderedGuidedChunked;
254 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
255 OMPScheduleType::ModifierOrdered))
256 return OMPScheduleType::OrderedRuntime;
257
258 return OrderingScheduleType;
259}
260
261/// Adds monotonicity modifier flags to schedule type.
262static OMPScheduleType
264 bool HasSimdModifier, bool HasMonotonic,
265 bool HasNonmonotonic, bool HasOrderedClause) {
266 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
267 OMPScheduleType::None &&
268 "Must not have monotonicity flags already set");
269 assert((!HasMonotonic || !HasNonmonotonic) &&
270 "Monotonic and Nonmonotonic are contradicting each other");
271
272 if (HasMonotonic) {
273 return ScheduleType | OMPScheduleType::ModifierMonotonic;
274 } else if (HasNonmonotonic) {
275 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
276 } else {
277 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
278 // If the static schedule kind is specified or if the ordered clause is
279 // specified, and if the nonmonotonic modifier is not specified, the
280 // effect is as if the monotonic modifier is specified. Otherwise, unless
281 // the monotonic modifier is specified, the effect is as if the
282 // nonmonotonic modifier is specified.
283 OMPScheduleType BaseScheduleType =
284 ScheduleType & ~OMPScheduleType::ModifierMask;
285 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
286 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
287 HasOrderedClause) {
288 // The monotonic is used by default in openmp runtime library, so no need
289 // to set it.
290 return ScheduleType;
291 } else {
292 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
293 }
294 }
295}
296
297/// Determine the schedule type using schedule and ordering clause arguments.
298static OMPScheduleType
299computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
300 bool HasSimdModifier, bool HasMonotonicModifier,
301 bool HasNonmonotonicModifier, bool HasOrderedClause,
302 bool HasDistScheduleChunks) {
304 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
305 OMPScheduleType OrderedSchedule =
306 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
308 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
309 HasNonmonotonicModifier, HasOrderedClause);
310
312 return Result;
313}
314
315/// Given a function, if it represents the entry point of a target kernel, this
316/// returns the execution mode flags associated with that kernel.
317static std::optional<omp::OMPTgtExecModeFlags>
319 CallInst *TargetInitCall = nullptr;
320 for (Instruction &Inst : Kernel.getEntryBlock()) {
321 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
322 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
323 TargetInitCall = Call;
324 break;
325 }
326 }
327 }
328
329 if (!TargetInitCall)
330 return std::nullopt;
331
332 // Get the kernel mode information from the global variable associated to the
333 // first argument to the call to __kmpc_target_init. Refer to
334 // createTargetInit() to see how this is initialized.
335 Value *InitOperand = TargetInitCall->getArgOperand(0);
336 GlobalVariable *KernelEnv = nullptr;
337 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
338 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
339 else
340 KernelEnv = cast<GlobalVariable>(InitOperand);
341 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
342 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
343 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
344 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
345}
346
347static bool isGenericKernel(Function &Fn) {
348 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
350 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
351}
352
353/// Make \p Source branch to \p Target.
354///
355/// Handles two situations:
356/// * \p Source already has an unconditional branch.
357/// * \p Source is a degenerate block (no terminator because the BB is
358/// the current head of the IR construction).
360 if (Instruction *Term = Source->getTerminatorOrNull()) {
361 auto *Br = cast<UncondBrInst>(Term);
362 BasicBlock *Succ = Br->getSuccessor();
363 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
364 Br->setSuccessor(Target);
365 return;
366 }
367
368 auto *NewBr = UncondBrInst::Create(Target, Source);
369 NewBr->setDebugLoc(DL);
370}
371
373 bool CreateBranch, DebugLoc DL) {
374 assert(New->getFirstInsertionPt() == New->begin() &&
375 "Target BB must not have PHI nodes");
376
377 // Move instructions to new block.
378 BasicBlock *Old = IP.getNodeParent();
379 // If the `Old` block is empty then there are no instructions to move. But in
380 // the new debug scheme, it could have trailing debug records which will be
381 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
382 // reasons:
383 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
384 // 2. Even if `New` is not empty, the rationale to move those records to `New`
385 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
386 // assumes that `Old` is optimized out and is going away. This is not the case
387 // here. The `Old` block is still being used e.g. a branch instruction is
388 // added to it later in this function.
389 // So we call `BasicBlock::splice` only when `Old` is not empty.
390 if (!Old->empty())
391 New->splice(New->begin(), Old, IP, Old->end());
392
393 if (CreateBranch) {
394 auto *NewBr = UncondBrInst::Create(New, Old);
395 NewBr->setDebugLoc(DL);
396 }
397}
398
399void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
400 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
401 BasicBlock *Old = Builder.GetInsertBlock();
402
403 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
404 if (CreateBranch)
405 Builder.SetInsertPoint(Old->getTerminator());
406 else
407 Builder.SetInsertPoint(Old);
408
409 // SetInsertPoint also updates the Builder's debug location, but we want to
410 // keep the one the Builder was configured to use.
411 Builder.SetCurrentDebugLocation(DebugLoc);
412}
413
415 DebugLoc DL, llvm::Twine Name) {
416 BasicBlock *Old = IP.getNodeParent();
418 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
419 Old->getParent(), Old->getNextNode());
420 spliceBB(IP, New, CreateBranch, DL);
421 New->replaceSuccessorsPhiUsesWith(Old, New);
422 return New;
423}
424
425BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
426 llvm::Twine Name) {
427 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
428 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
429 if (CreateBranch)
430 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
431 else
432 Builder.SetInsertPoint(Builder.GetInsertBlock());
433 // SetInsertPoint also updates the Builder's debug location, but we want to
434 // keep the one the Builder was configured to use.
435 Builder.SetCurrentDebugLocation(DebugLoc);
436 return New;
437}
438
439BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
440 llvm::Twine Name) {
441 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
442 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
443 if (CreateBranch)
444 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
445 else
446 Builder.SetInsertPoint(Builder.GetInsertBlock());
447 // SetInsertPoint also updates the Builder's debug location, but we want to
448 // keep the one the Builder was configured to use.
449 Builder.SetCurrentDebugLocation(DebugLoc);
450 return New;
451}
452
454 llvm::Twine Suffix) {
455 BasicBlock *Old = Builder.GetInsertBlock();
456 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
457}
458
459// This function creates a fake integer value and a fake use for the integer
460// value. It returns the fake value created. This is useful in modeling the
461// extra arguments to the outlined functions.
463 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
465 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
466 const Twine &Name = "", bool AsPtr = true,
467 bool Is64Bit = false) {
468 Builder.restoreIP(OuterAllocaIP);
469 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
470 Instruction *FakeVal;
471 AllocaInst *FakeValAddr =
472 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
473 ToBeDeleted.push_back(FakeValAddr);
474
475 if (AsPtr) {
476 FakeVal = FakeValAddr;
477 // The runtime passes these extra arguments to the outlined function as
478 // generic pointers, so cast away a non-zero alloca address space.
479 if (FakeValAddr->getAddressSpace() != 0) {
480 FakeVal = cast<Instruction>(Builder.CreateAddrSpaceCast(
481 FakeValAddr, Builder.getPtrTy(), Name + ".ascast"));
482 ToBeDeleted.push_back(FakeVal);
483 }
484 } else {
485 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
486 ToBeDeleted.push_back(FakeVal);
487 }
488
489 // Generate a fake use of this value
490 Builder.restoreIP(InnerAllocaIP);
491 Instruction *UseFakeVal;
492 if (AsPtr) {
493 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
494 } else {
495 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
496 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
497 }
498 ToBeDeleted.push_back(UseFakeVal);
499 return FakeVal;
500}
501
502//===----------------------------------------------------------------------===//
503// OpenMPIRBuilderConfig
504//===----------------------------------------------------------------------===//
505
506namespace {
508/// Values for bit flags for marking which requires clauses have been used.
509enum OpenMPOffloadingRequiresDirFlags {
510 /// flag undefined.
511 OMP_REQ_UNDEFINED = 0x000,
512 /// no requires directive present.
513 OMP_REQ_NONE = 0x001,
514 /// reverse_offload clause.
515 OMP_REQ_REVERSE_OFFLOAD = 0x002,
516 /// unified_address clause.
517 OMP_REQ_UNIFIED_ADDRESS = 0x004,
518 /// unified_shared_memory clause.
519 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
520 /// dynamic_allocators clause.
521 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
522 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
523};
524
525class OMPCodeExtractor : public CodeExtractor {
526public:
527 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
528 DominatorTree *DT = nullptr, bool AggregateArgs = false,
529 BlockFrequencyInfo *BFI = nullptr,
530 BranchProbabilityInfo *BPI = nullptr,
531 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
532 bool AllowAlloca = false,
533 BasicBlock *AllocationBlock = nullptr,
534 ArrayRef<BasicBlock *> DeallocationBlocks = {},
535 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
536 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
537 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
538 ArgsInZeroAddressSpace),
539 OMPBuilder(OMPBuilder) {}
540
541 virtual ~OMPCodeExtractor() = default;
542
543protected:
544 OpenMPIRBuilder &OMPBuilder;
545};
546
547class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
548public:
549 using OMPCodeExtractor::OMPCodeExtractor;
550 virtual ~DeviceSharedMemCodeExtractor() = default;
551
552protected:
553 virtual Instruction *
554 allocateVar(IRBuilder<>::InsertPoint AllocaIP, DebugLoc DL, Type *VarType,
555 const Twine &Name = Twine(""),
556 AddrSpaceCastInst **CastedAlloc = nullptr) override {
557 return OMPBuilder.createOMPAllocShared({AllocaIP, DL}, VarType, Name);
558 }
559
560 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
561 DebugLoc DL, Value *Var,
562 Type *VarType) override {
563 return OMPBuilder.createOMPFreeShared({DeallocIP, DL}, Var, VarType);
564 }
565};
566
567/// Helper storing information about regions to outline using device shared
568/// memory for intermediate allocations.
569struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
570 OpenMPIRBuilder &OMPBuilder;
571
572 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
573 : OMPBuilder(OMPBuilder) {}
574 virtual ~DeviceSharedMemOutlineInfo() = default;
575
576 virtual std::unique_ptr<CodeExtractor>
577 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
578 bool ArgsInZeroAddressSpace,
579 Twine Suffix = Twine("")) override;
580};
581
582} // anonymous namespace
583
585 : RequiresFlags(OMP_REQ_UNDEFINED) {}
586
589 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
590 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
593 RequiresFlags(OMP_REQ_UNDEFINED) {
594 if (HasRequiresReverseOffload)
595 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
596 if (HasRequiresUnifiedAddress)
597 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
598 if (HasRequiresUnifiedSharedMemory)
599 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
600 if (HasRequiresDynamicAllocators)
601 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
602}
603
605 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
606}
607
609 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
610}
611
613 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
614}
615
617 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
618}
619
621 return hasRequiresFlags() ? RequiresFlags
622 : static_cast<int64_t>(OMP_REQ_NONE);
623}
624
626 if (Value)
627 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
628 else
629 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
630}
631
633 if (Value)
634 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
635 else
636 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
637}
638
640 if (Value)
641 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
642 else
643 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
644}
645
647 if (Value)
648 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
649 else
650 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
651}
652
653//===----------------------------------------------------------------------===//
654// OpenMPIRBuilder
655//===----------------------------------------------------------------------===//
656
659 SmallVector<Value *> &ArgsVector) {
661 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
662 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
663 constexpr size_t MaxDim = 3;
664 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
665
666 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
667
668 Value *DynCGroupMemFallbackFlag =
669 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
670 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
671
672 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
673 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
674
675 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
676 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
677
678 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
679 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
680 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
681
682 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
683
684 Value *NumTeams3D =
685 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
686 Value *NumThreads3D =
687 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
688 for (unsigned I :
689 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
690 NumTeams3D =
691 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
692 for (unsigned I :
693 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
694 NumThreads3D =
695 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
696
697 ArgsVector = {Version,
698 PointerNum,
699 KernelArgs.RTArgs.BasePointersArray,
700 KernelArgs.RTArgs.PointersArray,
701 KernelArgs.RTArgs.SizesArray,
702 KernelArgs.RTArgs.MapTypesArray,
703 KernelArgs.RTArgs.MapNamesArray,
704 KernelArgs.RTArgs.MappersArray,
705 KernelArgs.NumIterations,
706 Flags,
707 NumTeams3D,
708 NumThreads3D,
709 KernelArgs.DynCGroupMem};
710}
711
713 LLVMContext &Ctx = Fn.getContext();
714
715 // Get the function's current attributes.
716 auto Attrs = Fn.getAttributes();
717 auto FnAttrs = Attrs.getFnAttrs();
718 auto RetAttrs = Attrs.getRetAttrs();
720 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
721 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
722
723 // Add AS to FnAS while taking special care with integer extensions.
724 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
725 bool Param = true) -> void {
726 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
727 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
728 if (HasSignExt || HasZeroExt) {
729 assert(AS.getNumAttributes() == 1 &&
730 "Currently not handling extension attr combined with others.");
731 if (Param) {
732 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
733 FnAS = FnAS.addAttribute(Ctx, AK);
734 } else if (auto AK =
735 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
736 FnAS = FnAS.addAttribute(Ctx, AK);
737 } else {
738 FnAS = FnAS.addAttributes(Ctx, AS);
739 }
740 };
741
742#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
743#include "llvm/Frontend/OpenMP/OMPKinds.def"
744
745 // Add attributes to the function declaration.
746 switch (FnID) {
747#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
748 case Enum: \
749 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
750 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
751 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
752 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
753 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
754 break;
755#include "llvm/Frontend/OpenMP/OMPKinds.def"
756 default:
757 // Attributes are optional.
758 break;
759 }
760}
761
764 FunctionType *FnTy = nullptr;
765 Function *Fn = nullptr;
766
767 // Try to find the declation in the module first.
768 switch (FnID) {
769#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
770 case Enum: \
771 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
772 IsVarArg); \
773 Fn = M.getFunction(Str); \
774 break;
775#include "llvm/Frontend/OpenMP/OMPKinds.def"
776 }
777
778 if (!Fn) {
779 // Create a new declaration if we need one.
780 switch (FnID) {
781#define OMP_RTL(Enum, Str, ...) \
782 case Enum: \
783 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
784 break;
785#include "llvm/Frontend/OpenMP/OMPKinds.def"
786 }
787 Fn->setCallingConv(Config.getRuntimeCC());
788 // Add information if the runtime function takes a callback function
789 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
790 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
791 LLVMContext &Ctx = Fn->getContext();
792 MDBuilder MDB(Ctx);
793 // Annotate the callback behavior of the runtime function:
794 // - The callback callee is argument number 2 (microtask).
795 // - The first two arguments of the callback callee are unknown (-1).
796 // - All variadic arguments to the runtime function are passed to the
797 // callback callee.
798 Fn->addMetadata(
799 LLVMContext::MD_callback,
801 2, {-1, -1}, /* VarArgsArePassed */ true)}));
802 }
803 }
804
805 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
806 << " with type " << *Fn->getFunctionType() << "\n");
807 addAttributes(FnID, *Fn);
808
809 } else {
810 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
811 << " with type " << *Fn->getFunctionType() << "\n");
812 }
813
814 assert(Fn && "Failed to create OpenMP runtime function");
815
816 return {FnTy, Fn};
817}
818
821 if (!FiniBB) {
822 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
824 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
825 Builder.SetInsertPoint(FiniBB);
826 // FiniCB adds the branch to the exit stub.
827 if (Error Err = FiniCB(Builder.saveIP()))
828 return Err;
829 }
830 return FiniBB;
831}
832
834 BasicBlock *OtherFiniBB) {
835 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
836 if (!FiniBB) {
837 FiniBB = OtherFiniBB;
838
839 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
840 if (Error Err = FiniCB(Builder.saveIP()))
841 return Err;
842
843 return Error::success();
844 }
845
846 // Move instructions from FiniBB to the start of OtherFiniBB.
847 auto EndIt = FiniBB->end();
848 if (FiniBB->size() >= 1)
849 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
850 EndIt = Prev;
851 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
852 EndIt);
853
854 FiniBB->replaceAllUsesWith(OtherFiniBB);
855 FiniBB->eraseFromParent();
856 FiniBB = OtherFiniBB;
857 return Error::success();
858}
859
862 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
863 assert(Fn && "Failed to create OpenMP runtime function pointer");
864 return Fn;
865}
866
869 StringRef Name) {
870 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
871 Call->setCallingConv(Config.getRuntimeCC());
872 return Call;
873}
874
875void OpenMPIRBuilder::initialize() { initializeTypes(M); }
876
879 BasicBlock &EntryBlock = Function->getEntryBlock();
880 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
881
882 // Loop over blocks looking for constant allocas, skipping the entry block
883 // as any allocas there are already in the desired location.
884 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
885 Block++) {
886 for (auto Inst = Block->getReverseIterator()->begin();
887 Inst != Block->getReverseIterator()->end();) {
889 Inst++;
891 continue;
892 AllocaInst->moveBeforePreserving(MoveLocInst);
893 } else {
894 Inst++;
895 }
896 }
897 }
898}
899
902
903 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
904 // TODO: For now, we support simple static allocations, we might need to
905 // move non-static ones as well. However, this will need further analysis to
906 // move the lenght arguments as well.
908 };
909
910 for (llvm::Instruction &Inst : Block)
912 if (ShouldHoistAlloca(*AllocaInst))
913 AllocasToMove.push_back(AllocaInst);
914
915 auto InsertPoint =
916 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
917
918 for (llvm::Instruction *AllocaInst : AllocasToMove)
920}
921
923 PostDominatorTree PostDomTree(*Func);
924 for (llvm::BasicBlock &BB : *Func)
925 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
927}
928
930 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
932 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
933 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
934 // Skip functions that have not finalized yet; may happen with nested
935 // function generation.
936 if (Fn && OI->getFunction() != Fn) {
937 DeferredOutlines.push_back(std::move(OI));
938 continue;
939 }
940
941 ParallelRegionBlockSet.clear();
942 Blocks.clear();
943 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
944
945 Function *OuterFn = OI->getFunction();
946 CodeExtractorAnalysisCache CEAC(*OuterFn);
947 // If we generate code for the target device, we need to allocate
948 // struct for aggregate params in the device default alloca address space.
949 // OpenMP runtime requires that the params of the extracted functions are
950 // passed as zero address space pointers. This flag ensures that
951 // CodeExtractor generates correct code for extracted functions
952 // which are used by OpenMP runtime.
953 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
954 std::unique_ptr<CodeExtractor> Extractor =
955 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
956
957 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
958 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
959 << " Exit: " << OI->ExitBB->getName() << "\n");
960 assert(Extractor->isEligible() &&
961 "Expected OpenMP outlining to be possible!");
962
963 for (auto *V : OI->ExcludeArgsFromAggregate)
964 Extractor->excludeArgFromAggregate(V);
965
966 Function *OutlinedFn =
967 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
968
969 // Forward target-cpu, target-features attributes to the outlined function.
970 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
971 if (TargetCpuAttr.isStringAttribute())
972 OutlinedFn->addFnAttr(TargetCpuAttr);
973
974 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
975 if (TargetFeaturesAttr.isStringAttribute())
976 OutlinedFn->addFnAttr(TargetFeaturesAttr);
977
978 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
979 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
980 assert(OutlinedFn->getReturnType()->isVoidTy() &&
981 "OpenMP outlined functions should not return a value!");
982
983 // For compability with the clang CG we move the outlined function after the
984 // one with the parallel region.
985 OutlinedFn->removeFromParent();
986 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
987
988 // Remove the artificial entry introduced by the extractor right away, we
989 // made our own entry block after all.
990 {
991 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
992 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
993 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
994 // Move instructions from the to-be-deleted ArtificialEntry to the entry
995 // basic block of the parallel region. CodeExtractor generates
996 // instructions to unwrap the aggregate argument and may sink
997 // allocas/bitcasts for values that are solely used in the outlined region
998 // and do not escape.
999 assert(!ArtificialEntry.empty() &&
1000 "Expected instructions to add in the outlined region entry");
1001 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
1002 End = ArtificialEntry.rend();
1003 It != End;) {
1004 Instruction &I = *It;
1005 It++;
1006
1007 if (I.isTerminator()) {
1008 // Absorb any debug value that terminator may have
1009 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1010 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1011 continue;
1012 }
1013
1014 I.moveBeforePreserving(*OI->EntryBB,
1015 OI->EntryBB->getFirstInsertionPt());
1016 }
1017
1018 OI->EntryBB->moveBefore(&ArtificialEntry);
1019 ArtificialEntry.eraseFromParent();
1020 }
1021 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1022 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1023
1024 // Run a user callback, e.g. to add attributes.
1025 if (OI->PostOutlineCB)
1026 OI->PostOutlineCB(*OutlinedFn);
1027
1028 if (OI->FixUpNonEntryAllocas)
1030 }
1031
1032 // Remove work items that have been completed.
1033 OutlineInfos = std::move(DeferredOutlines);
1034
1035 // The createTarget functions embeds user written code into
1036 // the target region which may inject allocas which need to
1037 // be moved to the entry block of our target or risk malformed
1038 // optimisations by later passes, this is only relevant for
1039 // the device pass which appears to be a little more delicate
1040 // when it comes to optimisations (however, we do not block on
1041 // that here, it's up to the inserter to the list to do so).
1042 // This notbaly has to occur after the OutlinedInfo candidates
1043 // have been extracted so we have an end product that will not
1044 // be implicitly adversely affected by any raises unless
1045 // intentionally appended to the list.
1046 // NOTE: This only does so for ConstantData, it could be extended
1047 // to ConstantExpr's with further effort, however, they should
1048 // largely be folded when they get here. Extending it to runtime
1049 // defined/read+writeable allocation sizes would be non-trivial
1050 // (need to factor in movement of any stores to variables the
1051 // allocation size depends on, as well as the usual loads,
1052 // otherwise it'll yield the wrong result after movement) and
1053 // likely be more suitable as an LLVM optimisation pass.
1056
1057 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1058 [](EmitMetadataErrorKind Kind,
1059 const TargetRegionEntryInfo &EntryInfo) -> void {
1060 errs() << "Error of kind: " << Kind
1061 << " when emitting offload entries and metadata during "
1062 "OMPIRBuilder finalization \n";
1063 };
1064
1065 if (!OffloadInfoManager.empty())
1067
1068 // Rewrite uses of globals to their replacement declare target globals if
1069 // we are processing a device module.
1070 if (Config.isTargetDevice())
1071 applyDeclareTargetGlobalReplacements();
1072
1073 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1074 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1075 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1076 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1077 }
1078
1079 IsFinalized = true;
1080}
1081
1082bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1083
1085 GlobalValue *Original, GlobalValue *Replacement) {
1086 assert(Original && Replacement &&
1087 "Null values provided to registerDeclareTargetGlobalReplacement");
1088 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1089}
1090
1091void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1092 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1093 GlobalValue *OldGV = R.Original;
1094 GlobalValue *NewGV = R.Replacement;
1095
1096 assert(OldGV && NewGV &&
1097 "A null value was inserted into DeclareTargetGlobalReplacements");
1098
1099 // The assert above should catch this case, but this is kept to attempt
1100 // to proceed without issue when asserts are off.
1101 if (!OldGV || !NewGV)
1102 continue;
1103
1104 // The replacement global is a reference pointer that holds the
1105 // address of the device-resident storage. Every use must load the
1106 // reference pointer first and use the loaded address.
1107 //
1108 // Constant expression users (e.g. a constant GEP embedded in another
1109 // global's initializer or in an instruction) cannot have a load inserted
1110 // in place, so first expand any constant-expression users that live inside
1111 // functions into instructions. Any remaining constant users are handled
1112 // via a direct constant rewrite below as we cannot materialize a load
1113 // there.
1114 //
1115 // NOTE: We extend the constant rewrite to module scope, as we replace all
1116 // usages.
1117 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1119 /*RestrictToFunc=*/nullptr,
1120 /*RemoveDeadConstants=*/false);
1121
1122 IRBuilderBase::InsertPointGuard Guard(Builder);
1124 for (User *U : Users) {
1125 auto *Insn = dyn_cast<Instruction>(U);
1126 if (!Insn)
1127 continue;
1128
1129 // A PHI node cannot have a load inserted immediately before it, as PHIs
1130 // must remain grouped at the top of their basic block. So we need to
1131 // make sure any loads we emit are generated in the preceding edge, a
1132 // PHI may reference the global on more than one edge, so every matching
1133 // slot must be handled.
1134 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1135 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1136 if (PHI->getIncomingValue(I) != OldGV)
1137 continue;
1138
1139 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1140 Builder.SetInsertPoint(IncomingBB->getTerminator());
1141 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1142 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1143 PHI->setIncomingValue(I, EdgeLoad);
1144 }
1145 continue;
1146 }
1147
1148 Builder.SetInsertPoint(Insn);
1149 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1150 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1151
1152 // The replacement declare target global lives in the default address
1153 // space, whereas the original global may reside in a non-default
1154 // address space. In that case the initial lowering may have
1155 // emitted an addrspacecast that is no longer valid. Replace the
1156 // whole addrspacecast with the load and erase it rather than
1157 // feeding the load back into the (now pointless) cast.
1158 // NOTE: If we end up with replacement declare target globals in
1159 // non-zero AS's the below will need some minor extensions to have the
1160 // option to alter the address space cast to the new address space where
1161 // required rather than just replacing it.
1162 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1163 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1164 assert(NewGVAS == 0 &&
1165 "Non-default address space declare target global");
1166 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1167 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1168 if (DestAS == 0 && NewGVAS != OldGVAS) {
1169 ASC->replaceAllUsesWith(Load);
1170 ASC->eraseFromParent();
1171 continue;
1172 }
1173 }
1174
1175 Insn->replaceUsesOfWith(OldGV, Load);
1176 }
1177 }
1178
1180}
1181
1183 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1184}
1185
1187 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1188 auto *GV =
1189 new GlobalVariable(M, I32Ty,
1190 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1191 ConstantInt::get(I32Ty, Value), Name);
1192 GV->setVisibility(GlobalValue::HiddenVisibility);
1193
1194 return GV;
1195}
1196
1198 if (List.empty())
1199 return;
1200
1201 // Convert List to what ConstantArray needs.
1203 UsedArray.resize(List.size());
1204 for (unsigned I = 0, E = List.size(); I != E; ++I)
1206 cast<Constant>(&*List[I]), Builder.getPtrTy());
1207
1208 if (UsedArray.empty())
1209 return;
1210 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1211
1212 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1213 ConstantArray::get(ATy, UsedArray), Name);
1214
1215 GV->setSection("llvm.metadata");
1216}
1217
1220 OMPTgtExecModeFlags Mode) {
1221 auto *Int8Ty = Builder.getInt8Ty();
1222 auto *GVMode = new GlobalVariable(
1223 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1224 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1225 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1226 return GVMode;
1227}
1228
1230 uint32_t SrcLocStrSize,
1231 IdentFlag LocFlags,
1232 unsigned Reserve2Flags) {
1233 // Enable "C-mode".
1234 LocFlags |= OMP_IDENT_FLAG_KMPC;
1235
1236 Constant *&Ident =
1237 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1238 if (!Ident) {
1239 Constant *I32Null = ConstantInt::getNullValue(Int32);
1240 Constant *IdentData[] = {I32Null,
1241 ConstantInt::get(Int32, uint32_t(LocFlags)),
1242 ConstantInt::get(Int32, Reserve2Flags),
1243 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1244
1245 size_t SrcLocStrArgIdx = 4;
1246 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1248 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1249 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1250 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1251 Constant *Initializer =
1252 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1253
1254 // Look for existing encoding of the location + flags, not needed but
1255 // minimizes the difference to the existing solution while we transition.
1256 for (GlobalVariable &GV : M.globals())
1257 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1258 if (GV.getInitializer() == Initializer)
1259 Ident = &GV;
1260
1261 if (!Ident) {
1262 auto *GV = new GlobalVariable(
1263 M, OpenMPIRBuilder::Ident,
1264 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1266 M.getDataLayout().getDefaultGlobalsAddressSpace());
1267 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1268 GV->setAlignment(Align(8));
1269 Ident = GV;
1270 }
1271 }
1272
1273 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1274}
1275
1277 uint32_t &SrcLocStrSize) {
1278 SrcLocStrSize = LocStr.size();
1279 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1280 if (!SrcLocStr) {
1281 Constant *Initializer =
1282 ConstantDataArray::getString(M.getContext(), LocStr);
1283
1284 // Look for existing encoding of the location, not needed but minimizes the
1285 // difference to the existing solution while we transition.
1286 for (GlobalVariable &GV : M.globals())
1287 if (GV.isConstant() && GV.hasInitializer() &&
1288 GV.getInitializer() == Initializer)
1289 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1290
1291 SrcLocStr = Builder.CreateGlobalString(
1292 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1293 &M);
1294 }
1295 return SrcLocStr;
1296}
1297
1299 StringRef FileName,
1300 unsigned Line, unsigned Column,
1301 uint32_t &SrcLocStrSize) {
1302 SmallString<128> Buffer;
1303 Buffer.push_back(';');
1304 Buffer.append(FileName);
1305 Buffer.push_back(';');
1306 Buffer.append(FunctionName);
1307 Buffer.push_back(';');
1308 Buffer.append(std::to_string(Line));
1309 Buffer.push_back(';');
1310 Buffer.append(std::to_string(Column));
1311 Buffer.push_back(';');
1312 Buffer.push_back(';');
1313 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1314}
1315
1316Constant *
1318 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1319 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1320}
1321
1323 uint32_t &SrcLocStrSize,
1324 const Function *F) {
1325 DILocation *DIL = DL.get();
1326 if (!DIL)
1327 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1328 StringRef FileName =
1329 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1330 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1331 if (Function.empty() && F)
1332 Function = F->getName();
1333 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1334 DIL->getColumn(), SrcLocStrSize);
1335}
1336
1338 uint32_t &SrcLocStrSize) {
1339 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1340 Loc.IP.getNodeParent()->getParent());
1341}
1342
1345 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1346 "omp_global_thread_num");
1347}
1348
1349OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1350 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1351 ArrayRef<Type *> ResultPtrTys,
1352 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1353 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1354 "expected one result pointer type per in_reduction item");
1355 if (!updateToLocation(Loc))
1356 return Loc.IP;
1357 if (OrigPtrs.empty())
1358 return Builder.saveIP();
1359
1360 // Compute the executing thread's gtid once for the whole target body and
1361 // reuse it for every in_reduction lookup, so a target with several
1362 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1363 // item.
1364 uint32_t SrcLocStrSize;
1365 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1366 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1367 Value *Gtid = getOrCreateThreadID(Ident);
1368
1369 // The runtime entry point takes (and returns) a generic, default-address-
1370 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1371 // taskgroups to find the matching task_reduction registration for the item.
1372 Type *PtrTy = PointerType::getUnqual(M.getContext());
1373 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1374 FunctionCallee GetThData =
1375 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1376
1377 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1378 // Normalize a non-default-address-space original pointer to the generic
1379 // address space before the call.
1380 Value *OrigPtr = OrigPtrs[Idx];
1381 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1382 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1383 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1384
1385 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1386 "omp.inred.priv");
1387
1388 // Cast the returned private pointer back to the requested address space
1389 // when it differs.
1390 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1391 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1392 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1393
1394 MapPrivateCB(Idx, Priv);
1395 }
1396 return Builder.saveIP();
1397}
1398
1401 bool ForceSimpleCall, bool CheckCancelFlag) {
1402 if (!updateToLocation(Loc))
1403 return Loc.IP;
1404
1405 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1406 // __kmpc_barrier(loc, thread_id);
1407
1408 IdentFlag BarrierLocFlags;
1409 switch (Kind) {
1410 case OMPD_for:
1411 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1412 break;
1413 case OMPD_sections:
1414 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1415 break;
1416 case OMPD_single:
1417 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1418 break;
1419 case OMPD_barrier:
1420 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1421 break;
1422 default:
1423 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1424 break;
1425 }
1426
1427 uint32_t SrcLocStrSize;
1428 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1429 Value *Args[] = {
1430 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1431 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1432
1433 // If we are in a cancellable parallel region, barriers are cancellation
1434 // points.
1435 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1436 bool UseCancelBarrier =
1437 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1438
1440 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1441 ? OMPRTL___kmpc_cancel_barrier
1442 : OMPRTL___kmpc_barrier),
1443 Args);
1444
1445 if (UseCancelBarrier && CheckCancelFlag)
1446 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1447 return Err;
1448
1449 return Builder.saveIP();
1450}
1451
1454 Value *IfCondition,
1455 omp::Directive CanceledDirective) {
1456 if (!updateToLocation(Loc))
1457 return Loc.IP;
1458
1459 // LLVM utilities like blocks with terminators.
1460 auto *UI = Builder.CreateUnreachable();
1461
1462 Instruction *ThenTI = UI, *ElseTI = nullptr;
1463 if (IfCondition) {
1464 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1465
1466 // Even if the if condition evaluates to false, this should count as a
1467 // cancellation point
1468 Builder.SetInsertPoint(ElseTI);
1469 auto ElseIP = Builder.saveIP();
1470
1472 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1473 if (!IPOrErr)
1474 return IPOrErr;
1475 }
1476
1477 Builder.SetInsertPoint(ThenTI);
1478
1479 Value *CancelKind = nullptr;
1480 switch (CanceledDirective) {
1481#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1482 case DirectiveEnum: \
1483 CancelKind = Builder.getInt32(Value); \
1484 break;
1485#include "llvm/Frontend/OpenMP/OMPKinds.def"
1486 default:
1487 llvm_unreachable("Unknown cancel kind!");
1488 }
1489
1490 uint32_t SrcLocStrSize;
1491 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1492 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1493 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1495 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1496
1497 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1498 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1499 return Err;
1500
1501 // Update the insertion point and remove the terminator we introduced.
1502 Builder.SetInsertPoint(UI->getParent());
1503 UI->eraseFromParent();
1504
1505 return Builder.saveIP();
1506}
1507
1510 omp::Directive CanceledDirective) {
1511 if (!updateToLocation(Loc))
1512 return Loc.IP;
1513
1514 // LLVM utilities like blocks with terminators.
1515 auto *UI = Builder.CreateUnreachable();
1516 Builder.SetInsertPoint(UI);
1517
1518 Value *CancelKind = nullptr;
1519 switch (CanceledDirective) {
1520#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1521 case DirectiveEnum: \
1522 CancelKind = Builder.getInt32(Value); \
1523 break;
1524#include "llvm/Frontend/OpenMP/OMPKinds.def"
1525 default:
1526 llvm_unreachable("Unknown cancel kind!");
1527 }
1528
1529 uint32_t SrcLocStrSize;
1530 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1531 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1532 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1534 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1535
1536 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1537 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1538 return Err;
1539
1540 // Update the insertion point and remove the terminator we introduced.
1541 Builder.SetInsertPoint(UI->getParent());
1542 UI->eraseFromParent();
1543
1544 return Builder.saveIP();
1545}
1546
1548 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1549 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1550 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1551 if (!updateToLocation(Loc))
1552 return Loc.IP;
1553
1554 Builder.restoreIP(AllocaIP);
1555 auto *KernelArgsPtr =
1556 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1558
1559 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1560 llvm::Value *Arg =
1561 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1562 Builder.CreateAlignedStore(
1563 KernelArgs[I], Arg,
1564 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1565 }
1566
1567 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1568 NumThreads, HostPtr, KernelArgsPtr};
1569
1571 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1572 OffloadingArgs);
1573
1574 return Builder.saveIP();
1575}
1576
1578 const LocationDescription &Loc, Value *OutlinedFnID,
1579 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1580 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1581
1582 if (!updateToLocation(Loc))
1583 return Loc.IP;
1584
1585 // On top of the arrays that were filled up, the target offloading call
1586 // takes as arguments the device id as well as the host pointer. The host
1587 // pointer is used by the runtime library to identify the current target
1588 // region, so it only has to be unique and not necessarily point to
1589 // anything. It could be the pointer to the outlined function that
1590 // implements the target region, but we aren't using that so that the
1591 // compiler doesn't need to keep that, and could therefore inline the host
1592 // function if proven worthwhile during optimization.
1593
1594 // From this point on, we need to have an ID of the target region defined.
1595 assert(OutlinedFnID && "Invalid outlined function ID!");
1596 (void)OutlinedFnID;
1597
1598 // Return value of the runtime offloading call.
1599 Value *Return = nullptr;
1600
1601 // Arguments for the target kernel.
1602 SmallVector<Value *> ArgsVector;
1603 getKernelArgsVector(Args, Builder, ArgsVector);
1604
1605 // The target region is an outlined function launched by the runtime
1606 // via calls to __tgt_target_kernel().
1607 //
1608 // Note that on the host and CPU targets, the runtime implementation of
1609 // these calls simply call the outlined function without forking threads.
1610 // The outlined functions themselves have runtime calls to
1611 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1612 // the compiler in emitTeamsCall() and emitParallelCall().
1613 //
1614 // In contrast, on the NVPTX target, the implementation of
1615 // __tgt_target_teams() launches a GPU kernel with the requested number
1616 // of teams and threads so no additional calls to the runtime are required.
1617 // Check the error code and execute the host version if required.
1618 Builder.restoreIP(emitTargetKernel(
1619 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1620 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1621
1622 BasicBlock *OffloadFailedBlock =
1623 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1624 BasicBlock *OffloadContBlock =
1625 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1626 Value *Failed = Builder.CreateIsNotNull(Return);
1627 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1628
1629 auto CurFn = Builder.GetInsertBlock()->getParent();
1630 emitBlock(OffloadFailedBlock, CurFn);
1631 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1632 if (!AfterIP)
1633 return AfterIP.takeError();
1634 Builder.restoreIP(*AfterIP);
1635 emitBranch(OffloadContBlock);
1636 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1637 return Builder.saveIP();
1638}
1639
1641 Value *CancelFlag, omp::Directive CanceledDirective) {
1642 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1643 "Unexpected cancellation!");
1644
1645 // For a cancel barrier we create two new blocks.
1646 BasicBlock *BB = Builder.GetInsertBlock();
1647 BasicBlock *NonCancellationBlock;
1648 if (Builder.GetInsertPoint() == BB->end()) {
1649 // TODO: This branch will not be needed once we moved to the
1650 // OpenMPIRBuilder codegen completely.
1651 NonCancellationBlock = BasicBlock::Create(
1652 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1653 } else {
1654 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1656 Builder.SetInsertPoint(BB);
1657 }
1658 BasicBlock *CancellationBlock = BasicBlock::Create(
1659 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1660
1661 // Jump to them based on the return value.
1662 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1663 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1664 /* TODO weight */ nullptr, nullptr);
1665
1666 // From the cancellation block we finalize all variables and go to the
1667 // post finalization block that is known to the FiniCB callback.
1668 auto &FI = FinalizationStack.back();
1669 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1670 if (!FiniBBOrErr)
1671 return FiniBBOrErr.takeError();
1672 Builder.SetInsertPoint(CancellationBlock);
1673 Builder.CreateBr(*FiniBBOrErr);
1674
1675 // The continuation block is where code generation continues.
1676 Builder.SetInsertPoint(NonCancellationBlock->begin());
1677 return Error::success();
1678}
1679
1680/// Create wrapper function used to gather the outlined function's argument
1681/// structure from a shared buffer and to forward them to it when running in
1682/// Generic mode.
1683///
1684/// The outlined function is expected to receive 2 integer arguments followed by
1685/// an optional pointer argument to an argument structure holding the rest.
1687 Function &OutlinedFn) {
1688 size_t NumArgs = OutlinedFn.arg_size();
1689 assert((NumArgs == 2 || NumArgs == 3) &&
1690 "expected a 2-3 argument parallel outlined function");
1691 bool UseArgStruct = NumArgs == 3;
1692
1693 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1694 IRBuilder<>::InsertPointGuard IPG(Builder);
1695 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1696 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1697 /*isVarArg=*/false);
1698 auto *WrapperFn =
1700 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1701
1702 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1703 WrapperFn->addParamAttr(0, Attribute::ZExt);
1704 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1705
1706 BasicBlock *EntryBB =
1707 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1708 Builder.SetInsertPoint(EntryBB);
1709
1710 // Allocation.
1711 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1712 /*ArraySize=*/nullptr, "addr");
1713 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1714 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1715 AddrAlloca->getName() + ".ascast");
1716
1717 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1718 /*ArraySize=*/nullptr, "zero");
1719 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1720 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1721 ZeroAlloca->getName() + ".ascast");
1722
1723 Value *ArgsAlloca = nullptr;
1724 if (UseArgStruct) {
1725 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1726 /*ArraySize=*/nullptr, "global_args");
1727 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1728 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1729 ArgsAlloca->getName() + ".ascast");
1730 }
1731
1732 // Initialization.
1733 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1734 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1735 if (UseArgStruct) {
1736 Builder.CreateCall(
1737 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1738 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1739 {ArgsAlloca});
1740 }
1741
1742 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1743
1744 // Load structArg from global_args.
1745 if (UseArgStruct) {
1746 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1747 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1748 {Builder.getInt64(0)});
1749 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1750 Args.push_back(StructArg);
1751 }
1752
1753 // Call the outlined function holding the parallel body.
1754 Builder.CreateCall(&OutlinedFn, Args);
1755 Builder.CreateRetVoid();
1756
1757 return WrapperFn;
1758}
1759
1760// Callback used to create OpenMP runtime calls to support
1761// omp parallel clause for the device.
1762// We need to use this callback to replace call to the OutlinedFn in OuterFn
1763// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1765 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1766 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1767 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1768 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1769 assert(OutlinedFn.arg_size() >= 2 &&
1770 "Expected at least tid and bounded tid as arguments");
1771 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1772
1773 // Add some known attributes.
1774 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1775 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1776 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1777 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1778 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1779 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1780
1781 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1782 assert(CI && "Expected call instruction to outlined function");
1783 CI->getParent()->setName("omp_parallel");
1784
1785 Builder.SetInsertPoint(CI);
1786 Type *PtrTy = OMPIRBuilder->VoidPtr;
1787
1788 // Add alloca for kernel args
1789 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1790 Builder.SetInsertPoint(OuterAllocaBB->getFirstInsertionPt());
1791 AllocaInst *ArgsAlloca =
1792 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1793 Value *Args = ArgsAlloca;
1794 // Add address space cast if array for storing arguments is not allocated
1795 // in address space 0
1796 if (ArgsAlloca->getAddressSpace())
1797 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1798 Builder.restoreIP(CurrentIP);
1799
1800 // Store captured vars which are used by kmpc_parallel_60
1801 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1802 Value *V = *(CI->arg_begin() + 2 + Idx);
1803 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1804 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1805 Builder.CreateStore(V, StoreAddress);
1806 }
1807
1808 Value *Cond =
1809 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1810 : Builder.getInt32(1);
1811 Value *NumThreadsArg =
1812 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1813 : Builder.getInt32(-1);
1814
1815 // If this is not a Generic kernel, we can skip generating the wrapper.
1816 Value *WrapperFn;
1817 if (isGenericKernel(*OuterFn))
1818 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1819 else
1820 WrapperFn = Constant::getNullValue(PtrTy);
1821
1822 // Build kmpc_parallel_60 call
1823 Value *Parallel60CallArgs[] = {
1824 /* identifier*/ Ident,
1825 /* global thread num*/ ThreadID,
1826 /* if expression */ Cond,
1827 /* number of threads */ NumThreadsArg,
1828 /* Proc bind */ Builder.getInt32(-1),
1829 /* outlined function */ &OutlinedFn,
1830 /* wrapper function */ WrapperFn,
1831 /* arguments of the outlined funciton*/ Args,
1832 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1833 /* strict for number of threads */ Builder.getInt32(0)};
1834
1835 FunctionCallee RTLFn =
1836 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1837
1838 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1839
1840 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1841 << *Builder.GetInsertBlock()->getParent() << "\n");
1842
1843 // Initialize the local TID stack location with the argument value.
1844 Builder.SetInsertPoint(PrivTID);
1845 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1846 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1847 PrivTIDAddr);
1848
1849 // Remove redundant call to the outlined function.
1850 CI->eraseFromParent();
1851
1852 for (Instruction *I : ToBeDeleted) {
1853 I->eraseFromParent();
1854 }
1855}
1856
1857// Callback used to create OpenMP runtime calls to support
1858// omp parallel clause for the host.
1859// We need to use this callback to replace call to the OutlinedFn in OuterFn
1860// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1861static void
1863 Function *OuterFn, Value *Ident, Value *IfCondition,
1864 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1865 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1866 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1867 FunctionCallee RTLFn;
1868 if (IfCondition) {
1869 RTLFn =
1870 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1871 } else {
1872 RTLFn =
1873 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1874 }
1875 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1876 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1877 LLVMContext &Ctx = F->getContext();
1878 MDBuilder MDB(Ctx);
1879 // Annotate the callback behavior of the __kmpc_fork_call:
1880 // - The callback callee is argument number 2 (microtask).
1881 // - The first two arguments of the callback callee are unknown (-1).
1882 // - All variadic arguments to the __kmpc_fork_call are passed to the
1883 // callback callee.
1884 F->addMetadata(LLVMContext::MD_callback,
1886 2, {-1, -1},
1887 /* VarArgsArePassed */ true)}));
1888 }
1889 }
1890 // Add some known attributes.
1891 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1892 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1893 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1894
1895 assert(OutlinedFn.arg_size() >= 2 &&
1896 "Expected at least tid and bounded tid as arguments");
1897 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1898
1899 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1900 CI->getParent()->setName("omp_parallel");
1901 Builder.SetInsertPoint(CI);
1902
1903 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1904 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1905 &OutlinedFn};
1906
1907 SmallVector<Value *, 16> RealArgs;
1908 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1909 if (IfCondition) {
1910 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1911 RealArgs.push_back(Cond);
1912 }
1913 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1914
1915 // __kmpc_fork_call_if always expects a void ptr as the last argument
1916 // If there are no arguments, pass a null pointer.
1917 auto PtrTy = OMPIRBuilder->VoidPtr;
1918 if (IfCondition && NumCapturedVars == 0) {
1919 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1920 RealArgs.push_back(NullPtrValue);
1921 }
1922
1923 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1924
1925 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1926 << *Builder.GetInsertBlock()->getParent() << "\n");
1927
1928 // Initialize the local TID stack location with the argument value.
1929 Builder.SetInsertPoint(PrivTID);
1930 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1931 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1932 PrivTIDAddr);
1933
1934 // Remove redundant call to the outlined function.
1935 CI->eraseFromParent();
1936
1937 for (Instruction *I : ToBeDeleted) {
1938 I->eraseFromParent();
1939 }
1940}
1941
1943 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1944 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1945 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1946 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1947 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1948
1949 if (!updateToLocation(Loc))
1950 return Loc.IP;
1951
1952 uint32_t SrcLocStrSize;
1953 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1954 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1955 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1956 (ProcBind != OMP_PROC_BIND_default);
1957 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1958 // If we generate code for the target device, we need to allocate
1959 // struct for aggregate params in the device default alloca address space.
1960 // OpenMP runtime requires that the params of the extracted functions are
1961 // passed as zero address space pointers. This flag ensures that extracted
1962 // function arguments are declared in zero address space
1963 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1964
1965 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1966 // only if we compile for host side.
1967 if (NumThreads && !Config.isTargetDevice()) {
1968 Value *Args[] = {
1969 Ident, ThreadID,
1970 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1972 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1973 }
1974
1975 if (ProcBind != OMP_PROC_BIND_default) {
1976 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1977 Value *Args[] = {
1978 Ident, ThreadID,
1979 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1981 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1982 }
1983
1984 BasicBlock *InsertBB = Builder.GetInsertBlock();
1985 Function *OuterFn = InsertBB->getParent();
1986
1987 // Save the outer alloca block because the insertion iterator may get
1988 // invalidated and we still need this later.
1989 BasicBlock *OuterAllocaBlock = OuterAllocIP.getNodeParent();
1990
1991 // Vector to remember instructions we used only during the modeling but which
1992 // we want to delete at the end.
1994
1995 // Change the location to the outer alloca insertion point to create and
1996 // initialize the allocas we pass into the parallel region.
1997 InsertPointTy NewOuter(OuterAllocaBlock->begin());
1998 Builder.restoreIP(NewOuter);
1999 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
2000 AllocaInst *ZeroAddrAlloca =
2001 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
2002 Instruction *TIDAddr = TIDAddrAlloca;
2003 Instruction *ZeroAddr = ZeroAddrAlloca;
2004 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
2005 // Add additional casts to enforce pointers in zero address space
2006 TIDAddr = new AddrSpaceCastInst(
2007 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2008 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2009 ToBeDeleted.push_back(TIDAddr);
2010 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2011 PointerType ::get(M.getContext(), 0),
2012 "zero.addr.ascast");
2013 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2014 ToBeDeleted.push_back(ZeroAddr);
2015 }
2016
2017 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2018 // associated arguments in the outlined function, so we delete them later.
2019 ToBeDeleted.push_back(TIDAddrAlloca);
2020 ToBeDeleted.push_back(ZeroAddrAlloca);
2021
2022 // Create an artificial insertion point that will also ensure the blocks we
2023 // are about to split are not degenerated.
2024 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2025
2026 BasicBlock *EntryBB = UI->getParent();
2027 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2028 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2029 BasicBlock *PRegPreFiniBB =
2030 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2031 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2032
2033 auto FiniCBWrapper = [&](InsertPointTy IP) {
2034 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2035 // target to the region exit block.
2036 if (IP == IP.getNodeParent()->end()) {
2038 Builder.restoreIP(IP);
2039 Instruction *I = Builder.CreateBr(PRegExitBB);
2040 IP = I->getIterator();
2041 }
2042 assert(IP.getNodeParent()->getTerminator()->getNumSuccessors() == 1 &&
2043 IP.getNodeParent()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2044 "Unexpected insertion point for finalization call!");
2045 return FiniCB(IP);
2046 };
2047
2048 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2049
2050 // Generate the privatization allocas in the block that will become the entry
2051 // of the outlined function.
2052 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2053 InsertPointTy InnerAllocaIP = Builder.saveIP();
2054
2055 AllocaInst *PrivTIDAddr =
2056 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2057 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2058
2059 // Add some fake uses for OpenMP provided arguments.
2060 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2061 Instruction *ZeroAddrUse =
2062 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2063 ToBeDeleted.push_back(ZeroAddrUse);
2064
2065 // EntryBB
2066 // |
2067 // V
2068 // PRegionEntryBB <- Privatization allocas are placed here.
2069 // |
2070 // V
2071 // PRegionBodyBB <- BodeGen is invoked here.
2072 // |
2073 // V
2074 // PRegPreFiniBB <- The block we will start finalization from.
2075 // |
2076 // V
2077 // PRegionExitBB <- A common exit to simplify block collection.
2078 //
2079
2080 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2081
2082 // Let the caller create the body.
2083 assert(BodyGenCB && "Expected body generation callback!");
2084 InsertPointTy CodeGenIP(PRegBodyBB->begin());
2085 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2086 return Err;
2087
2088 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2089
2090 // If OuterFn is a Generic kernel, we need to use device shared memory to
2091 // allocate argument structures. Otherwise, we use stack allocations as usual.
2092 bool UsesDeviceSharedMemory =
2093 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2094 std::unique_ptr<OutlineInfo> OI =
2095 UsesDeviceSharedMemory
2096 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2097 : std::make_unique<OutlineInfo>();
2098
2099 if (Config.isTargetDevice()) {
2100 // Generate OpenMP target specific runtime call
2101 OI->PostOutlineCB = [=, ToBeDeletedVec =
2102 std::move(ToBeDeleted)](Function &OutlinedFn) {
2103 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2104 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2105 ThreadID, ToBeDeletedVec);
2106 };
2107 } else {
2108 // Generate OpenMP host runtime call
2109 OI->PostOutlineCB = [=, ToBeDeletedVec =
2110 std::move(ToBeDeleted)](Function &OutlinedFn) {
2111 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2112 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2113 };
2114 }
2115
2116 OI->FixUpNonEntryAllocas = true;
2117 OI->OuterAllocBB = OuterAllocaBlock;
2118 OI->EntryBB = PRegEntryBB;
2119 OI->ExitBB = PRegExitBB;
2120 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2121 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2122
2123 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2125 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2126
2127 CodeExtractorAnalysisCache CEAC(*OuterFn);
2128 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2129 /* AggregateArgs */ false,
2130 /* BlockFrequencyInfo */ nullptr,
2131 /* BranchProbabilityInfo */ nullptr,
2132 /* AssumptionCache */ nullptr,
2133 /* AllowVarArgs */ true,
2134 /* AllowAlloca */ true,
2135 /* AllocationBlock */ OuterAllocaBlock,
2136 /* DeallocationBlocks */ {},
2137 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2138
2139 // Find inputs to, outputs from the code region.
2140 BasicBlock *CommonExit = nullptr;
2141 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2142 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2143
2144 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2145 /*CollectGlobalInputs=*/true);
2146
2147 Inputs.remove_if([&](Value *I) {
2149 return GV->getValueType() == OpenMPIRBuilder::Ident;
2150
2151 return false;
2152 });
2153
2154 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2155
2156 FunctionCallee TIDRTLFn =
2157 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2158
2159 auto PrivHelper = [&](Value &V) -> Error {
2160 if (&V == TIDAddr || &V == ZeroAddr) {
2161 OI->ExcludeArgsFromAggregate.push_back(&V);
2162 return Error::success();
2163 }
2164
2166 for (Use &U : V.uses())
2167 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2168 if (ParallelRegionBlockSet.count(UserI->getParent()))
2169 Uses.insert(&U);
2170
2171 // __kmpc_fork_call expects extra arguments as pointers. If the input
2172 // already has a pointer type, everything is fine. Otherwise, store the
2173 // value onto stack and load it back inside the to-be-outlined region. This
2174 // will ensure only the pointer will be passed to the function.
2175 // FIXME: if there are more than 15 trailing arguments, they must be
2176 // additionally packed in a struct.
2177 Value *Inner = &V;
2178 if (!V.getType()->isPointerTy()) {
2180 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2181
2182 Builder.restoreIP(OuterAllocIP);
2183 Value *Ptr;
2184 if (UsesDeviceSharedMemory) {
2185 // Use device shared memory instead, if needed.
2186 Ptr = createOMPAllocShared(Builder, V.getType(),
2187 V.getName() + ".reloaded");
2188 for (BasicBlock *DeallocBlock : OuterDeallocBlocks) {
2189 assert(DeallocBlock->getParent() ==
2190 OuterAllocIP.getNodeParent()->getParent() &&
2191 "Dealloc block must be in the allocation's function to reuse "
2192 "its debug location");
2193 createOMPFreeShared({DeallocBlock->getFirstInsertionPt(),
2194 Builder.getCurrentDebugLocation()},
2195 Ptr, V.getType());
2196 }
2197 } else {
2198 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2199 V.getName() + ".reloaded");
2200 }
2201
2202 // Store to stack at end of the block that currently branches to the entry
2203 // block of the to-be-outlined region.
2204 Builder.SetInsertPoint(InsertBB->getTerminator()->getIterator());
2205 Builder.CreateStore(&V, Ptr);
2206
2207 // Load back next to allocations in the to-be-outlined region.
2208 Builder.restoreIP(InnerAllocaIP);
2209 Inner = Builder.CreateLoad(V.getType(), Ptr);
2210 }
2211
2212 Value *ReplacementValue = nullptr;
2213 CallInst *CI = dyn_cast<CallInst>(&V);
2214 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2215 ReplacementValue = PrivTID;
2216 } else {
2217 InsertPointOrErrorTy AfterIP =
2218 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2219 if (!AfterIP)
2220 return AfterIP.takeError();
2221 Builder.restoreIP(*AfterIP);
2222 InnerAllocaIP =
2223 InnerAllocaIP.getNodeParent()->getTerminator()->getIterator();
2224
2225 assert(ReplacementValue &&
2226 "Expected copy/create callback to set replacement value!");
2227 if (ReplacementValue == &V)
2228 return Error::success();
2229 }
2230
2231 for (Use *UPtr : Uses)
2232 UPtr->set(ReplacementValue);
2233
2234 return Error::success();
2235 };
2236
2237 // Reset the inner alloca insertion as it will be used for loading the values
2238 // wrapped into pointers before passing them into the to-be-outlined region.
2239 // Configure it to insert immediately after the fake use of zero address so
2240 // that they are available in the generated body and so that the
2241 // OpenMP-related values (thread ID and zero address pointers) remain leading
2242 // in the argument list.
2243 InnerAllocaIP = ZeroAddrUse->getNextNode()->getIterator();
2244
2245 // Reset the outer alloca insertion point to the entry of the relevant block
2246 // in case it was invalidated.
2247 OuterAllocIP = OuterAllocaBlock->getFirstInsertionPt();
2248
2249 for (Value *Input : Inputs) {
2250 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2251 if (Error Err = PrivHelper(*Input))
2252 return Err;
2253 }
2254 LLVM_DEBUG({
2255 for (Value *Output : Outputs)
2256 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2257 });
2258 assert(Outputs.empty() &&
2259 "OpenMP outlining should not produce live-out values!");
2260
2261 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2262 LLVM_DEBUG({
2263 for (auto *BB : Blocks)
2264 dbgs() << " PBR: " << BB->getName() << "\n";
2265 });
2266
2267 // Adjust the finalization stack, verify the adjustment, and call the
2268 // finalize function a last time to finalize values between the pre-fini
2269 // block and the exit block if we left the parallel "the normal way".
2270 auto FiniInfo = FinalizationStack.pop_back_val();
2271 (void)FiniInfo;
2272 assert(FiniInfo.DK == OMPD_parallel &&
2273 "Unexpected finalization stack state!");
2274
2275 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2276
2277 InsertPointTy PreFiniIP(PRegPreFiniTI->getIterator());
2278 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2279 if (!FiniBBOrErr)
2280 return FiniBBOrErr.takeError();
2281 {
2283 Builder.restoreIP(PreFiniIP);
2284 Builder.CreateBr(*FiniBBOrErr);
2285 // There's currently a branch to omp.par.exit. Delete it. We will get there
2286 // via the fini block
2287 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2288 Term->eraseFromParent();
2289 }
2290
2291 // Register the outlined info.
2292 addOutlineInfo(std::move(OI));
2293
2294 InsertPointTy AfterIP(UI->getParent()->end());
2295 UI->eraseFromParent();
2296
2297 return AfterIP;
2298}
2299
2301 // Build call void __kmpc_flush(ident_t *loc)
2302 uint32_t SrcLocStrSize;
2303 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2304 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2305
2307 Args);
2308}
2309
2311 if (!updateToLocation(Loc))
2312 return;
2313 emitFlush(Loc);
2314}
2315
2317 Value *Message) {
2318 if (!updateToLocation(Loc))
2319 return;
2320
2321 // Build call void __kmpc_error(ident_t *loc, int severity,
2322 // const char *message)
2323 uint32_t SrcLocStrSize;
2324 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2325 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2326 // Severity: 1 = warning, 2 = fatal.
2327 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2328 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2329 Value *Args[] = {Ident, Severity, MessageArg};
2330
2332 Args);
2333}
2334
2336 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2337 uint32_t SrcLocStrSize;
2338 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2339 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2340 Constant *I32Null = ConstantInt::getNullValue(Int32);
2341 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2342
2344 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2345}
2346
2352
2354 const DependData &Dep) {
2355 // Store the pointer to the variable
2356 Value *Addr = Builder.CreateStructGEP(
2357 DependInfo, Entry,
2358 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2359 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2360 Builder.CreateStore(DepValPtr, Addr);
2361 // Store the size of the variable
2362 Value *Size = Builder.CreateStructGEP(
2363 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2364 Builder.CreateStore(
2365 ConstantInt::get(SizeTy,
2366 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2367 Size);
2368 // Store the dependency kind
2369 Value *Flags = Builder.CreateStructGEP(
2370 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2371 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2372 static_cast<unsigned int>(Dep.DepKind)),
2373 Flags);
2374}
2375
2376// Processes the dependencies in Dependencies and does the following
2377// - Allocates space on the stack of an array of DependInfo objects
2378// - Populates each DependInfo object with relevant information of
2379// the corresponding dependence.
2380// - All code is inserted in the entry block of the current function.
2382 OpenMPIRBuilder &OMPBuilder,
2384 // Early return if we have no dependencies to process
2385 if (Dependencies.empty())
2386 return nullptr;
2387
2388 // Given a vector of DependData objects, in this function we create an
2389 // array on the stack that holds kmp_depend_info objects corresponding
2390 // to each dependency. This is then passed to the OpenMP runtime.
2391 // For example, if there are 'n' dependencies then the following psedo
2392 // code is generated. Assume the first dependence is on a variable 'a'
2393 //
2394 // \code{c}
2395 // DepArray = alloc(n x sizeof(kmp_depend_info);
2396 // idx = 0;
2397 // DepArray[idx].base_addr = ptrtoint(&a);
2398 // DepArray[idx].len = 8;
2399 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2400 // ++idx;
2401 // DepArray[idx].base_addr = ...;
2402 // \endcode
2403
2404 IRBuilderBase &Builder = OMPBuilder.Builder;
2405 Type *DependInfo = OMPBuilder.DependInfo;
2406
2407 Value *DepArray = nullptr;
2408 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2409 {
2410 // Use a InsertPointGuard to restore the location back along with the
2411 // insertion point.
2412 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2413 Builder.SetInsertPoint(
2414 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2415 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2416 }
2417
2418 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2419 Value *Base =
2420 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2421 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2422 }
2423 return DepArray;
2424}
2425
2427 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2428 // global_tid);
2429 uint32_t SrcLocStrSize;
2430 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2431 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2432 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2433
2434 // Ignore return result until untied tasks are supported.
2436 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2437}
2438
2440 DependenciesInfo Dependencies,
2441 bool IsNowait) {
2442 if (!updateToLocation(Loc))
2443 return;
2444
2445 Value *DepArray = nullptr;
2446 Type *DepArrayTy = nullptr;
2447 Value *NumDeps = nullptr;
2448 if (Dependencies.DepArray) {
2449 DepArray = Dependencies.DepArray;
2450 NumDeps = Dependencies.NumDeps;
2451 } else if (!Dependencies.Deps.empty()) {
2452 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2453 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2454 {
2456 BasicBlock &entryBB =
2457 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2458 Builder.SetInsertPoint(entryBB.getFirstInsertionPt());
2459 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2460 }
2461
2462 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2463 Value *Base =
2464 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2465 this->emitTaskDependency(Builder, Base, Dep);
2466 }
2467 }
2468
2469 if (DepArray) {
2470 uint32_t SrcLocStrSize;
2471 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2472 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2473 Value *Args[] = {
2474 Ident,
2475 getOrCreateThreadID(Ident),
2476 NumDeps,
2477 DepArray,
2478 ConstantInt::get(Builder.getInt32Ty(), 0),
2480 ConstantInt::get(Builder.getInt32Ty(), IsNowait)};
2483 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2484 Args);
2485 } else {
2487 }
2488}
2489
2490/// Create the task duplication function passed to kmpc_taskloop.
2491Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2492 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2493 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2494 if (!DupCB)
2496 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2497
2498 // From OpenMP Runtime p_task_dup_t:
2499 // Routine optionally generated by the compiler for setting the lastprivate
2500 // flag and calling needed constructors for private/firstprivate objects (used
2501 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2502 // lastprivate flag.
2503 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2504
2505 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2506
2507 FunctionType *DupFuncTy = FunctionType::get(
2508 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2509 /*isVarArg=*/false);
2510
2511 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2512 "omp_taskloop_dup", M);
2513 Value *DestTaskArg = DupFunction->getArg(0);
2514 Value *SrcTaskArg = DupFunction->getArg(1);
2515 Value *LastprivateFlagArg = DupFunction->getArg(2);
2516 DestTaskArg->setName("dest_task");
2517 SrcTaskArg->setName("src_task");
2518 LastprivateFlagArg->setName("lastprivate_flag");
2519
2520 IRBuilderBase::InsertPointGuard Guard(Builder);
2521 Builder.SetInsertPoint(
2522 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2523
2524 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2525 Type *TaskWithPrivatesTy =
2526 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2527 Value *TaskPrivates = Builder.CreateGEP(
2528 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2529 Value *ContextPtr = Builder.CreateGEP(
2530 PrivatesTy, TaskPrivates,
2531 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2532 return ContextPtr;
2533 };
2534
2535 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2536 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2537
2538 DestTaskContextPtr->setName("destPtr");
2539 SrcTaskContextPtr->setName("srcPtr");
2540
2541 InsertPointTy AllocaIP(DupFunction->getEntryBlock().begin());
2542 InsertPointTy CodeGenIP = Builder.saveIP();
2543 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2544 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2545 if (!AfterIPOrError)
2546 return AfterIPOrError.takeError();
2547 Builder.restoreIP(*AfterIPOrError);
2548
2549 Builder.CreateRetVoid();
2550
2551 return DupFunction;
2552}
2553
2554OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2555 const LocationDescription &Loc, InsertPointTy AllocaIP,
2556 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2557 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2558 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2559 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2560 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2561 Value *TaskContextStructPtrVal, bool FreeAgent) {
2562
2563 if (!updateToLocation(Loc))
2564 return InsertPointTy();
2565
2566 uint32_t SrcLocStrSize;
2567 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2568 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2569
2570 BasicBlock *TaskloopExitBB =
2571 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2572 BasicBlock *TaskloopBodyBB =
2573 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2574 BasicBlock *TaskloopAllocaBB =
2575 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2576
2577 InsertPointTy TaskloopAllocaIP = TaskloopAllocaBB->begin();
2578 InsertPointTy TaskloopBodyIP = TaskloopBodyBB->begin();
2579
2580 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2581 return Err;
2582
2583 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2584 if (!result) {
2585 return result.takeError();
2586 }
2587
2588 llvm::CanonicalLoopInfo *CLI = result.get();
2589 auto OI = std::make_unique<OutlineInfo>();
2590 OI->EntryBB = TaskloopAllocaBB;
2591 OI->OuterAllocBB = AllocaIP.getNodeParent();
2592 OI->ExitBB = TaskloopExitBB;
2593 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2594 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2595
2596 // Add the thread ID argument.
2597 SmallVector<Instruction *> ToBeDeleted;
2598 // dummy instruction to be used as a fake argument
2599 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2600 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2601 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2602 TaskloopAllocaIP, "lb", false, true);
2603 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2604 TaskloopAllocaIP, "ub", false, true);
2605 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2606 TaskloopAllocaIP, "step", false, true);
2607 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2608 // aggregate struct
2609 OI->Inputs.insert(FakeLB);
2610 OI->Inputs.insert(FakeUB);
2611 OI->Inputs.insert(FakeStep);
2612 if (TaskContextStructPtrVal)
2613 OI->Inputs.insert(TaskContextStructPtrVal);
2614 assert(((TaskContextStructPtrVal && DupCB) ||
2615 (!TaskContextStructPtrVal && !DupCB)) &&
2616 "Task context struct ptr and duplication callback must be both set "
2617 "or both null");
2618
2619 // It isn't safe to run the duplication bodygen callback inside the post
2620 // outlining callback so this has to be run now before we know the real task
2621 // shareds structure type.
2622 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2623 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2624 Type *FakeSharedsTy = StructType::get(
2625 Builder.getContext(),
2626 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2627 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2628 FakeSharedsTy,
2629 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2630 if (!TaskDupFnOrErr) {
2631 return TaskDupFnOrErr.takeError();
2632 }
2633 Value *TaskDupFn = *TaskDupFnOrErr;
2634
2635 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2636 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2637 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2638 FakeSharedsTy, Final, Mergeable, Priority,
2639 NumOfCollapseLoops,
2640 FreeAgent](Function &OutlinedFn) mutable {
2641 // Replace the Stale CI by appropriate RTL function call.
2642 assert(OutlinedFn.hasOneUse() &&
2643 "there must be a single user for the outlined function");
2644 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2645
2646 /* Create the casting for the Bounds Values that can be used when outlining
2647 * to replace the uses of the fakes with real values */
2648 BasicBlock *CodeReplBB = StaleCI->getParent();
2649 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2650 Value *CastedLBVal =
2651 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2652 Value *CastedUBVal =
2653 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2654 Value *CastedStepVal =
2655 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2656
2657 Builder.SetInsertPoint(StaleCI);
2658
2659 // Gather the arguments for emitting the runtime call for
2660 // @__kmpc_omp_task_alloc
2661 Function *TaskAllocFn =
2662 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2663
2664 Value *ThreadID = getOrCreateThreadID(Ident);
2665
2666 if (!NoGroup) {
2667 // Emit runtime call for @__kmpc_taskgroup
2668 Function *TaskgroupFn =
2669 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2670 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2671 }
2672
2673 // `flags` Argument Configuration
2674 // Task is tied if (Flags & 1) == 1.
2675 // Task is untied if (Flags & 1) == 0.
2676 // Task is final if (Flags & 2) == 2.
2677 // Task is not final if (Flags & 2) == 0.
2678 // Task is mergeable if (Flags & 4) == 4.
2679 // Task is not mergeable if (Flags & 4) == 0.
2680 // Task is priority if (Flags & 32) == 32.
2681 // Task is not priority if (Flags & 32) == 0.
2682 // Task is free-agent eligible if (Flags & 128) == 128.
2683 // Task is not free-agent eligible if (Flags & 128) == 0.
2684 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2685 if (Final)
2686 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2687 if (Mergeable)
2688 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2689 if (Priority)
2690 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2691 if (FreeAgent)
2692 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2693
2694 Value *TaskSize = Builder.getInt64(
2695 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2696
2697 AllocaInst *ArgStructAlloca =
2699 assert(ArgStructAlloca &&
2700 "Unable to find the alloca instruction corresponding to arguments "
2701 "for extracted function");
2702 std::optional<TypeSize> ArgAllocSize =
2703 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2704 assert(ArgAllocSize &&
2705 "Unable to determine size of arguments for extracted function");
2706 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2707
2708 // Emit the @__kmpc_omp_task_alloc runtime call
2709 // The runtime call returns a pointer to an area where the task captured
2710 // variables must be copied before the task is run (TaskData)
2711 CallInst *TaskData = Builder.CreateCall(
2712 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2713 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2714 /*task_func=*/&OutlinedFn});
2715
2716 Value *Shareds = StaleCI->getArgOperand(1);
2717 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2718 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2719 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2720 SharedsSize);
2721 // Get the pointer to loop lb, ub, step from task ptr
2722 // and set up the lowerbound,upperbound and step values
2723 llvm::Value *Lb = Builder.CreateGEP(
2724 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2725
2726 llvm::Value *Ub = Builder.CreateGEP(
2727 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2728
2729 llvm::Value *Step = Builder.CreateGEP(
2730 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2731 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2732
2733 // set up the arguments for emitting kmpc_taskloop runtime call
2734 // setting values for ifval, nogroup, sched, grainsize, task_dup
2735 Value *IfCondVal =
2736 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2737 : Builder.getInt32(1);
2738 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2739 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2740 Value *NoGroupVal = Builder.getInt32(1);
2741 Value *SchedVal = Builder.getInt32(Sched);
2742 Value *GrainSizeVal =
2743 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2744 : Builder.getInt64(0);
2745 Value *TaskDup = TaskDupFn;
2746
2747 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2748 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2749
2750 // taskloop runtime call
2751 Function *TaskloopFn =
2752 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2753 Builder.CreateCall(TaskloopFn, Args);
2754
2755 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2756 // nogroup is not defined
2757 if (!NoGroup) {
2758 Function *EndTaskgroupFn =
2759 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2760 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2761 }
2762
2763 StaleCI->eraseFromParent();
2764
2765 Builder.SetInsertPoint(TaskloopAllocaBB->begin());
2766
2767 LoadInst *SharedsOutlined =
2768 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2769 OutlinedFn.getArg(1)->replaceUsesWithIf(
2770 SharedsOutlined,
2771 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2772
2773 Value *IV = CLI->getIndVar();
2774 Type *IVTy = IV->getType();
2775 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2776
2777 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2778 // UpperBound. These GEP's can be reused for loading the tasks respective
2779 // bounds.
2780 Value *TaskLB = nullptr;
2781 Value *TaskUB = nullptr;
2782 Value *TaskStep = nullptr;
2783 Value *LoadTaskLB = nullptr;
2784 Value *LoadTaskUB = nullptr;
2785 Value *LoadTaskStep = nullptr;
2786 for (Instruction &I : *TaskloopAllocaBB) {
2787 if (I.getOpcode() == Instruction::GetElementPtr) {
2788 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2789 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2790 switch (CI->getZExtValue()) {
2791 case 0:
2792 TaskLB = &I;
2793 break;
2794 case 1:
2795 TaskUB = &I;
2796 break;
2797 case 2:
2798 TaskStep = &I;
2799 break;
2800 }
2801 }
2802 } else if (I.getOpcode() == Instruction::Load) {
2803 LoadInst &Load = cast<LoadInst>(I);
2804 if (Load.getPointerOperand() == TaskLB) {
2805 assert(TaskLB != nullptr && "Expected value for TaskLB");
2806 LoadTaskLB = &I;
2807 } else if (Load.getPointerOperand() == TaskUB) {
2808 assert(TaskUB != nullptr && "Expected value for TaskUB");
2809 LoadTaskUB = &I;
2810 } else if (Load.getPointerOperand() == TaskStep) {
2811 assert(TaskStep != nullptr && "Expected value for TaskStep");
2812 LoadTaskStep = &I;
2813 }
2814 }
2815 }
2816
2817 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2818
2819 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2820 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2821 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2822 Value *TripCountMinusOne = Builder.CreateSDiv(
2823 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2824 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2825 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2826 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2827 // set the trip count in the CLI
2828 CLI->setTripCount(CastedTripCount);
2829
2830 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
2831
2832 if (NumOfCollapseLoops > 1) {
2833 llvm::SmallVector<User *> UsersToReplace;
2834 // When using the collapse clause, the bounds of the loop have to be
2835 // adjusted to properly represent the iterator of the outer loop.
2836 Value *IVPlusTaskLB = Builder.CreateAdd(
2837 CLI->getIndVar(),
2838 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2839 // To ensure every Use is correctly captured, we first want to record
2840 // which users to replace the value in, and then replace the value.
2841 for (auto IVUse = CLI->getIndVar()->uses().begin();
2842 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2843 User *IVUser = IVUse->getUser();
2844 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2845 if (Op->getOpcode() == Instruction::URem ||
2846 Op->getOpcode() == Instruction::UDiv) {
2847 UsersToReplace.push_back(IVUser);
2848 }
2849 }
2850 }
2851 for (User *User : UsersToReplace) {
2852 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2853 }
2854 } else {
2855 // The canonical loop is generated with a fixed lower bound. We need to
2856 // update the index calculation code to use the task's lower bound. The
2857 // generated code looks like this:
2858 // %omp_loop.iv = phi ...
2859 // ...
2860 // %tmp = mul [type] %omp_loop.iv, step
2861 // %user_index = add [type] tmp, lb
2862 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2863 // of the normalised induction variable:
2864 // 1. This one: converting the normalised IV to the user IV
2865 // 2. The increment (add)
2866 // 3. The comparison against the trip count (icmp)
2867 // (1) is the only use that is a mul followed by an add so this cannot
2868 // match other IR.
2869 assert(CLI->getIndVar()->getNumUses() == 3 &&
2870 "Canonical loop should have exactly three uses of the ind var");
2871 for (User *IVUser : CLI->getIndVar()->users()) {
2872 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2873 if (Mul->getOpcode() == Instruction::Mul) {
2874 for (User *MulUser : Mul->users()) {
2875 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2876 if (Add->getOpcode() == Instruction::Add) {
2877 Add->setOperand(1, CastedTaskLB);
2878 }
2879 }
2880 }
2881 }
2882 }
2883 }
2884 }
2885
2886 FakeLB->replaceAllUsesWith(CastedLBVal);
2887 FakeUB->replaceAllUsesWith(CastedUBVal);
2888 FakeStep->replaceAllUsesWith(CastedStepVal);
2889 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2890 I->eraseFromParent();
2891 }
2892 };
2893
2894 addOutlineInfo(std::move(OI));
2895 Builder.SetInsertPoint(TaskloopExitBB->begin());
2896 return Builder.saveIP();
2897}
2898
2901 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2903 llvm::Type::getInt32Ty(M.getContext()));
2904}
2905
2907 const LocationDescription &Loc, InsertPointTy AllocaIP,
2908 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2909 bool Tied, Value *Final, Value *IfCondition,
2910 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2911 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2912
2913 if (!updateToLocation(Loc))
2914 return InsertPointTy();
2915
2916 uint32_t SrcLocStrSize;
2917 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2918 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2919 // The current basic block is split into four basic blocks. After outlining,
2920 // they will be mapped as follows:
2921 // ```
2922 // def current_fn() {
2923 // current_basic_block:
2924 // br label %task.exit
2925 // task.exit:
2926 // ; instructions after task
2927 // }
2928 // def outlined_fn() {
2929 // task.alloca:
2930 // br label %task.body
2931 // task.body:
2932 // ret void
2933 // }
2934 // ```
2935 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2936 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2937 BasicBlock *TaskAllocaBB =
2938 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2939
2940 InsertPointTy TaskAllocaIP = TaskAllocaBB->begin();
2941 InsertPointTy TaskBodyIP = TaskBodyBB->begin();
2942 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2943 return Err;
2944
2945 auto OI = std::make_unique<OutlineInfo>();
2946 OI->EntryBB = TaskAllocaBB;
2947 OI->OuterAllocBB = AllocaIP.getNodeParent();
2948 OI->ExitBB = TaskExitBB;
2949 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2950 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2951
2952 // Add the thread ID argument.
2954 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2955 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2956
2957 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2958 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2959 TaskAllocaBB,
2960 ToBeDeleted](Function &OutlinedFn) mutable {
2961 // Replace the Stale CI by appropriate RTL function call.
2962 assert(OutlinedFn.hasOneUse() &&
2963 "there must be a single user for the outlined function");
2964 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2965
2966 // HasShareds is true if any variables are captured in the outlined region,
2967 // false otherwise.
2968 bool HasShareds = StaleCI->arg_size() > 1;
2969 Builder.SetInsertPoint(StaleCI);
2970
2971 // Gather the arguments for emitting the runtime call for
2972 // @__kmpc_omp_task_alloc
2973 Function *TaskAllocFn =
2974 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2975
2976 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2977 // call.
2978 Value *ThreadID = getOrCreateThreadID(Ident);
2979
2980 // Argument - `flags`
2981 // Task is tied iff (Flags & 1) == 1.
2982 // Task is untied iff (Flags & 1) == 0.
2983 // Task is final iff (Flags & 2) == 2.
2984 // Task is not final iff (Flags & 2) == 0.
2985 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2986 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2987 // Task is detachable iff (Flags & 64) == 64.
2988 // Task is not detachable iff (Flags & 64) == 0.
2989 // Task is priority iff (Flags & 32) == 32.
2990 // Task is not priority iff (Flags & 32) == 0.
2991 // Task is free-agent eligible iff (Flags & 128) == 128.
2992 // Task is not free-agent eligible iff (Flags & 128) == 0.
2993 // TODO: Handle the other flags.
2994 Value *Flags = Builder.getInt32(Tied);
2995 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2996 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
2997 if (Final) {
2998 Value *FinalFlag =
2999 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3000 Flags = Builder.CreateOr(FinalFlag, Flags);
3001 }
3002
3003 if (Mergeable || UseMergedIf0Path)
3004 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3005 if (EventHandle)
3006 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3007 if (Priority)
3008 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3009 if (FreeAgent)
3010 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3011
3012 // Argument - `sizeof_kmp_task_t` (TaskSize)
3013 // Tasksize refers to the size in bytes of kmp_task_t data structure
3014 // including private vars accessed in task.
3015 // TODO: add kmp_task_t_with_privates (privates)
3016 Value *TaskSize = Builder.getInt64(
3017 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3018
3019 // Argument - `sizeof_shareds` (SharedsSize)
3020 // SharedsSize refers to the shareds array size in the kmp_task_t data
3021 // structure.
3022 Value *SharedsSize = Builder.getInt64(0);
3023 if (HasShareds) {
3024 AllocaInst *ArgStructAlloca =
3026 assert(ArgStructAlloca &&
3027 "Unable to find the alloca instruction corresponding to arguments "
3028 "for extracted function");
3029 std::optional<TypeSize> ArgAllocSize =
3030 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3031 assert(ArgAllocSize &&
3032 "Unable to determine size of arguments for extracted function");
3033 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3034 }
3035 // Emit the @__kmpc_omp_task_alloc runtime call
3036 // The runtime call returns a pointer to an area where the task captured
3037 // variables must be copied before the task is run (TaskData)
3039 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3040 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3041 /*task_func=*/&OutlinedFn});
3042
3043 if (Affinities.Count && Affinities.Info) {
3045 OMPRTL___kmpc_omp_reg_task_with_affinity);
3046
3047 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3048 Affinities.Count, Affinities.Info});
3049 }
3050
3051 // Emit detach clause initialization.
3052 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3053 // task_descriptor);
3054 if (EventHandle) {
3056 OMPRTL___kmpc_task_allow_completion_event);
3057 llvm::Value *EventVal =
3058 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3059 llvm::Value *EventHandleAddr =
3060 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3061 Builder.getPtrTy(0));
3062 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3063 Builder.CreateStore(EventVal, EventHandleAddr);
3064 }
3065 // Copy the arguments for outlined function
3066 if (HasShareds) {
3067 Value *Shareds = StaleCI->getArgOperand(1);
3068 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3069 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3070 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3071 SharedsSize);
3072 }
3073
3074 if (Priority) {
3075 //
3076 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3077 // we populate the priority information into the "kmp_task_t" here
3078 //
3079 // The struct "kmp_task_t" definition is available in kmp.h
3080 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3081 // data2 is used for priority
3082 //
3083 Type *Int32Ty = Builder.getInt32Ty();
3084 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3085 // kmp_task_t* => { ptr }
3086 Type *TaskPtr = StructType::get(VoidPtr);
3087 Value *TaskGEP =
3088 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3089 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3090 Type *TaskStructType = StructType::get(
3091 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3092 Value *PriorityData = Builder.CreateInBoundsGEP(
3093 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3094 // kmp_cmplrdata_t => { ptr, ptr }
3095 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3096 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3097 PriorityData, {Zero, Zero});
3098 Builder.CreateStore(Priority, CmplrData);
3099 }
3100
3101 Value *DepArray = nullptr;
3102 Value *NumDeps = nullptr;
3103 if (Dependencies.DepArray) {
3104 DepArray = Dependencies.DepArray;
3105 NumDeps = Dependencies.NumDeps;
3106 } else if (!Dependencies.Deps.empty()) {
3107 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3108 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3109 }
3110
3111 // In the presence of the `if` clause, the following IR is generated:
3112 // ...
3113 // %data = call @__kmpc_omp_task_alloc(...)
3114 // br i1 %if_condition, label %then, label %else
3115 // then:
3116 // call @__kmpc_omp_task(...)
3117 // br label %exit
3118 // else:
3119 // ;; Wait for resolution of dependencies, if any, before
3120 // ;; beginning the task
3121 // call @__kmpc_omp_wait_deps(...)
3122 // call @__kmpc_omp_task_begin_if0(...)
3123 // call @outlined_fn(...)
3124 // call @__kmpc_omp_task_complete_if0(...)
3125 // br label %exit
3126 // exit:
3127 // ...
3128 if (IfCondition && !UseMergedIf0Path) {
3129 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3130 // terminator.
3131 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3132 Instruction *IfTerminator =
3133 Builder.GetInsertPoint()->getParent()->getTerminator();
3134 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3135 Builder.SetInsertPoint(IfTerminator);
3136 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3137 &ElseTI);
3138 Builder.SetInsertPoint(ElseTI);
3139
3140 if (DepArray) {
3141 Function *TaskWaitFn =
3142 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3144 TaskWaitFn,
3145 {Ident, ThreadID, NumDeps, DepArray,
3146 ConstantInt::get(Builder.getInt32Ty(), 0),
3148 }
3149 Function *TaskBeginFn =
3150 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3151 Function *TaskCompleteFn =
3152 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3153 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3154 CallInst *CI = nullptr;
3155 if (HasShareds)
3156 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3157 else
3158 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3159 CI->setDebugLoc(StaleCI->getDebugLoc());
3160 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3161 Builder.SetInsertPoint(ThenTI);
3162 }
3163
3164 if (DepArray) {
3165 Function *TaskFn =
3166 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3168 TaskFn,
3169 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3170 ConstantInt::get(Builder.getInt32Ty(), 0),
3172
3173 } else {
3174 // Emit the @__kmpc_omp_task runtime call to spawn the task
3175 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3176 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3177 }
3178
3179 StaleCI->eraseFromParent();
3180
3181 Builder.SetInsertPoint(TaskAllocaBB->begin());
3182 if (HasShareds) {
3183 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3184 OutlinedFn.getArg(1)->replaceUsesWithIf(
3185 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3186 }
3187
3188 // The insert point may refer to one of the instructions about to be
3189 // deleted. It is not needed anymore so clear it instead of leaving it
3190 // dangling.
3191 Builder.ClearInsertionPoint();
3192 for (Instruction *I : llvm::reverse(ToBeDeleted))
3193 I->eraseFromParent();
3194 };
3195
3196 addOutlineInfo(std::move(OI));
3197 Builder.SetInsertPoint(TaskExitBB->begin());
3198
3199 return Builder.saveIP();
3200}
3201
3203 const LocationDescription &Loc, InsertPointTy AllocaIP,
3204 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3205 if (!updateToLocation(Loc))
3206 return InsertPointTy();
3207
3208 uint32_t SrcLocStrSize;
3209 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3210 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3211 Value *ThreadID = getOrCreateThreadID(Ident);
3212
3213 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3214 Function *TaskgroupFn =
3215 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3216 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3217
3218 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3219 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3220 return Err;
3221
3222 Builder.SetInsertPoint(TaskgroupExitBB);
3223 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3224 Function *EndTaskgroupFn =
3225 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3226 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3227
3228 return Builder.saveIP();
3229}
3230
3232 const LocationDescription &Loc, InsertPointTy AllocaIP,
3234 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3235 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3236
3237 if (!updateToLocation(Loc))
3238 return Loc.IP;
3239
3240 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3241
3242 // Each section is emitted as a switch case
3243 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3244 // -> OMP.createSection() which generates the IR for each section
3245 // Iterate through all sections and emit a switch construct:
3246 // switch (IV) {
3247 // case 0:
3248 // <SectionStmt[0]>;
3249 // break;
3250 // ...
3251 // case <NumSection> - 1:
3252 // <SectionStmt[<NumSection> - 1]>;
3253 // break;
3254 // }
3255 // ...
3256 // section_loop.after:
3257 // <FiniCB>;
3258 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3259 Builder.restoreIP(CodeGenIP);
3261 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3262 Function *CurFn = Continue->getParent();
3263 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3264
3265 unsigned CaseNumber = 0;
3266 for (auto SectionCB : SectionCBs) {
3268 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3269 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3270 Builder.SetInsertPoint(CaseBB);
3271 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3272 if (Error Err = SectionCB(InsertPointTy(), CaseEndBr->getIterator(), {}))
3273 return Err;
3274 CaseNumber++;
3275 }
3276 // remove the existing terminator from body BB since there can be no
3277 // terminators after switch/case
3278 return Error::success();
3279 };
3280 // Loop body ends here
3281 // LowerBound, UpperBound, and STride for createCanonicalLoop
3282 Type *I32Ty = Type::getInt32Ty(M.getContext());
3283 Value *LB = ConstantInt::get(I32Ty, 0);
3284 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3285 Value *ST = ConstantInt::get(I32Ty, 1);
3287 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3288 if (!LoopInfo)
3289 return LoopInfo.takeError();
3290
3291 InsertPointOrErrorTy WsloopIP =
3292 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3293 WorksharingLoopType::ForStaticLoop, !IsNowait);
3294 if (!WsloopIP)
3295 return WsloopIP.takeError();
3296 InsertPointTy AfterIP = *WsloopIP;
3297
3298 BasicBlock *LoopFini = AfterIP.getNodeParent()->getSinglePredecessor();
3299 assert(LoopFini && "Bad structure of static workshare loop finalization");
3300
3301 // Apply the finalization callback in LoopAfterBB
3302 auto FiniInfo = FinalizationStack.pop_back_val();
3303 assert(FiniInfo.DK == OMPD_sections &&
3304 "Unexpected finalization stack state!");
3305 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3306 return Err;
3307
3308 return AfterIP;
3309}
3310
3313 BodyGenCallbackTy BodyGenCB,
3314 FinalizeCallbackTy FiniCB) {
3315 if (!updateToLocation(Loc))
3316 return Loc.IP;
3317
3318 auto FiniCBWrapper = [&](InsertPointTy IP) {
3319 if (IP != IP.getNodeParent()->end())
3320 return FiniCB(IP);
3321 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3322 // will fail because that function requires the Finalization Basic Block to
3323 // have a terminator, which is already removed by EmitOMPRegionBody.
3324 // IP is currently at cancelation block.
3325 // We need to backtrack to the condition block to fetch
3326 // the exit block and create a branch from cancelation
3327 // to exit block.
3329 Builder.restoreIP(IP);
3330 auto *CaseBB = Loc.IP.getNodeParent();
3331 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3332 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3333 Instruction *I = Builder.CreateBr(ExitBB);
3334 IP = I->getIterator();
3335 return FiniCB(IP);
3336 };
3337
3338 Directive OMPD = Directive::OMPD_sections;
3339 // Since we are using Finalization Callback here, HasFinalize
3340 // and IsCancellable have to be true
3341 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3342 /*Conditional*/ false, /*hasFinalize*/ true,
3343 /*IsCancellable*/ true);
3344}
3345
3351
3352Value *OpenMPIRBuilder::getGPUThreadID() {
3355 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3356 {});
3357}
3358
3359Value *OpenMPIRBuilder::getGPUWarpSize() {
3361 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3362}
3363
3364Value *OpenMPIRBuilder::getNVPTXWarpID() {
3365 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3366 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3367}
3368
3369Value *OpenMPIRBuilder::getNVPTXLaneID() {
3370 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3371 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3372 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3373 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3374 "nvptx_lane_id");
3375}
3376
3377Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3378 Type *ToType) {
3379 Type *FromType = From->getType();
3380 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3381 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3382 assert(FromSize > 0 && "From size must be greater than zero");
3383 assert(ToSize > 0 && "To size must be greater than zero");
3384 if (FromType == ToType)
3385 return From;
3386 if (FromSize == ToSize)
3387 return Builder.CreateBitCast(From, ToType);
3388 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3389 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3390 InsertPointTy SaveIP = Builder.saveIP();
3391 Builder.restoreIP(AllocaIP);
3392 Value *CastItem = Builder.CreateAlloca(ToType);
3393 Builder.restoreIP(SaveIP);
3394
3395 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3396 CastItem, Builder.getPtrTy(0));
3397 Builder.CreateStore(From, ValCastItem);
3398 return Builder.CreateLoad(ToType, CastItem);
3399}
3400
3401Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3402 Value *Element,
3403 Type *ElementType,
3404 Value *Offset) {
3405 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3406 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3407
3408 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3409 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3410 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3411 Value *WarpSize =
3412 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3414 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3415 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3416 Value *WarpSizeCast =
3417 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3418 Value *ShuffleCall =
3419 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3420 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3421 // down to the requested element type, otherwise storing the result would
3422 // write past the end of an element narrower than the shuffle width.
3423 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3424}
3425
3426void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3427 Value *DstAddr, Type *ElemType,
3428 Value *Offset, Type *ReductionArrayTy,
3429 bool IsByRefElem) {
3430 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3431 // Create the loop over the big sized data.
3432 // ptr = (void*)Elem;
3433 // ptrEnd = (void*) Elem + 1;
3434 // Step = 8;
3435 // while (ptr + Step < ptrEnd)
3436 // shuffle((int64_t)*ptr);
3437 // Step = 4;
3438 // while (ptr + Step < ptrEnd)
3439 // shuffle((int32_t)*ptr);
3440 // ...
3441 Type *IndexTy = Builder.getIndexTy(
3442 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3443 Value *ElemPtr = DstAddr;
3444 Value *Ptr = SrcAddr;
3445 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3446 if (Size < IntSize)
3447 continue;
3448 Type *IntType = Builder.getIntNTy(IntSize * 8);
3449 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3450 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3451 Value *SrcAddrGEP =
3452 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3453 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3454 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3455
3456 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3457 if ((Size / IntSize) > 1) {
3458 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3459 SrcAddrGEP, Builder.getPtrTy());
3460 BasicBlock *PreCondBB =
3461 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3462 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3463 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3464 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3465 emitBlock(PreCondBB, CurFunc);
3466 PHINode *PhiSrc =
3467 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3468 PhiSrc->addIncoming(Ptr, CurrentBB);
3469 PHINode *PhiDest =
3470 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3471 PhiDest->addIncoming(ElemPtr, CurrentBB);
3472 Ptr = PhiSrc;
3473 ElemPtr = PhiDest;
3474 Value *PtrDiff = Builder.CreatePtrDiff(
3475 Builder.getInt8Ty(), PtrEnd,
3476 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3477 Builder.CreateCondBr(
3478 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3479 ExitBB);
3480 emitBlock(ThenBB, CurFunc);
3481 Value *Res = createRuntimeShuffleFunction(
3482 AllocaIP,
3483 Builder.CreateAlignedLoad(
3484 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3485 IntType, Offset);
3486 Builder.CreateAlignedStore(Res, ElemPtr,
3487 M.getDataLayout().getPrefTypeAlign(ElemType));
3488 Value *LocalPtr =
3489 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3490 Value *LocalElemPtr =
3491 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3492 PhiSrc->addIncoming(LocalPtr, ThenBB);
3493 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3494 emitBranch(PreCondBB);
3495 emitBlock(ExitBB, CurFunc);
3496 } else {
3497 // The shuffled value comes back as the chunk's integer type, so the
3498 // store covers exactly this chunk regardless of what ElemType is.
3499 Value *Res = createRuntimeShuffleFunction(
3500 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3501 Builder.CreateStore(Res, ElemPtr);
3502 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3503 ElemPtr =
3504 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3505 }
3506 Size = Size % IntSize;
3507 }
3508}
3509
3510Error OpenMPIRBuilder::emitReductionListCopy(
3511 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3512 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3513 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3514 Type *IndexTy = Builder.getIndexTy(
3515 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3516 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3517
3518 // Iterates, element-by-element, through the source Reduce list and
3519 // make a copy.
3520 for (auto En : enumerate(ReductionInfos)) {
3521 const ReductionInfo &RI = En.value();
3522 Value *SrcElementAddr = nullptr;
3523 AllocaInst *DestAlloca = nullptr;
3524 Value *DestElementAddr = nullptr;
3525 Value *DestElementPtrAddr = nullptr;
3526 // Should we shuffle in an element from a remote lane?
3527 bool ShuffleInElement = false;
3528 // Set to true to update the pointer in the dest Reduce list to a
3529 // newly created element.
3530 bool UpdateDestListPtr = false;
3531
3532 // Step 1.1: Get the address for the src element in the Reduce list.
3533 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3534 ReductionArrayTy, SrcBase,
3535 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3536 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3537
3538 // Step 1.2: Create a temporary to store the element in the destination
3539 // Reduce list.
3540 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3541 ReductionArrayTy, DestBase,
3542 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3543 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3544 switch (Action) {
3546 InsertPointTy CurIP = Builder.saveIP();
3547 Builder.restoreIP(AllocaIP);
3548
3549 Type *DestAllocaType =
3550 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3551 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3552 ".omp.reduction.element");
3553 DestAlloca->setAlignment(
3554 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3555 DestElementAddr = DestAlloca;
3556 DestElementAddr =
3557 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3558 DestElementAddr->getName() + ".ascast");
3559 Builder.restoreIP(CurIP);
3560 ShuffleInElement = true;
3561 UpdateDestListPtr = true;
3562 break;
3563 }
3565 DestElementAddr =
3566 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3567 break;
3568 }
3569 }
3570
3571 // Now that all active lanes have read the element in the
3572 // Reduce list, shuffle over the value from the remote lane.
3573 if (ShuffleInElement) {
3574 Type *ShuffleType = RI.ElementType;
3575 Value *ShuffleSrcAddr = SrcElementAddr;
3576 Value *ShuffleDestAddr = DestElementAddr;
3577 AllocaInst *LocalStorage = nullptr;
3578
3579 if (IsByRefElem) {
3580 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3581 assert(RI.ByRefAllocatedType &&
3582 "Expected by-ref allocated type to be set");
3583 // For by-ref reductions, we need to copy from the remote lane the
3584 // actual value of the partial reduction computed by that remote lane;
3585 // rather than, for example, a pointer to that data or, even worse, a
3586 // pointer to the descriptor of the by-ref reduction element.
3587 ShuffleType = RI.ByRefElementType;
3588
3589 if (RI.DataPtrPtrGen) {
3590 // Descriptor-based by-ref: extract data pointer from descriptor.
3591 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3592 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3593
3594 if (!GenResult)
3595 return GenResult.takeError();
3596
3597 ShuffleSrcAddr =
3598 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3599
3600 {
3601 InsertPointTy OldIP = Builder.saveIP();
3602 Builder.restoreIP(AllocaIP);
3603
3604 LocalStorage = Builder.CreateAlloca(ShuffleType);
3605 Builder.restoreIP(OldIP);
3606 ShuffleDestAddr = LocalStorage;
3607 }
3608 } else {
3609 // Non-descriptor by-ref: the pointer already references data
3610 // directly. Shuffle into the destination alloca.
3611 ShuffleDestAddr = DestElementAddr;
3612 }
3613 }
3614
3615 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3616 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3617
3618 if (IsByRefElem && RI.DataPtrPtrGen) {
3619 // Copy descriptor from source and update base_ptr to shuffled data
3620 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3621 DestAlloca, Builder.getPtrTy(), ".ascast");
3622
3623 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3624 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3625 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3626
3627 if (!GenResult)
3628 return GenResult.takeError();
3629 }
3630 } else {
3631 switch (RI.EvaluationKind) {
3632 case EvalKind::Scalar: {
3633 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3634 // Store the source element value to the dest element address.
3635 Builder.CreateStore(Elem, DestElementAddr);
3636 break;
3637 }
3638 case EvalKind::Complex: {
3639 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3640 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3641 Value *SrcReal = Builder.CreateLoad(
3642 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3643 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3644 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3645 Value *SrcImg = Builder.CreateLoad(
3646 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3647
3648 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3649 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3650 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3651 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3652 Builder.CreateStore(SrcReal, DestRealPtr);
3653 Builder.CreateStore(SrcImg, DestImgPtr);
3654 break;
3655 }
3656 case EvalKind::Aggregate: {
3657 Value *SizeVal = Builder.getInt64(
3658 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3659 Builder.CreateMemCpy(
3660 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3661 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3662 SizeVal, false);
3663 break;
3664 }
3665 };
3666 }
3667
3668 // Step 3.1: Modify reference in dest Reduce list as needed.
3669 // Modifying the reference in Reduce list to point to the newly
3670 // created element. The element is live in the current function
3671 // scope and that of functions it invokes (i.e., reduce_function).
3672 // RemoteReduceData[i] = (void*)&RemoteElem
3673 if (UpdateDestListPtr) {
3674 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3675 DestElementAddr, Builder.getPtrTy(),
3676 DestElementAddr->getName() + ".ascast");
3677 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3678 }
3679 }
3680
3681 return Error::success();
3682}
3683
3684Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3685 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3686 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3687 IRBuilder<>::InsertPointGuard IPG(Builder);
3688 LLVMContext &Ctx = M.getContext();
3689 FunctionType *FuncTy = FunctionType::get(
3690 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3691 /* IsVarArg */ false);
3692 Function *WcFunc =
3694 "_omp_reduction_inter_warp_copy_func", &M);
3695 WcFunc->setCallingConv(Config.getRuntimeCC());
3696 WcFunc->setAttributes(FuncAttrs);
3697 WcFunc->addParamAttr(0, Attribute::NoUndef);
3698 WcFunc->addParamAttr(1, Attribute::NoUndef);
3699 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3700 Builder.SetInsertPoint(EntryBB);
3701 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3702
3703 // ReduceList: thread local Reduce list.
3704 // At the stage of the computation when this function is called, partially
3705 // aggregated values reside in the first lane of every active warp.
3706 Argument *ReduceListArg = WcFunc->getArg(0);
3707 // NumWarps: number of warps active in the parallel region. This could
3708 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3709 Argument *NumWarpsArg = WcFunc->getArg(1);
3710
3711 // This array is used as a medium to transfer, one reduce element at a time,
3712 // the data from the first lane of every warp to lanes in the first warp
3713 // in order to perform the final step of a reduction in a parallel region
3714 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3715 // for reduced latency, as well as to have a distinct copy for concurrently
3716 // executing target regions. The array is declared with common linkage so
3717 // as to be shared across compilation units.
3718 StringRef TransferMediumName =
3719 "__openmp_nvptx_data_transfer_temporary_storage";
3720 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3721 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3722 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3723 if (!TransferMedium) {
3724 TransferMedium = new GlobalVariable(
3725 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3726 UndefValue::get(ArrayTy), TransferMediumName,
3727 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3728 /*AddressSpace=*/3);
3729 }
3730
3731 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3732 Value *GPUThreadID = getGPUThreadID();
3733 // nvptx_lane_id = nvptx_id % warpsize
3734 Value *LaneID = getNVPTXLaneID();
3735 // nvptx_warp_id = nvptx_id / warpsize
3736 Value *WarpID = getNVPTXWarpID();
3737
3738 InsertPointTy AllocaIP = Builder.GetInsertBlock()->getFirstInsertionPt();
3739 Type *Arg0Type = ReduceListArg->getType();
3740 Type *Arg1Type = NumWarpsArg->getType();
3741 Builder.restoreIP(AllocaIP);
3742 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3743 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3744 AllocaInst *NumWarpsAlloca =
3745 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3746 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3747 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3748 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3749 NumWarpsAlloca, Builder.getPtrTy(0),
3750 NumWarpsAlloca->getName() + ".ascast");
3751 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3752 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3753 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3754 InsertPointTy CodeGenIP =
3755 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3756 Builder.restoreIP(CodeGenIP);
3757
3758 Value *ReduceList =
3759 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3760
3761 for (auto En : enumerate(ReductionInfos)) {
3762 //
3763 // Warp master copies reduce element to transfer medium in __shared__
3764 // memory.
3765 //
3766 const ReductionInfo &RI = En.value();
3767 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3768 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3769 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3770 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3771 Type *CType = Builder.getIntNTy(TySize * 8);
3772
3773 unsigned NumIters = RealTySize / TySize;
3774 if (NumIters == 0)
3775 continue;
3776 Value *Cnt = nullptr;
3777 Value *CntAddr = nullptr;
3778 BasicBlock *PrecondBB = nullptr;
3779 BasicBlock *ExitBB = nullptr;
3780 if (NumIters > 1) {
3781 CodeGenIP = Builder.saveIP();
3782 Builder.restoreIP(AllocaIP);
3783 CntAddr =
3784 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3785
3786 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3787 CntAddr->getName() + ".ascast");
3788 Builder.restoreIP(CodeGenIP);
3789 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3790 CntAddr,
3791 /*Volatile=*/false);
3792 PrecondBB = BasicBlock::Create(Ctx, "precond");
3793 ExitBB = BasicBlock::Create(Ctx, "exit");
3794 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3795 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3796 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3797 /*Volatile=*/false);
3798 Value *Cmp = Builder.CreateICmpULT(
3799 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3800 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3801 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3802 }
3803
3804 // kmpc_barrier.
3805 InsertPointOrErrorTy BarrierIP1 =
3807 omp::Directive::OMPD_unknown,
3808 /* ForceSimpleCall */ false,
3809 /* CheckCancelFlag */ true);
3810 if (!BarrierIP1)
3811 return BarrierIP1.takeError();
3812 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3813 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3814 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3815
3816 // if (lane_id == 0)
3817 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3818 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3819 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3820
3821 // Reduce element = LocalReduceList[i]
3822 auto *RedListArrayTy =
3823 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3824 Type *IndexTy = Builder.getIndexTy(
3825 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3826 Value *ElemPtrPtr =
3827 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3828 {ConstantInt::get(IndexTy, 0),
3829 ConstantInt::get(IndexTy, En.index())});
3830 // elemptr = ((CopyType*)(elemptrptr)) + I
3831 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3832
3833 if (IsByRefElem && RI.DataPtrPtrGen) {
3834 InsertPointOrErrorTy GenRes =
3835 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3836
3837 if (!GenRes)
3838 return GenRes.takeError();
3839
3840 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3841 }
3842
3843 if (NumIters > 1)
3844 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3845
3846 // Get pointer to location in transfer medium.
3847 // MediumPtr = &medium[warp_id]
3848 Value *MediumPtr = Builder.CreateInBoundsGEP(
3849 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3850 // elem = *elemptr
3851 //*MediumPtr = elem
3852 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3853 // Store the source element value to the dest element address.
3854 Builder.CreateStore(Elem, MediumPtr,
3855 /*IsVolatile*/ true);
3856 Builder.CreateBr(MergeBB);
3857
3858 // else
3859 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3860 Builder.CreateBr(MergeBB);
3861
3862 // endif
3863 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3864 InsertPointOrErrorTy BarrierIP2 =
3866 omp::Directive::OMPD_unknown,
3867 /* ForceSimpleCall */ false,
3868 /* CheckCancelFlag */ true);
3869 if (!BarrierIP2)
3870 return BarrierIP2.takeError();
3871
3872 // Warp 0 copies reduce element from transfer medium
3873 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3874 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3875 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3876
3877 Value *NumWarpsVal =
3878 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3879 // Up to 32 threads in warp 0 are active.
3880 Value *IsActiveThread =
3881 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3882 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3883
3884 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3885
3886 // SecMediumPtr = &medium[tid]
3887 // SrcMediumVal = *SrcMediumPtr
3888 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3889 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3890 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3891 Value *TargetElemPtrPtr =
3892 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3893 {ConstantInt::get(IndexTy, 0),
3894 ConstantInt::get(IndexTy, En.index())});
3895 Value *TargetElemPtrVal =
3896 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3897 Value *TargetElemPtr = TargetElemPtrVal;
3898
3899 if (IsByRefElem && RI.DataPtrPtrGen) {
3900 InsertPointOrErrorTy GenRes =
3901 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3902
3903 if (!GenRes)
3904 return GenRes.takeError();
3905
3906 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3907 }
3908
3909 if (NumIters > 1)
3910 TargetElemPtr =
3911 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3912
3913 // *TargetElemPtr = SrcMediumVal;
3914 Value *SrcMediumValue =
3915 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3916 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3917 Builder.CreateBr(W0MergeBB);
3918
3919 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3920 Builder.CreateBr(W0MergeBB);
3921
3922 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3923
3924 if (NumIters > 1) {
3925 Cnt = Builder.CreateNSWAdd(
3926 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3927 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3928
3929 auto *CurFn = Builder.GetInsertBlock()->getParent();
3930 emitBranch(PrecondBB);
3931 emitBlock(ExitBB, CurFn);
3932 }
3933 RealTySize %= TySize;
3934 }
3935 }
3936
3937 Builder.CreateRetVoid();
3938
3939 return WcFunc;
3940}
3941
3942Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3943 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3944 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3945 LLVMContext &Ctx = M.getContext();
3946 IRBuilder<>::InsertPointGuard IPG(Builder);
3947 FunctionType *FuncTy =
3948 FunctionType::get(Builder.getVoidTy(),
3949 {Builder.getPtrTy(), Builder.getInt16Ty(),
3950 Builder.getInt16Ty(), Builder.getInt16Ty()},
3951 /* IsVarArg */ false);
3952 Function *SarFunc =
3954 "_omp_reduction_shuffle_and_reduce_func", &M);
3955 SarFunc->setCallingConv(Config.getRuntimeCC());
3956 SarFunc->setAttributes(FuncAttrs);
3957 SarFunc->addParamAttr(0, Attribute::NoUndef);
3958 SarFunc->addParamAttr(1, Attribute::NoUndef);
3959 SarFunc->addParamAttr(2, Attribute::NoUndef);
3960 SarFunc->addParamAttr(3, Attribute::NoUndef);
3961 SarFunc->addParamAttr(1, Attribute::SExt);
3962 SarFunc->addParamAttr(2, Attribute::SExt);
3963 SarFunc->addParamAttr(3, Attribute::SExt);
3964 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3965 Builder.SetInsertPoint(EntryBB);
3966 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3967
3968 // Thread local Reduce list used to host the values of data to be reduced.
3969 Argument *ReduceListArg = SarFunc->getArg(0);
3970 // Current lane id; could be logical.
3971 Argument *LaneIDArg = SarFunc->getArg(1);
3972 // Offset of the remote source lane relative to the current lane.
3973 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3974 // Algorithm version. This is expected to be known at compile time.
3975 Argument *AlgoVerArg = SarFunc->getArg(3);
3976
3977 Type *ReduceListArgType = ReduceListArg->getType();
3978 Type *LaneIDArgType = LaneIDArg->getType();
3979 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3980 Value *ReduceListAlloca = Builder.CreateAlloca(
3981 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3982 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3983 LaneIDArg->getName() + ".addr");
3984 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3985 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3986 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3987 AlgoVerArg->getName() + ".addr");
3988 ArrayType *RedListArrayTy =
3989 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3990
3991 // Create a local thread-private variable to host the Reduce list
3992 // from a remote lane.
3993 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3994 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3995
3996 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3997 ReduceListAlloca, ReduceListArgType,
3998 ReduceListAlloca->getName() + ".ascast");
3999 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4000 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4001 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4002 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4003 RemoteLaneOffsetAlloca->getName() + ".ascast");
4004 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4005 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4006 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4007 RemoteReductionListAlloca, Builder.getPtrTy(),
4008 RemoteReductionListAlloca->getName() + ".ascast");
4009
4010 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4011 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4012 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4013 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4014
4015 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4016 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4017 Value *RemoteLaneOffset =
4018 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4019 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4020
4021 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4022
4023 // This loop iterates through the list of reduce elements and copies,
4024 // element by element, from a remote lane in the warp to RemoteReduceList,
4025 // hosted on the thread's stack.
4026 Error EmitRedLsCpRes = emitReductionListCopy(
4027 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4028 ReduceList, RemoteListAddrCast, IsByRef,
4029 {RemoteLaneOffset, nullptr, nullptr});
4030
4031 if (EmitRedLsCpRes)
4032 return EmitRedLsCpRes;
4033
4034 // The actions to be performed on the Remote Reduce list is dependent
4035 // on the algorithm version.
4036 //
4037 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4038 // LaneId % 2 == 0 && Offset > 0):
4039 // do the reduction value aggregation
4040 //
4041 // The thread local variable Reduce list is mutated in place to host the
4042 // reduced data, which is the aggregated value produced from local and
4043 // remote lanes.
4044 //
4045 // Note that AlgoVer is expected to be a constant integer known at compile
4046 // time.
4047 // When AlgoVer==0, the first conjunction evaluates to true, making
4048 // the entire predicate true during compile time.
4049 // When AlgoVer==1, the second conjunction has only the second part to be
4050 // evaluated during runtime. Other conjunctions evaluates to false
4051 // during compile time.
4052 // When AlgoVer==2, the third conjunction has only the second part to be
4053 // evaluated during runtime. Other conjunctions evaluates to false
4054 // during compile time.
4055 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4056 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4057 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4058 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4059 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4060 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4061 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4062 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4063 Value *RemoteOffsetComp =
4064 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4065 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4066 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4067 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4068
4069 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4070 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4071 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4072
4073 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4074 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4075 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4076 ReduceList, Builder.getPtrTy());
4077 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4078 RemoteListAddrCast, Builder.getPtrTy());
4079 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4080 ->addFnAttr(Attribute::NoUnwind);
4081 Builder.CreateBr(MergeBB);
4082
4083 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4084 Builder.CreateBr(MergeBB);
4085
4086 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4087
4088 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4089 // Reduce list.
4090 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4091 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4092 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4093
4094 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4095 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4096 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4097 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4098
4099 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4100
4101 EmitRedLsCpRes = emitReductionListCopy(
4102 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4103 RemoteListAddrCast, ReduceList, IsByRef);
4104
4105 if (EmitRedLsCpRes)
4106 return EmitRedLsCpRes;
4107
4108 Builder.CreateBr(CpyMergeBB);
4109
4110 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4111 Builder.CreateBr(CpyMergeBB);
4112
4113 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4114
4115 Builder.CreateRetVoid();
4116
4117 return SarFunc;
4118}
4119
4121OpenMPIRBuilder::generateReductionDescriptor(
4122 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4123 Type *DescriptorType,
4124 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4125 DataPtrPtrGen) {
4126
4127 // Copy the source descriptor to preserve all metadata (rank, extents,
4128 // strides, etc.)
4129 Value *DescriptorSize =
4130 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4131 Builder.CreateMemCpy(
4132 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4133 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4134 DescriptorSize);
4135
4136 // Update the base pointer field to point to the local shuffled data
4137 Value *DataPtrField;
4138 InsertPointOrErrorTy GenResult =
4139 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4140
4141 if (!GenResult)
4142 return GenResult.takeError();
4143
4144 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4145 DataPtr, Builder.getPtrTy(), ".ascast"),
4146 DataPtrField);
4147
4148 return Builder.saveIP();
4149}
4150
4151Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4152 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4153 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4154 InsertPointTy OldIP = Builder.saveIP();
4155 Builder.restoreIP(AllocaIP);
4156
4157 AllocaInst *DescriptorAlloca =
4158 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4159 DescriptorAlloca->setAlignment(
4160 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4161 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4162 DescriptorAlloca, DescriptorPtrTy,
4163 DescriptorAlloca->getName() + ".ascast");
4164
4165 Builder.restoreIP(OldIP);
4166
4167 InsertPointOrErrorTy GenResult =
4168 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4169 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4170 if (!GenResult)
4171 return GenResult.takeError();
4172
4173 return DescriptorAddr;
4174}
4175
4176Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4177 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4178 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4179 IRBuilder<>::InsertPointGuard IPG(Builder);
4180 LLVMContext &Ctx = M.getContext();
4181 FunctionType *FuncTy = FunctionType::get(
4182 Builder.getVoidTy(),
4183 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4184 /* IsVarArg */ false);
4185 Function *LtGCFunc =
4187 "_omp_reduction_list_to_global_copy_func", &M);
4188 LtGCFunc->setAttributes(FuncAttrs);
4189 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4190 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4191 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4192
4193 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4194 Builder.SetInsertPoint(EntryBlock);
4195 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4196
4197 // Buffer: global reduction buffer.
4198 Argument *BufferArg = LtGCFunc->getArg(0);
4199 // Idx: index of the buffer.
4200 Argument *IdxArg = LtGCFunc->getArg(1);
4201 // ReduceList: thread local Reduce list.
4202 Argument *ReduceListArg = LtGCFunc->getArg(2);
4203
4204 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4205 BufferArg->getName() + ".addr");
4206 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4207 IdxArg->getName() + ".addr");
4208 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4209 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4210 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4211 BufferArgAlloca, Builder.getPtrTy(),
4212 BufferArgAlloca->getName() + ".ascast");
4213 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4214 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4215 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4216 ReduceListArgAlloca, Builder.getPtrTy(),
4217 ReduceListArgAlloca->getName() + ".ascast");
4218
4219 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4220 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4221 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4222
4223 Value *LocalReduceList =
4224 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4225 Value *BufferArgVal =
4226 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4227 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4228 Type *IndexTy = Builder.getIndexTy(
4229 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4230 for (auto En : enumerate(ReductionInfos)) {
4231 const ReductionInfo &RI = En.value();
4232 auto *RedListArrayTy =
4233 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4234 // Reduce element = LocalReduceList[i]
4235 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4236 RedListArrayTy, LocalReduceList,
4237 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4238 // elemptr = ((CopyType*)(elemptrptr)) + I
4239 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4240
4241 // Global = Buffer.VD[Idx];
4242 Value *BufferVD =
4243 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4244 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4245 ReductionsBufferTy, BufferVD, 0, En.index());
4246
4247 switch (RI.EvaluationKind) {
4248 case EvalKind::Scalar: {
4249 Value *TargetElement;
4250
4251 if (IsByRef.empty() || !IsByRef[En.index()]) {
4252 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4253 } else {
4254 if (RI.DataPtrPtrGen) {
4255 InsertPointOrErrorTy GenResult =
4256 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4257
4258 if (!GenResult)
4259 return GenResult.takeError();
4260
4261 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4262 }
4263 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4264 }
4265
4266 Builder.CreateStore(TargetElement, GlobVal);
4267 break;
4268 }
4269 case EvalKind::Complex: {
4270 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4271 RI.ElementType, ElemPtr, 0, 0, ".realp");
4272 Value *SrcReal = Builder.CreateLoad(
4273 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4274 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4275 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4276 Value *SrcImg = Builder.CreateLoad(
4277 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4278
4279 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4280 RI.ElementType, GlobVal, 0, 0, ".realp");
4281 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4282 RI.ElementType, GlobVal, 0, 1, ".imagp");
4283 Builder.CreateStore(SrcReal, DestRealPtr);
4284 Builder.CreateStore(SrcImg, DestImgPtr);
4285 break;
4286 }
4287 case EvalKind::Aggregate: {
4288 Value *SizeVal =
4289 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4290 Builder.CreateMemCpy(
4291 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4292 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4293 break;
4294 }
4295 }
4296 }
4297
4298 Builder.CreateRetVoid();
4299 return LtGCFunc;
4300}
4301
4302Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4303 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4304 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4305 IRBuilder<>::InsertPointGuard IPG(Builder);
4306 LLVMContext &Ctx = M.getContext();
4307 FunctionType *FuncTy = FunctionType::get(
4308 Builder.getVoidTy(),
4309 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4310 /* IsVarArg */ false);
4311 Function *LtGRFunc =
4313 "_omp_reduction_list_to_global_reduce_func", &M);
4314 LtGRFunc->setAttributes(FuncAttrs);
4315 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4316 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4317 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4318
4319 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4320 Builder.SetInsertPoint(EntryBlock);
4321 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4322
4323 // Buffer: global reduction buffer.
4324 Argument *BufferArg = LtGRFunc->getArg(0);
4325 // Idx: index of the buffer.
4326 Argument *IdxArg = LtGRFunc->getArg(1);
4327 // ReduceList: thread local Reduce list.
4328 Argument *ReduceListArg = LtGRFunc->getArg(2);
4329
4330 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4331 BufferArg->getName() + ".addr");
4332 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4333 IdxArg->getName() + ".addr");
4334 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4335 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4336 auto *RedListArrayTy =
4337 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4338
4339 // 1. Build a list of reduction variables.
4340 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4341 Value *LocalReduceList =
4342 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4343
4344 InsertPointTy AllocaIP(EntryBlock->begin());
4345
4346 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4347 BufferArgAlloca, Builder.getPtrTy(),
4348 BufferArgAlloca->getName() + ".ascast");
4349 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4350 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4351 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4352 ReduceListArgAlloca, Builder.getPtrTy(),
4353 ReduceListArgAlloca->getName() + ".ascast");
4354 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4355 LocalReduceList, Builder.getPtrTy(),
4356 LocalReduceList->getName() + ".ascast");
4357
4358 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4359 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4360 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4361
4362 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4363 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4364 Type *IndexTy = Builder.getIndexTy(
4365 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4366 for (auto En : enumerate(ReductionInfos)) {
4367 const ReductionInfo &RI = En.value();
4368
4369 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4370 RedListArrayTy, LocalReduceListAddrCast,
4371 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4372 Value *BufferVD =
4373 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4374 // Global = Buffer.VD[Idx];
4375 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4376 ReductionsBufferTy, BufferVD, 0, En.index());
4377
4378 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4379 // Get source descriptor from the reduce list argument
4380 Value *ReduceList =
4381 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4382 Value *SrcElementPtrPtr =
4383 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4384 {ConstantInt::get(IndexTy, 0),
4385 ConstantInt::get(IndexTy, En.index())});
4386 Value *SrcDescriptorAddr =
4387 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4388
4389 // Copy descriptor from source and update base_ptr to global buffer data
4390 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4391 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4392 if (!ByRefAlloc)
4393 return ByRefAlloc.takeError();
4394
4395 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4396 } else {
4397 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4398 }
4399 }
4400
4401 // Call reduce_function(GlobalReduceList, ReduceList)
4402 Value *ReduceList =
4403 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4404 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4405 ->addFnAttr(Attribute::NoUnwind);
4406 Builder.CreateRetVoid();
4407 return LtGRFunc;
4408}
4409
4410Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4411 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4412 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4413 IRBuilder<>::InsertPointGuard IPG(Builder);
4414 LLVMContext &Ctx = M.getContext();
4415 FunctionType *FuncTy = FunctionType::get(
4416 Builder.getVoidTy(),
4417 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4418 /* IsVarArg */ false);
4419 Function *GtLCFunc =
4421 "_omp_reduction_global_to_list_copy_func", &M);
4422 GtLCFunc->setAttributes(FuncAttrs);
4423 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4424 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4425 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4426
4427 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4428 Builder.SetInsertPoint(EntryBlock);
4429 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4430
4431 // Buffer: global reduction buffer.
4432 Argument *BufferArg = GtLCFunc->getArg(0);
4433 // Idx: index of the buffer.
4434 Argument *IdxArg = GtLCFunc->getArg(1);
4435 // ReduceList: thread local Reduce list.
4436 Argument *ReduceListArg = GtLCFunc->getArg(2);
4437
4438 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4439 BufferArg->getName() + ".addr");
4440 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4441 IdxArg->getName() + ".addr");
4442 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4443 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4444 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4445 BufferArgAlloca, Builder.getPtrTy(),
4446 BufferArgAlloca->getName() + ".ascast");
4447 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4448 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4449 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4450 ReduceListArgAlloca, Builder.getPtrTy(),
4451 ReduceListArgAlloca->getName() + ".ascast");
4452 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4453 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4454 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4455
4456 Value *LocalReduceList =
4457 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4458 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4459 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4460 Type *IndexTy = Builder.getIndexTy(
4461 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4462 for (auto En : enumerate(ReductionInfos)) {
4463 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4464 auto *RedListArrayTy =
4465 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4466 // Reduce element = LocalReduceList[i]
4467 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4468 RedListArrayTy, LocalReduceList,
4469 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4470 // elemptr = ((CopyType*)(elemptrptr)) + I
4471 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4472 // Global = Buffer.VD[Idx];
4473 Value *BufferVD =
4474 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4475 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4476 ReductionsBufferTy, BufferVD, 0, En.index());
4477
4478 switch (RI.EvaluationKind) {
4479 case EvalKind::Scalar: {
4480 Type *ElemType = RI.ElementType;
4481
4482 if (!IsByRef.empty() && IsByRef[En.index()]) {
4483 ElemType = RI.ByRefElementType;
4484 if (RI.DataPtrPtrGen) {
4485 InsertPointOrErrorTy GenResult =
4486 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4487
4488 if (!GenResult)
4489 return GenResult.takeError();
4490
4491 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4492 }
4493 }
4494
4495 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4496 Builder.CreateStore(TargetElement, ElemPtr);
4497 break;
4498 }
4499 case EvalKind::Complex: {
4500 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4501 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4502 Value *SrcReal = Builder.CreateLoad(
4503 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4504 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4505 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4506 Value *SrcImg = Builder.CreateLoad(
4507 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4508
4509 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4510 RI.ElementType, ElemPtr, 0, 0, ".realp");
4511 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4512 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4513 Builder.CreateStore(SrcReal, DestRealPtr);
4514 Builder.CreateStore(SrcImg, DestImgPtr);
4515 break;
4516 }
4517 case EvalKind::Aggregate: {
4518 Value *SizeVal =
4519 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4520 Builder.CreateMemCpy(
4521 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4522 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4523 SizeVal, false);
4524 break;
4525 }
4526 }
4527 }
4528
4529 Builder.CreateRetVoid();
4530 return GtLCFunc;
4531}
4532
4533Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4534 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4535 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4536 IRBuilder<>::InsertPointGuard IPG(Builder);
4537 LLVMContext &Ctx = M.getContext();
4538 auto *FuncTy = FunctionType::get(
4539 Builder.getVoidTy(),
4540 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4541 /* IsVarArg */ false);
4542 Function *GtLRFunc =
4544 "_omp_reduction_global_to_list_reduce_func", &M);
4545 GtLRFunc->setAttributes(FuncAttrs);
4546 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4547 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4548 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4549
4550 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4551 Builder.SetInsertPoint(EntryBlock);
4552 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4553
4554 // Buffer: global reduction buffer.
4555 Argument *BufferArg = GtLRFunc->getArg(0);
4556 // Idx: index of the buffer.
4557 Argument *IdxArg = GtLRFunc->getArg(1);
4558 // ReduceList: thread local Reduce list.
4559 Argument *ReduceListArg = GtLRFunc->getArg(2);
4560
4561 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4562 BufferArg->getName() + ".addr");
4563 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4564 IdxArg->getName() + ".addr");
4565 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4566 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4567 ArrayType *RedListArrayTy =
4568 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4569
4570 // 1. Build a list of reduction variables.
4571 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4572 Value *LocalReduceList =
4573 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4574
4575 InsertPointTy AllocaIP(EntryBlock->begin());
4576
4577 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4578 BufferArgAlloca, Builder.getPtrTy(),
4579 BufferArgAlloca->getName() + ".ascast");
4580 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4581 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4582 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4583 ReduceListArgAlloca, Builder.getPtrTy(),
4584 ReduceListArgAlloca->getName() + ".ascast");
4585 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4586 LocalReduceList, Builder.getPtrTy(),
4587 LocalReduceList->getName() + ".ascast");
4588
4589 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4590 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4591 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4592
4593 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4594 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4595 Type *IndexTy = Builder.getIndexTy(
4596 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4597 for (auto En : enumerate(ReductionInfos)) {
4598 const ReductionInfo &RI = En.value();
4599
4600 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4601 RedListArrayTy, ReductionList,
4602 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4603 // Global = Buffer.VD[Idx];
4604 Value *BufferVD =
4605 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4606 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4607 ReductionsBufferTy, BufferVD, 0, En.index());
4608
4609 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4610 // Get source descriptor from the reduce list
4611 Value *ReduceListVal =
4612 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4613 Value *SrcElementPtrPtr =
4614 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4615 {ConstantInt::get(IndexTy, 0),
4616 ConstantInt::get(IndexTy, En.index())});
4617 Value *SrcDescriptorAddr =
4618 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4619
4620 // Copy descriptor from source and update base_ptr to global buffer data
4621 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4622 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4623 if (!ByRefAlloc)
4624 return ByRefAlloc.takeError();
4625
4626 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4627 } else {
4628 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4629 }
4630 }
4631
4632 // Call reduce_function(ReduceList, GlobalReduceList)
4633 Value *ReduceList =
4634 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4635 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4636 ->addFnAttr(Attribute::NoUnwind);
4637 Builder.CreateRetVoid();
4638 return GtLRFunc;
4639}
4640
4641std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4642 std::string Suffix =
4643 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4644 return (Name + Suffix).str();
4645}
4646
4647Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4648 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4650 AttributeList FuncAttrs) {
4651 IRBuilder<>::InsertPointGuard IPG(Builder);
4652 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4653 {Builder.getPtrTy(), Builder.getPtrTy()},
4654 /* IsVarArg */ false);
4655 std::string Name = getReductionFuncName(ReducerName);
4656 Function *ReductionFunc =
4658 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4659 ReductionFunc->setAttributes(FuncAttrs);
4660 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4661 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4662 BasicBlock *EntryBB =
4663 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4664 Builder.SetInsertPoint(EntryBB);
4665 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4666
4667 // Need to alloca memory here and deal with the pointers before getting
4668 // LHS/RHS pointers out
4669 Value *LHSArrayPtr = nullptr;
4670 Value *RHSArrayPtr = nullptr;
4671 Argument *Arg0 = ReductionFunc->getArg(0);
4672 Argument *Arg1 = ReductionFunc->getArg(1);
4673 Type *Arg0Type = Arg0->getType();
4674 Type *Arg1Type = Arg1->getType();
4675
4676 Value *LHSAlloca =
4677 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4678 Value *RHSAlloca =
4679 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4680 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4681 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4682 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4683 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4684 Builder.CreateStore(Arg0, LHSAddrCast);
4685 Builder.CreateStore(Arg1, RHSAddrCast);
4686 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4687 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4688
4689 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4690 Type *IndexTy = Builder.getIndexTy(
4691 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4692 SmallVector<Value *> LHSPtrs, RHSPtrs;
4693 for (auto En : enumerate(ReductionInfos)) {
4694 const ReductionInfo &RI = En.value();
4695 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4696 RedArrayTy, RHSArrayPtr,
4697 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4698 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4699 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4700 RHSI8Ptr, RI.PrivateVariable->getType(),
4701 RHSI8Ptr->getName() + ".ascast");
4702
4703 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4704 RedArrayTy, LHSArrayPtr,
4705 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4706 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4707 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4708 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4709
4711 LHSPtrs.emplace_back(LHSPtr);
4712 RHSPtrs.emplace_back(RHSPtr);
4713 } else {
4714 Value *LHS = LHSPtr;
4715 Value *RHS = RHSPtr;
4716
4717 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4718 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4719 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4720 }
4721
4722 Value *Reduced;
4723 InsertPointOrErrorTy AfterIP =
4724 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4725 if (!AfterIP)
4726 return AfterIP.takeError();
4727 if (!Builder.GetInsertBlock())
4728 return ReductionFunc;
4729
4730 Builder.restoreIP(*AfterIP);
4731
4732 if (!IsByRef.empty() && !IsByRef[En.index()])
4733 Builder.CreateStore(Reduced, LHSPtr);
4734 }
4735 }
4736
4738 for (auto En : enumerate(ReductionInfos)) {
4739 unsigned Index = En.index();
4740 const ReductionInfo &RI = En.value();
4741 Value *LHSFixupPtr, *RHSFixupPtr;
4742 Builder.restoreIP(RI.ReductionGenClang(
4743 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4744
4745 // Fix the CallBack code genereated to use the correct Values for the LHS
4746 // and RHS
4747 LHSFixupPtr->replaceUsesWithIf(
4748 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4749 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4750 ReductionFunc;
4751 });
4752 RHSFixupPtr->replaceUsesWithIf(
4753 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4754 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4755 ReductionFunc;
4756 });
4757 }
4758
4759 Builder.CreateRetVoid();
4760 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4761 // to the entry block (this is dones for higher opt levels by later passes in
4762 // the pipeline). This has caused issues because non-entry `alloca`s force the
4763 // function to use dynamic stack allocations and we might run out of scratch
4764 // memory.
4765 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4766
4767 return ReductionFunc;
4768}
4769
4770static void
4772 bool IsGPU) {
4773 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4774 (void)RI;
4775 assert(RI.Variable && "expected non-null variable");
4776 assert(RI.PrivateVariable && "expected non-null private variable");
4777 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4778 "expected non-null reduction generator callback");
4779 if (!IsGPU) {
4780 assert(
4781 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4782 "expected variables and their private equivalents to have the same "
4783 "type");
4784 }
4785 assert(RI.Variable->getType()->isPointerTy() &&
4786 "expected variables to be pointers");
4787 }
4788}
4789
4790// The atomic cross-team reduction fast path applies when every reduction in the
4791// set can be represented by an atomicrmw. Clang only populates it for scalar
4792// reductions with a supported atomic operator.
4795 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4796 return static_cast<bool>(RI.AtomicReductionGen);
4797 });
4798}
4799
4801 const LocationDescription &Loc, InsertPointTy AllocaIP,
4802 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4803 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4804 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4805 Value *SrcLocInfo) {
4806 if (!updateToLocation(Loc))
4807 return InsertPointTy();
4808 Builder.restoreIP(CodeGenIP);
4809 Builder.SetCurrentDebugLocation(Loc.DL);
4810 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4811 LLVMContext &Ctx = M.getContext();
4812
4813 // Source location for the ident struct
4814 if (!SrcLocInfo) {
4815 uint32_t SrcLocStrSize;
4816 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4817 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4818 }
4819
4820 if (ReductionInfos.size() == 0)
4821 return Builder.saveIP();
4822
4823 BasicBlock *ContinuationBlock = nullptr;
4825 // Copied code from createReductions
4826 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
4827 ContinuationBlock = InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
4828 InsertBlock->getTerminator()->eraseFromParent();
4829 Builder.SetInsertPoint(InsertBlock->end());
4830 }
4831
4832 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4833 AttributeList FuncAttrs;
4834 AttrBuilder AttrBldr(Ctx);
4835 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4836 AttrBldr.addAttribute(Attr);
4837 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4838 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4839
4840 Expected<Function *> ReductionResult = createReductionFunction(
4841 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4842 ReductionGenCBKind, FuncAttrs);
4843 if (!ReductionResult)
4844 return ReductionResult.takeError();
4845 Function *ReductionFunc = *ReductionResult;
4846
4847 // Set the grid value in the config needed for lowering later on
4848 if (GridValue.has_value())
4849 Config.setGridValue(GridValue.value());
4850 else
4851 Config.setGridValue(getGridValue(T, ReductionFunc));
4852
4853 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4854 // RedList, shuffle_reduce_func, interwarp_copy_func);
4855 // or
4856 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4857 Value *Res;
4858
4859 // 1. Build a list of reduction variables.
4860 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4861 auto Size = ReductionInfos.size();
4862 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4863 Type *FuncPtrTy =
4864 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4865 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4866 Value *ReductionList;
4867 {
4869 Builder.restoreIP(AllocaIP);
4870 Value *ReductionListAlloca =
4871 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4872 ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4873 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4874 }
4875 Type *IndexTy = Builder.getIndexTy(
4876 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4877 for (auto En : enumerate(ReductionInfos)) {
4878 const ReductionInfo &RI = En.value();
4879 Value *ElemPtr = Builder.CreateInBoundsGEP(
4880 RedArrayTy, ReductionList,
4881 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4882
4883 Value *PrivateVar = RI.PrivateVariable;
4884 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4885 if (IsByRefElem)
4886 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4887
4888 Value *CastElem =
4889 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4890 Builder.CreateStore(CastElem, ElemPtr);
4891 }
4892 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4893 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4894
4895 if (!SarFunc)
4896 return SarFunc.takeError();
4897
4898 Expected<Function *> CopyResult =
4899 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4900 if (!CopyResult)
4901 return CopyResult.takeError();
4902 Function *WcFunc = *CopyResult;
4903
4904 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4905
4906 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4907 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4908 // not currently use it. It is computed here conservatively as max(element
4909 // sizes) * N rather than the exact sum, which over-calculates the size for
4910 // mixed reduction types but is harmless given the argument is unused.
4911 // TODO: Consider dropping this computation if the runtime API is ever revised
4912 // to remove the unused parameter.
4913 unsigned MaxDataSize = 0;
4914 SmallVector<Type *> ReductionTypeArgs;
4915 for (auto En : enumerate(ReductionInfos)) {
4916 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4917 // the actual data size stored in the global reduction buffer, consistent
4918 // with the ReductionsBufferTy struct used for GEP offsets below.
4919 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4920 ? En.value().ByRefElementType
4921 : En.value().ElementType;
4922 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4923 if (Size > MaxDataSize)
4924 MaxDataSize = Size;
4925 ReductionTypeArgs.emplace_back(RedTypeArg);
4926 }
4927 Value *ReductionDataSize =
4928 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4929
4930 // Helper function to copy thread-local data back to the original reduction
4931 // list.
4932 Function *CopyScratchToListFunc = nullptr;
4933 // Thread-local storage for the reduction variables.
4934 Value *ScratchForCopyBack = nullptr;
4935 // RL pointer to which the final value from the per-thread scratch should be
4936 // copied back. (Basically RL, appropriately casted if necessary.)
4937 Value *RLForCopyBack = RL;
4938
4939 bool IsAtomicReduction =
4940 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4941
4942 if (!IsTeamsReduction) {
4943 Value *SarFuncCast =
4944 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4945 Value *WcFuncCast =
4946 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4947 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4948 WcFuncCast};
4950 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4951 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4952 } else if (IsAtomicReduction) {
4953 // Atomic cross-team reduction fast path: determine the team's main thread
4954 // that is later to fold its value atomically into the mapped variable.
4955 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4956 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4957 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4958 } else {
4959 StructType *ReductionsBufferTy = StructType::create(
4960 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4961
4962 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4963 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4964 if (!LtGCFunc)
4965 return LtGCFunc.takeError();
4966
4967 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4968 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4969 if (!GtLCFunc)
4970 return GtLCFunc.takeError();
4971
4972 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4973 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4974 if (!GtLRFunc)
4975 return GtLRFunc.takeError();
4976
4977 // The runtime's cross-team final aggregate uses the storage pointed at by
4978 // its reduce-list argument as per-thread scratch. When the surrounding
4979 // kernel is already in SPMD execution mode, clang emitted each reduction
4980 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4981 // (RL) is already per-thread and nothing else is needed.
4982 //
4983 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4984 // Generic-mode globalization put the reduction private into team-shared
4985 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4986 // point all threads of the last team would race on the shared LDS slot.
4987 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4988 // value in, and hand the per-thread RL to the runtime instead. The writer
4989 // thread copies the final value from that per-thread scratch back to RL
4990 // before running the existing combine path below.
4991
4992 // Thread-local RL (might need localization below before being passed to the
4993 // runtime).
4994 Value *RuntimeRL = RL;
4995
4996 if (!IsSPMD) {
4997 Value *PerThreadScratch;
4998
4999 {
5001 Builder.restoreIP(AllocaIP);
5002 // Allocate thread-local buffer for the reduction variables.
5003 Value *PerThreadScratchAlloca =
5004 Builder.CreateAlloca(ReductionsBufferTy, /*ArraySize=*/nullptr,
5005 ".omp.reduction.scratch");
5006 PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5007 PerThreadScratchAlloca, PtrTy,
5008 PerThreadScratchAlloca->getName() + ".ascast");
5009 // Allocate thread-local buffer for the pointers to the reduction
5010 // variables.
5011 Value *PerThreadRedListAlloca =
5012 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5013 ".omp.reduction.per_thread_red_list");
5014 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5015 PerThreadRedListAlloca, PtrTy,
5016 PerThreadRedListAlloca->getName() + ".ascast");
5017 }
5018
5019 // Iterate over the reduction variables and copy the team-local value to
5020 // the thread-local buffer.
5021 for (auto En : enumerate(ReductionInfos)) {
5022 const ReductionInfo &RI = En.value();
5023 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5024
5025 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5026 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5027 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5028 0, En.index());
5029
5030 Value *RuntimeListEntry = FieldPtr;
5031 if (IsByRefElem && RI.DataPtrPtrGen) {
5032 Value *SrcDescriptor =
5033 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5034 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5035 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5036 if (!Descriptor)
5037 return Descriptor.takeError();
5038 RuntimeListEntry = *Descriptor;
5039 }
5040 Builder.CreateStore(RuntimeListEntry, Slot);
5041 }
5042 // The copy helpers were emitted with default-AS (AS 0) pointer params
5043 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5044 // but PerThreadScratch and RL live in the target's default AS, which
5045 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5046 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5047 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5048 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5049 PerThreadScratch, CopyArg0Ty);
5050 RLForCopyBack =
5051 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5052 // Use index 0 because there is no array of target values to index into,
5053 // there is only one thread-local memory slot.
5054 // restoreIP above left a stale/empty debug location; this inlinable call
5055 // to a debug-info-bearing helper needs one or the verifier rejects the
5056 // module ("!dbg attachment points at wrong subprogram") after inlining.
5057 Builder.SetCurrentDebugLocation(Loc.DL);
5058 Builder.CreateCall(
5059 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5060 CopyScratchToListFunc = *GtLCFunc;
5061 }
5062
5063 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5064 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5065
5066 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5067 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5068 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5069 }
5070
5071 // 5. Build if (res == 1)
5072 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5073 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5074 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5075 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5076
5077 // 6. Build then branch: where we have reduced values in the master
5078 // thread in each team.
5079 // __kmpc_end_reduce{_nowait}(<gtid>);
5080 // break;
5081 emitBlock(ThenBB, CurFunc);
5082
5083 // Copy the writer thread's per-thread scratch result back into the original
5084 // red-list storage before the existing combine path reads RI.PrivateVariable.
5085 // Set a debug location: this inlinable call to a debug-info-bearing helper
5086 // needs one or the verifier rejects the module after inlining.
5087 if (ScratchForCopyBack) {
5088 Builder.SetCurrentDebugLocation(Loc.DL);
5089 Builder.CreateCall(
5090 CopyScratchToListFunc,
5091 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5092 }
5093
5094 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5095 for (auto En : enumerate(ReductionInfos)) {
5096 const ReductionInfo &RI = En.value();
5097
5098 // Atomic cross-team fast path: each team's main thread folds its
5099 // team-reduced value directly into the mapped reduction variable with a
5100 // single atomicrmw.
5101 if (IsAtomicReduction) {
5103 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5104 if (!AfterIP)
5105 return AfterIP.takeError();
5106 Builder.restoreIP(*AfterIP);
5107 continue;
5108 }
5109
5111 Value *RedValue = RI.Variable;
5112
5113 Value *RHS =
5114 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5115
5117 Value *LHSPtr, *RHSPtr;
5118 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5119 &LHSPtr, &RHSPtr, CurFunc));
5120
5121 // Fix the CallBack code genereated to use the correct Values for the LHS
5122 // and RHS. Cast to match types before replacing (necessary to handle
5123 // different address spaces).
5124 if (LHSPtr->getType() != RedValue->getType())
5125 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5126 RedValue, LHSPtr->getType());
5127 if (RHSPtr->getType() != RHS->getType())
5128 RHS =
5129 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5130
5131 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5132 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5133 ReductionFunc;
5134 });
5135 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5136 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5137 ReductionFunc;
5138 });
5139 } else {
5140 if (IsByRef.empty() || !IsByRef[En.index()]) {
5141 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5142 "red.value." + Twine(En.index()));
5143 }
5144 Value *PrivateRedValue = Builder.CreateLoad(
5145 ValueType, RHS, "red.private.value" + Twine(En.index()));
5146 Value *Reduced;
5147 InsertPointOrErrorTy AfterIP =
5148 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5149 if (!AfterIP)
5150 return AfterIP.takeError();
5151 Builder.restoreIP(*AfterIP);
5152
5153 if (!IsByRef.empty() && !IsByRef[En.index()])
5154 Builder.CreateStore(Reduced, RI.Variable);
5155 }
5156 }
5157 emitBlock(ExitBB, CurFunc);
5158 if (ContinuationBlock) {
5159 Builder.CreateBr(ContinuationBlock);
5160 Builder.SetInsertPoint(ContinuationBlock);
5161 }
5162 Config.setEmitLLVMUsed();
5163
5164 return Builder.saveIP();
5165}
5166
5168 Type *VoidTy = Type::getVoidTy(M.getContext());
5169 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5170 auto *FuncTy =
5171 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5173 ".omp.reduction.func", &M);
5174}
5175
5177 Function *ReductionFunc,
5179 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5180 IRBuilder<>::InsertPointGuard IPG(Builder);
5181 Module *Module = ReductionFunc->getParent();
5182 BasicBlock *ReductionFuncBlock =
5183 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5184 Builder.SetInsertPoint(ReductionFuncBlock);
5185 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5186 Value *LHSArrayPtr = nullptr;
5187 Value *RHSArrayPtr = nullptr;
5188 if (IsGPU) {
5189 // Need to alloca memory here and deal with the pointers before getting
5190 // LHS/RHS pointers out
5191 //
5192 Argument *Arg0 = ReductionFunc->getArg(0);
5193 Argument *Arg1 = ReductionFunc->getArg(1);
5194 Type *Arg0Type = Arg0->getType();
5195 Type *Arg1Type = Arg1->getType();
5196
5197 Value *LHSAlloca =
5198 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5199 Value *RHSAlloca =
5200 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5201 Value *LHSAddrCast =
5202 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5203 Value *RHSAddrCast =
5204 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5205 Builder.CreateStore(Arg0, LHSAddrCast);
5206 Builder.CreateStore(Arg1, RHSAddrCast);
5207 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5208 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5209 } else {
5210 LHSArrayPtr = ReductionFunc->getArg(0);
5211 RHSArrayPtr = ReductionFunc->getArg(1);
5212 }
5213
5214 unsigned NumReductions = ReductionInfos.size();
5215 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5216
5217 for (auto En : enumerate(ReductionInfos)) {
5218 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5219 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5220 RedArrayTy, LHSArrayPtr, 0, En.index());
5221 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5222 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5223 LHSI8Ptr, RI.Variable->getType());
5224 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5225 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5226 RedArrayTy, RHSArrayPtr, 0, En.index());
5227 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5228 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5229 RHSI8Ptr, RI.PrivateVariable->getType());
5230 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5231 Value *Reduced;
5233 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5234 if (!AfterIP)
5235 return AfterIP.takeError();
5236
5237 Builder.restoreIP(*AfterIP);
5238 // TODO: Consider flagging an error.
5239 if (!Builder.GetInsertBlock())
5240 return Error::success();
5241
5242 // store is inside of the reduction region when using by-ref
5243 if (!IsByRef[En.index()])
5244 Builder.CreateStore(Reduced, LHSPtr);
5245 }
5246 Builder.CreateRetVoid();
5247 return Error::success();
5248}
5249
5251 const LocationDescription &Loc, InsertPointTy AllocaIP,
5252 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5253 bool IsNoWait, bool IsTeamsReduction) {
5254 assert(ReductionInfos.size() == IsByRef.size());
5255 if (Config.isGPU())
5256 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5257 IsByRef, IsNoWait, IsTeamsReduction);
5258
5259 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5260
5261 if (!updateToLocation(Loc))
5262 return InsertPointTy();
5263
5264 if (ReductionInfos.size() == 0)
5265 return Builder.saveIP();
5266
5267 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
5268 BasicBlock *ContinuationBlock =
5269 InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
5270 InsertBlock->getTerminator()->eraseFromParent();
5271
5272 // Create and populate array of type-erased pointers to private reduction
5273 // values.
5274 unsigned NumReductions = ReductionInfos.size();
5275 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5276 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getTerminator());
5277 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5278
5279 Builder.SetInsertPoint(InsertBlock->end());
5280 // Emitting the alloca moved the insertion point into the alloca block and
5281 // can clear the debug loc. Restore back to Loc.DL.
5282 Builder.SetCurrentDebugLocation(Loc.DL);
5283
5284 for (auto En : enumerate(ReductionInfos)) {
5285 unsigned Index = En.index();
5286 const ReductionInfo &RI = En.value();
5287 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5288 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5289 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5290 }
5291
5292 // Emit a call to the runtime function that orchestrates the reduction.
5293 // Declare the reduction function in the process.
5294 Type *IndexTy = Builder.getIndexTy(
5295 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5296 Function *Func = Builder.GetInsertBlock()->getParent();
5297 Module *Module = Func->getParent();
5298 uint32_t SrcLocStrSize;
5299 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5300 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5301 return RI.AtomicReductionGen;
5302 });
5303 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5304 CanGenerateAtomic
5305 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5306 : IdentFlag(0));
5307 Value *ThreadId = getOrCreateThreadID(Ident);
5308 Constant *NumVariables = Builder.getInt32(NumReductions);
5309 const DataLayout &DL = Module->getDataLayout();
5310 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5311 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5312 Function *ReductionFunc = getFreshReductionFunc(*Module);
5313 Value *Lock = getOMPCriticalRegionLock(".reduction");
5315 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5316 : RuntimeFunction::OMPRTL___kmpc_reduce);
5317 CallInst *ReduceCall =
5318 createRuntimeFunctionCall(ReduceFunc,
5319 {Ident, ThreadId, NumVariables, RedArraySize,
5320 RedArray, ReductionFunc, Lock},
5321 "reduce");
5322
5323 // Create final reduction entry blocks for the atomic and non-atomic case.
5324 // Emit IR that dispatches control flow to one of the blocks based on the
5325 // reduction supporting the atomic mode.
5326 BasicBlock *NonAtomicRedBlock =
5327 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5328 BasicBlock *AtomicRedBlock =
5329 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5330 SwitchInst *Switch =
5331 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5332 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5333 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5334
5335 // Populate the non-atomic reduction using the elementwise reduction function.
5336 // This loads the elements from the global and private variables and reduces
5337 // them before storing back the result to the global variable.
5338 Builder.SetInsertPoint(NonAtomicRedBlock);
5339 for (auto En : enumerate(ReductionInfos)) {
5340 const ReductionInfo &RI = En.value();
5342 // We have one less load for by-ref case because that load is now inside of
5343 // the reduction region
5344 Value *RedValue = RI.Variable;
5345 if (!IsByRef[En.index()]) {
5346 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5347 "red.value." + Twine(En.index()));
5348 }
5349 Value *PrivateRedValue =
5350 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5351 "red.private.value." + Twine(En.index()));
5352 Value *Reduced;
5353 InsertPointOrErrorTy AfterIP =
5354 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5355 if (!AfterIP)
5356 return AfterIP.takeError();
5357 Builder.restoreIP(*AfterIP);
5358
5359 if (!Builder.GetInsertBlock())
5360 return InsertPointTy();
5361 // for by-ref case, the load is inside of the reduction region
5362 if (!IsByRef[En.index()])
5363 Builder.CreateStore(Reduced, RI.Variable);
5364 }
5365 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5366 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5367 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5368 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5369 Builder.CreateBr(ContinuationBlock);
5370
5371 // Populate the atomic reduction using the atomic elementwise reduction
5372 // function. There are no loads/stores here because they will be happening
5373 // inside the atomic elementwise reduction.
5374 Builder.SetInsertPoint(AtomicRedBlock);
5375 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5376 for (const ReductionInfo &RI : ReductionInfos) {
5378 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5379 if (!AfterIP)
5380 return AfterIP.takeError();
5381 Builder.restoreIP(*AfterIP);
5382 if (!Builder.GetInsertBlock())
5383 return InsertPointTy();
5384 }
5385 Builder.CreateBr(ContinuationBlock);
5386 } else {
5387 Builder.CreateUnreachable();
5388 }
5389
5390 // Populate the outlined reduction function using the elementwise reduction
5391 // function. Partial values are extracted from the type-erased array of
5392 // pointers to private variables.
5393 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5394 IsByRef, /*isGPU=*/false);
5395 if (Err)
5396 return Err;
5397
5398 if (!Builder.GetInsertBlock())
5399 return InsertPointTy();
5400
5401 Builder.SetInsertPoint(ContinuationBlock);
5402 return Builder.saveIP();
5403}
5404
5407 BodyGenCallbackTy BodyGenCB,
5408 FinalizeCallbackTy FiniCB) {
5409 if (!updateToLocation(Loc))
5410 return Loc.IP;
5411
5412 Directive OMPD = Directive::OMPD_master;
5413 uint32_t SrcLocStrSize;
5414 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5415 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5416 Value *ThreadId = getOrCreateThreadID(Ident);
5417 Value *Args[] = {Ident, ThreadId};
5418
5419 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5420 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5421
5422 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5423 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5424
5425 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5426 /*Conditional*/ true, /*hasFinalize*/ true);
5427}
5428
5431 BodyGenCallbackTy BodyGenCB,
5432 FinalizeCallbackTy FiniCB, Value *Filter) {
5434 if (!updateToLocation(Loc))
5435 return Loc.IP;
5436
5437 Directive OMPD = Directive::OMPD_masked;
5438 uint32_t SrcLocStrSize;
5439 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5440 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5441 Value *ThreadId = getOrCreateThreadID(Ident);
5442 Value *Args[] = {Ident, ThreadId, Filter};
5443 Value *ArgsEnd[] = {Ident, ThreadId};
5444
5445 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5446 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5447
5448 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5449 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5450
5451 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5452 /*Conditional*/ true, /*hasFinalize*/ true);
5453}
5454
5456 llvm::FunctionCallee Callee,
5458 const llvm::Twine &Name) {
5459 llvm::CallInst *Call = Builder.CreateCall(
5460 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5461 Call->setDoesNotThrow();
5462 return Call;
5463}
5464
5465// Expects input basic block is dominated by BeforeScanBB.
5466// Once Scan directive is encountered, the code after scan directive should be
5467// dominated by AfterScanBB. Scan directive splits the code sequence to
5468// scan and input phase. Based on whether inclusive or exclusive
5469// clause is used in the scan directive and whether input loop or scan loop
5470// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5471// input loop and second is the scan loop. The code generated handles only
5472// inclusive scans now.
5474 const LocationDescription &Loc, InsertPointTy AllocaIP,
5475 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5476 bool IsInclusive, ScanInfo *ScanRedInfo) {
5477 if (ScanRedInfo->OMPFirstScanLoop) {
5478 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5479 ScanVarsType, ScanRedInfo);
5480 if (Err)
5481 return Err;
5482 }
5483 if (!updateToLocation(Loc))
5484 return Loc.IP;
5485
5486 llvm::Value *IV = ScanRedInfo->IV;
5487
5488 if (ScanRedInfo->OMPFirstScanLoop) {
5489 // Emit buffer[i] = red; at the end of the input phase.
5490 for (size_t i = 0; i < ScanVars.size(); i++) {
5491 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5492 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5493 Type *DestTy = ScanVarsType[i];
5494 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5495 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5496
5497 Builder.CreateStore(Src, Val);
5498 }
5499 }
5500 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5501 emitBlock(ScanRedInfo->OMPScanDispatch,
5502 Builder.GetInsertBlock()->getParent());
5503
5504 if (!ScanRedInfo->OMPFirstScanLoop) {
5505 IV = ScanRedInfo->IV;
5506 // Emit red = buffer[i]; at the entrance to the scan phase.
5507 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5508 for (size_t i = 0; i < ScanVars.size(); i++) {
5509 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5510 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5511 Type *DestTy = ScanVarsType[i];
5512 Value *SrcPtr =
5513 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5514 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5515 Builder.CreateStore(Src, ScanVars[i]);
5516 }
5517 }
5518
5519 // TODO: Update it to CreateBr and remove dead blocks
5520 llvm::Value *CmpI = Builder.getInt1(true);
5521 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5522 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5523 ScanRedInfo->OMPAfterScanBlock);
5524 } else {
5525 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5526 ScanRedInfo->OMPBeforeScanBlock);
5527 }
5528 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5529 Builder.GetInsertBlock()->getParent());
5530 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5531 return Builder.saveIP();
5532}
5533
5534Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5535 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5536 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5537
5538 Builder.restoreIP(AllocaIP);
5539 // Create the shared pointer at alloca IP.
5540 for (size_t i = 0; i < ScanVars.size(); i++) {
5541 llvm::Value *BuffPtr =
5542 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5543 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5544 }
5545
5546 // Allocate temporary buffer by master thread
5547 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5548 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5549 Builder.restoreIP(CodeGenIP);
5550 Value *AllocSpan =
5551 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5552 for (size_t i = 0; i < ScanVars.size(); i++) {
5553 Type *IntPtrTy = Builder.getInt32Ty();
5554 Value *Allocsize = Builder.CreateTypeSize(
5555 IntPtrTy, M.getDataLayout().getTypeAllocSize(ScanVarsType[i]));
5556 Value *Buff =
5557 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5558 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5559 }
5560 return Error::success();
5561 };
5562 // TODO: Perform finalization actions for variables. This has to be
5563 // called for variables which have destructors/finalizers.
5564 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5565
5566 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5567 llvm::Value *FilterVal = Builder.getInt32(0);
5569 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5570
5571 if (!AfterIP)
5572 return AfterIP.takeError();
5573 Builder.restoreIP(*AfterIP);
5574 BasicBlock *InputBB = Builder.GetInsertBlock();
5575 if (InputBB->hasTerminator())
5576 Builder.SetInsertPoint(InputBB->getTerminator());
5577 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5578 if (!AfterIP)
5579 return AfterIP.takeError();
5580 Builder.restoreIP(*AfterIP);
5581
5582 return Error::success();
5583}
5584
5585Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5586 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5587 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5588 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5589 Builder.restoreIP(CodeGenIP);
5590 for (ReductionInfo RedInfo : ReductionInfos) {
5591 Value *PrivateVar = RedInfo.PrivateVariable;
5592 Value *OrigVar = RedInfo.Variable;
5593 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5594 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5595
5596 Type *SrcTy = RedInfo.ElementType;
5597 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5598 "arrayOffset");
5599 Value *Src = Builder.CreateLoad(SrcTy, Val);
5600
5601 Builder.CreateStore(Src, OrigVar);
5602 Builder.CreateFree(Buff);
5603 }
5604 return Error::success();
5605 };
5606 // TODO: Perform finalization actions for variables. This has to be
5607 // called for variables which have destructors/finalizers.
5608 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5609
5610 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5611 Builder.SetInsertPoint(TI);
5612 else
5613 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5614
5615 llvm::Value *FilterVal = Builder.getInt32(0);
5617 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5618
5619 if (!AfterIP)
5620 return AfterIP.takeError();
5621 Builder.restoreIP(*AfterIP);
5622 BasicBlock *InputBB = Builder.GetInsertBlock();
5623 if (InputBB->hasTerminator())
5624 Builder.SetInsertPoint(InputBB->getTerminator());
5625 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5626 if (!AfterIP)
5627 return AfterIP.takeError();
5628 Builder.restoreIP(*AfterIP);
5629 return Error::success();
5630}
5631
5633 const LocationDescription &Loc,
5635 ScanInfo *ScanRedInfo) {
5636
5637 if (!updateToLocation(Loc))
5638 return Loc.IP;
5639 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5640 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5641 Builder.restoreIP(CodeGenIP);
5642 Function *CurFn = Builder.GetInsertBlock()->getParent();
5643 // for (int k = 0; k <= ceil(log2(n)); ++k)
5644 llvm::BasicBlock *LoopBB =
5645 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5646 llvm::BasicBlock *ExitBB =
5647 splitBB(Builder, false, "omp.outer.log.scan.exit");
5649 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::log2,
5650 Builder.getDoubleTy());
5651 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5652 llvm::Value *Arg =
5653 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5654 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5656 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::ceil,
5657 Builder.getDoubleTy());
5658 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5659 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5660 llvm::Value *NMin1 = Builder.CreateNUWSub(
5661 ScanRedInfo->Span,
5662 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5663 Builder.SetInsertPoint(InputBB);
5664 Builder.CreateBr(LoopBB);
5665 emitBlock(LoopBB, CurFn);
5666 Builder.SetInsertPoint(LoopBB);
5667
5668 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5669 // size pow2k = 1;
5670 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5671 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5672 InputBB);
5673 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5674 InputBB);
5675 // for (size i = n - 1; i >= 2 ^ k; --i)
5676 // tmp[i] op= tmp[i-pow2k];
5677 llvm::BasicBlock *InnerLoopBB =
5678 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5679 llvm::BasicBlock *InnerExitBB =
5680 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5681 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5682 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5683 emitBlock(InnerLoopBB, CurFn);
5684 Builder.SetInsertPoint(InnerLoopBB);
5685 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5686 IVal->addIncoming(NMin1, LoopBB);
5687 for (ReductionInfo RedInfo : ReductionInfos) {
5688 Value *ReductionVal = RedInfo.PrivateVariable;
5689 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5690 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5691 Type *DestTy = RedInfo.ElementType;
5692 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5693 Value *LHSPtr =
5694 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5695 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5696 Value *RHSPtr =
5697 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5698 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5699 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5700 llvm::Value *Result;
5701 InsertPointOrErrorTy AfterIP =
5702 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5703 if (!AfterIP)
5704 return AfterIP.takeError();
5705 Builder.CreateStore(Result, LHSPtr);
5706 }
5707 llvm::Value *NextIVal = Builder.CreateNUWSub(
5708 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5709 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5710 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5711 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5712 emitBlock(InnerExitBB, CurFn);
5713 llvm::Value *Next = Builder.CreateNUWAdd(
5714 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5715 Counter->addIncoming(Next, Builder.GetInsertBlock());
5716 // pow2k <<= 1;
5717 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5718 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5719 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5720 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5721 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5722 return Error::success();
5723 };
5724
5725 // TODO: Perform finalization actions for variables. This has to be
5726 // called for variables which have destructors/finalizers.
5727 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5728
5729 llvm::Value *FilterVal = Builder.getInt32(0);
5731 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5732
5733 if (!AfterIP)
5734 return AfterIP.takeError();
5735 Builder.restoreIP(*AfterIP);
5736 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5737
5738 if (!AfterIP)
5739 return AfterIP.takeError();
5740 Builder.restoreIP(*AfterIP);
5741 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5742 if (Err)
5743 return Err;
5744
5745 return AfterIP;
5746}
5747
5748Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5749 llvm::function_ref<Error()> InputLoopGen,
5750 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5751 ScanInfo *ScanRedInfo) {
5752
5753 {
5754 // Emit loop with input phase:
5755 // for (i: 0..<num_iters>) {
5756 // <input phase>;
5757 // buffer[i] = red;
5758 // }
5759 ScanRedInfo->OMPFirstScanLoop = true;
5760 Error Err = InputLoopGen();
5761 if (Err)
5762 return Err;
5763 }
5764 {
5765 // Emit loop with scan phase:
5766 // for (i: 0..<num_iters>) {
5767 // red = buffer[i];
5768 // <scan phase>;
5769 // }
5770 ScanRedInfo->OMPFirstScanLoop = false;
5771 Error Err = ScanLoopGen(Builder);
5772 if (Err)
5773 return Err;
5774 }
5775 return Error::success();
5776}
5777
5778void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5779 Function *Fun = Builder.GetInsertBlock()->getParent();
5780 ScanRedInfo->OMPScanDispatch =
5781 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5782 ScanRedInfo->OMPAfterScanBlock =
5783 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5784 ScanRedInfo->OMPBeforeScanBlock =
5785 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5786 ScanRedInfo->OMPScanLoopExit =
5787 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5788}
5790 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5791 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5792 Module *M = F->getParent();
5793 LLVMContext &Ctx = M->getContext();
5794 Type *IndVarTy = TripCount->getType();
5795
5796 // Create the basic block structure.
5797 BasicBlock *Preheader =
5798 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5799 BasicBlock *Header =
5800 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5801 BasicBlock *Cond =
5802 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5803 BasicBlock *Body =
5804 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5805 BasicBlock *Latch =
5806 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5807 BasicBlock *Exit =
5808 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5809 BasicBlock *After =
5810 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5811
5812 // Use specified DebugLoc for new instructions.
5813 Builder.SetCurrentDebugLocation(DL);
5814
5815 Builder.SetInsertPoint(Preheader);
5816 Builder.CreateBr(Header);
5817
5818 Builder.SetInsertPoint(Header);
5819 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5820 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5821 Builder.CreateBr(Cond);
5822
5823 Builder.SetInsertPoint(Cond);
5824 Value *Cmp =
5825 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5826 Builder.CreateCondBr(Cmp, Body, Exit);
5827
5828 Builder.SetInsertPoint(Body);
5829 Builder.CreateBr(Latch);
5830
5831 Builder.SetInsertPoint(Latch);
5832 // Decide whether the induction variable increment can carry nsw.
5833 //
5834 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5835 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5836 // for valid programs 0 <= count <= INT_MAX always holds.
5837 //
5838 // Collapsed loops: the trip count is a product that can overflow i32 even for
5839 // a conforming program, so nsw is kept only when the product is a constant
5840 // that provably fits, dropped otherwise.
5841 bool HasNSW = Config.hasNoSignedWrap();
5842 if (HasNSW) {
5843 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5844 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5846 if (CI->getValue().ugt(SignedMax))
5847 HasNSW = false;
5848 } else if (IsCollapsed) {
5849 HasNSW = false;
5850 }
5851 }
5852 Value *Next =
5853 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5854 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5855 Builder.CreateBr(Header);
5856 IndVarPHI->addIncoming(Next, Latch);
5857
5858 Builder.SetInsertPoint(Exit);
5859 Builder.CreateBr(After);
5860
5861 // Remember and return the canonical control flow.
5862 LoopInfos.emplace_front();
5863 CanonicalLoopInfo *CL = &LoopInfos.front();
5864
5865 CL->Header = Header;
5866 CL->Cond = Cond;
5867 CL->Latch = Latch;
5868 CL->Exit = Exit;
5869
5870#ifndef NDEBUG
5871 CL->assertOK();
5872#endif
5873 return CL;
5874}
5875
5878 LoopBodyGenCallbackTy BodyGenCB,
5879 Value *TripCount, const Twine &Name) {
5880 BasicBlock *BB = Loc.IP.getNodeParent();
5881 BasicBlock *NextBB = BB->getNextNode();
5882
5883 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5884 NextBB, NextBB, Name);
5885 BasicBlock *After = CL->getAfter();
5886
5887 // If location is not set, don't connect the loop.
5888 if (updateToLocation(Loc)) {
5889 // Split the loop at the insertion point: Branch to the preheader and move
5890 // every following instruction to after the loop (the After BB). Also, the
5891 // new successor is the loop's after block.
5892 spliceBB(Builder, After, /*CreateBranch=*/false);
5893 Builder.CreateBr(CL->getPreheader());
5894 }
5895
5896 // Emit the body content. We do it after connecting the loop to the CFG to
5897 // avoid that the callback encounters degenerate BBs.
5898 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5899 return Err;
5900
5901#ifndef NDEBUG
5902 CL->assertOK();
5903#endif
5904 return CL;
5905}
5906
5908 ScanInfos.emplace_front();
5909 ScanInfo *Result = &ScanInfos.front();
5910 return Result;
5911}
5912
5916 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5917 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5918 LocationDescription ComputeLoc =
5919 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5920 updateToLocation(ComputeLoc);
5921
5923
5925 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5926 ScanRedInfo->Span = TripCount;
5927 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5928 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5929
5930 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5931 Builder.restoreIP(CodeGenIP);
5932 ScanRedInfo->IV = IV;
5933 createScanBBs(ScanRedInfo);
5934 BasicBlock *InputBlock = Builder.GetInsertBlock();
5935 Instruction *Terminator = InputBlock->getTerminator();
5936 assert(Terminator->getNumSuccessors() == 1);
5937 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5938 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5939 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5940 Builder.GetInsertBlock()->getParent());
5941 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5942 emitBlock(ScanRedInfo->OMPScanLoopExit,
5943 Builder.GetInsertBlock()->getParent());
5944 Builder.CreateBr(ContinueBlock);
5945 Builder.SetInsertPoint(
5946 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5947 return BodyGenCB(Builder.saveIP(), IV);
5948 };
5949
5950 const auto &&InputLoopGen = [&]() -> Error {
5952 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5953 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5954 if (!LoopInfo)
5955 return LoopInfo.takeError();
5956 Result.push_back(*LoopInfo);
5957 Builder.restoreIP((*LoopInfo)->getAfterIP());
5958 return Error::success();
5959 };
5960 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5962 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5963 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5964 if (!LoopInfo)
5965 return LoopInfo.takeError();
5966 Result.push_back(*LoopInfo);
5967 Builder.restoreIP((*LoopInfo)->getAfterIP());
5968 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5969 return Error::success();
5970 };
5971 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5972 if (Err)
5973 return Err;
5974 return Result;
5975}
5976
5978 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5979 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5980
5981 // Consider the following difficulties (assuming 8-bit signed integers):
5982 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5983 // DO I = 1, 100, 50
5984 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5985 // DO I = 100, 0, -128
5986
5987 // Start, Stop and Step must be of the same integer type.
5988 auto *IndVarTy = cast<IntegerType>(Start->getType());
5989 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5990 assert(IndVarTy == Step->getType() && "Step type mismatch");
5991
5993
5994 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5995 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5996
5997 // Like Step, but always positive.
5998 Value *Incr = Step;
5999
6000 // Distance between Start and Stop; always positive.
6001 Value *Span;
6002
6003 // Condition whether there are no iterations are executed at all, e.g. because
6004 // UB < LB.
6005 Value *ZeroCmp;
6006
6007 if (IsSigned) {
6008 // Ensure that increment is positive. If not, negate and invert LB and UB.
6009 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6010 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6011 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6012 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6013 Span = Builder.CreateSub(UB, LB, "", false, true);
6014 ZeroCmp = Builder.CreateICmp(
6015 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6016 } else {
6017 Span = Builder.CreateSub(Stop, Start, "", true);
6018 ZeroCmp = Builder.CreateICmp(
6019 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6020 }
6021
6022 Value *CountIfLooping;
6023 if (InclusiveStop) {
6024 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6025 } else {
6026 // Avoid incrementing past stop since it could overflow.
6027 Value *CountIfTwo = Builder.CreateAdd(
6028 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6029 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6030 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6031 }
6032
6033 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6034 "omp_" + Name + ".tripcount");
6035}
6036
6039 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6040 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6041 ScanInfo *ScanRedInfo) {
6042 LocationDescription ComputeLoc =
6043 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6044
6046 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6047
6048 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6049 Builder.restoreIP(CodeGenIP);
6050 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6051 /*HasNSW=*/Config.hasNoSignedWrap());
6052 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6053 /*HasNSW=*/Config.hasNoSignedWrap());
6054 if (InScan)
6055 ScanRedInfo->IV = IndVar;
6056 return BodyGenCB(Builder.saveIP(), IndVar);
6057 };
6058 LocationDescription LoopLoc =
6059 ComputeIP.isValid()
6060 ? Loc
6061 : LocationDescription(Builder.saveIP(),
6062 Builder.getCurrentDebugLocation());
6063 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6064}
6065
6066// Returns an LLVM function to call for initializing loop bounds using OpenMP
6067// static scheduling for composite `distribute parallel for` depending on
6068// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6069// integers as unsigned similarly to CanonicalLoopInfo.
6070static FunctionCallee
6072 OpenMPIRBuilder &OMPBuilder) {
6073 unsigned Bitwidth = Ty->getIntegerBitWidth();
6074 if (Bitwidth == 32)
6075 return OMPBuilder.getOrCreateRuntimeFunction(
6076 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6077 if (Bitwidth == 64)
6078 return OMPBuilder.getOrCreateRuntimeFunction(
6079 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6080 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6081}
6082
6083// Returns an LLVM function to call for initializing loop bounds using OpenMP
6084// static scheduling depending on `type`. Only i32 and i64 are supported by the
6085// runtime. Always interpret integers as unsigned similarly to
6086// CanonicalLoopInfo.
6088 OpenMPIRBuilder &OMPBuilder) {
6089 unsigned Bitwidth = Ty->getIntegerBitWidth();
6090 if (Bitwidth == 32)
6091 return OMPBuilder.getOrCreateRuntimeFunction(
6092 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6093 if (Bitwidth == 64)
6094 return OMPBuilder.getOrCreateRuntimeFunction(
6095 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6096 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6097}
6098
6099OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6100 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6101 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6102 OMPScheduleType DistScheduleSchedType) {
6103 assert(CLI->isValid() && "Requires a valid canonical loop");
6104 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6105 "Require dedicated allocate IP");
6106
6107 // Set up the source location value for OpenMP runtime.
6108 Builder.restoreIP(CLI->getPreheaderIP());
6109 Builder.SetCurrentDebugLocation(DL);
6110
6111 uint32_t SrcLocStrSize;
6112 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6114 switch (LoopType) {
6115 case WorksharingLoopType::ForStaticLoop:
6116 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6117 break;
6118 case WorksharingLoopType::DistributeStaticLoop:
6119 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6120 break;
6121 case WorksharingLoopType::DistributeForStaticLoop:
6122 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6123 break;
6124 }
6125 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6126
6127 // Declare useful OpenMP runtime functions.
6128 Value *IV = CLI->getIndVar();
6129 Type *IVTy = IV->getType();
6130 FunctionCallee StaticInit =
6131 LoopType == WorksharingLoopType::DistributeForStaticLoop
6132 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6133 : getKmpcForStaticInitForType(IVTy, M, *this);
6134 FunctionCallee StaticFini =
6135 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6136
6137 // Allocate space for computed loop bounds as expected by the "init" function.
6138 Builder.SetInsertPoint(
6139 AllocaIP.getNodeParent()->getFirstNonPHIOrDbgOrAlloca());
6140
6141 Type *I32Type = Type::getInt32Ty(M.getContext());
6142 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6143 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6144 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6145 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6146 CLI->setLastIter(PLastIter);
6147
6148 // At the end of the preheader, prepare for calling the "init" function by
6149 // storing the current loop bounds into the allocated space. A canonical loop
6150 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6151 // and produces an inclusive upper bound.
6152 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6153 Constant *Zero = ConstantInt::get(IVTy, 0);
6154 Constant *One = ConstantInt::get(IVTy, 1);
6155 Builder.CreateStore(Zero, PLowerBound);
6156 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6157 Builder.CreateStore(UpperBound, PUpperBound);
6158 Builder.CreateStore(One, PStride);
6159
6160 Value *ThreadNum =
6161 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6162
6163 OMPScheduleType SchedType =
6164 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6165 ? OMPScheduleType::OrderedDistribute
6167 Constant *SchedulingType =
6168 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6169
6170 // Call the "init" function and update the trip count of the loop with the
6171 // value it produced.
6172 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6173 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6174 this](Value *SchedulingType, auto &Builder) {
6175 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6176 PLowerBound, PUpperBound});
6177 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6178 Value *PDistUpperBound =
6179 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6180 Args.push_back(PDistUpperBound);
6181 }
6182 Args.append({PStride, One, Zero});
6183 createRuntimeFunctionCall(StaticInit, Args);
6184 };
6185 BuildInitCall(SchedulingType, Builder);
6186 if (HasDistSchedule &&
6187 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6188 Constant *DistScheduleSchedType = ConstantInt::get(
6189 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6190 // We want to emit a second init function call for the dist_schedule clause
6191 // to the Distribute construct. This should only be done however if a
6192 // Workshare Loop is nested within a Distribute Construct
6193 BuildInitCall(DistScheduleSchedType, Builder);
6194 }
6195 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6196 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6197 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6198 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6199 CLI->setTripCount(TripCount);
6200
6201 // Update all uses of the induction variable except the one in the condition
6202 // block that compares it with the actual upper bound, and the increment in
6203 // the latch block.
6204
6205 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6206 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6207 Builder.SetCurrentDebugLocation(DL);
6208 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6209 /*HasNSW=*/Config.hasNoSignedWrap());
6210 });
6211
6212 // In the "exit" block, call the "fini" function.
6213 Builder.SetInsertPoint(CLI->getExit()->getTerminator()->getIterator());
6214 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6215
6216 // Add the barrier if requested.
6217 if (NeedsBarrier) {
6218 InsertPointOrErrorTy BarrierIP =
6220 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6221 /* CheckCancelFlag */ false);
6222 if (!BarrierIP)
6223 return BarrierIP.takeError();
6224 }
6225
6226 InsertPointTy AfterIP = CLI->getAfterIP();
6227 CLI->invalidate();
6228
6229 return AfterIP;
6230}
6231
6232static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6233 LoopInfo &LI);
6234static void addLoopMetadata(CanonicalLoopInfo *Loop,
6236
6238 LLVMContext &Ctx, Loop *Loop,
6240 SmallVector<Metadata *> &LoopMDList) {
6241 SmallSet<BasicBlock *, 8> Reachable;
6242
6243 // Get the basic blocks from the loop in which memref instructions
6244 // can be found.
6245 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6246 // preferably without running any passes.
6247 for (BasicBlock *Block : Loop->getBlocks()) {
6248 if (Block == CLI->getCond() || Block == CLI->getHeader())
6249 continue;
6250 Reachable.insert(Block);
6251 }
6252
6253 // Add access group metadata to memory-access instructions.
6255 for (BasicBlock *BB : Reachable)
6257 // TODO: If the loop has existing parallel access metadata, have
6258 // to combine two lists.
6259 LoopMDList.push_back(MDNode::get(
6260 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6261}
6262
6264OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6265 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6266 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6267 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6268 assert(CLI->isValid() && "Requires a valid canonical loop");
6269 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6270
6271 LLVMContext &Ctx = CLI->getFunction()->getContext();
6272 Value *IV = CLI->getIndVar();
6273 Value *OrigTripCount = CLI->getTripCount();
6274 Type *IVTy = IV->getType();
6275 assert(IVTy->getIntegerBitWidth() <= 64 &&
6276 "Max supported tripcount bitwidth is 64 bits");
6277 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6278 : Type::getInt64Ty(Ctx);
6279 Type *I32Type = Type::getInt32Ty(M.getContext());
6280 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6281 Constant *One = ConstantInt::get(InternalIVTy, 1);
6282
6283 Function *F = CLI->getFunction();
6284 // Blocks must have terminators.
6285 // FIXME: Don't run analyses on incomplete/invalid IR.
6286 SmallVector<Instruction *> UIs;
6287 for (BasicBlock &BB : *F)
6288 if (!BB.hasTerminator())
6289 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6291 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6292 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6293 LoopAnalysis LIA;
6294 LoopInfo &&LI = LIA.run(*F, FAM);
6295 for (Instruction *I : UIs)
6296 I->eraseFromParent();
6297 Loop *L = LI.getLoopFor(CLI->getHeader());
6298 SmallVector<Metadata *> LoopMDList;
6299 if (ChunkSize || DistScheduleChunkSize)
6300 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6301 addLoopMetadata(CLI, LoopMDList);
6302
6303 // Declare useful OpenMP runtime functions.
6304 FunctionCallee StaticInit =
6305 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6306 FunctionCallee StaticFini =
6307 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6308
6309 // Allocate space for computed loop bounds as expected by the "init" function.
6310 Builder.restoreIP(AllocaIP);
6311 Builder.SetCurrentDebugLocation(DL);
6312 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6313 Value *PLowerBound =
6314 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6315 Value *PUpperBound =
6316 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6317 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6318 CLI->setLastIter(PLastIter);
6319
6320 // Set up the source location value for the OpenMP runtime.
6321 Builder.restoreIP(CLI->getPreheaderIP());
6322 Builder.SetCurrentDebugLocation(DL);
6323
6324 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6325 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6326 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6327 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6328 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6329 "distschedulechunksize");
6330 Value *CastedTripCount =
6331 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6332
6333 Constant *SchedulingType =
6334 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6335 Constant *DistSchedulingType =
6336 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6337 Builder.CreateStore(Zero, PLowerBound);
6338 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6339 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6340 Value *UpperBound =
6341 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6342 Builder.CreateStore(UpperBound, PUpperBound);
6343 Builder.CreateStore(One, PStride);
6344
6345 // Call the "init" function and update the trip count of the loop with the
6346 // value it produced.
6347 uint32_t SrcLocStrSize;
6348 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6349 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6350 if (DistScheduleSchedType != OMPScheduleType::None) {
6351 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6352 }
6353 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6354 Value *ThreadNum =
6355 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6356 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6357 PUpperBound, PStride, One,
6358 this](Value *SchedulingType, Value *ChunkSize,
6359 auto &Builder) {
6361 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6362 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6363 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6364 /*pstride=*/PStride, /*incr=*/One,
6365 /*chunk=*/ChunkSize});
6366 };
6367 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6368 if (DistScheduleSchedType != OMPScheduleType::None &&
6369 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6370 SchedType != OMPScheduleType::OrderedDistribute) {
6371 // We want to emit a second init function call for the dist_schedule clause
6372 // to the Distribute construct. This should only be done however if a
6373 // Workshare Loop is nested within a Distribute Construct
6374 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6375 }
6376
6377 // Load values written by the "init" function.
6378 Value *FirstChunkStart =
6379 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6380 Value *FirstChunkStop =
6381 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6382 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6383 Value *ChunkRange =
6384 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6385 Value *NextChunkStride =
6386 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6387
6388 // Create outer "dispatch" loop for enumerating the chunks.
6389 BasicBlock *DispatchEnter = splitBB(Builder, true);
6390 Value *DispatchCounter;
6391
6392 // It is safe to assume this didn't return an error because the callback
6393 // passed into createCanonicalLoop is the only possible error source, and it
6394 // always returns success.
6395 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6396 {Builder.saveIP(), DL},
6397 [&](InsertPointTy BodyIP, Value *Counter) {
6398 DispatchCounter = Counter;
6399 return Error::success();
6400 },
6401 FirstChunkStart, CastedTripCount, NextChunkStride,
6402 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6403 "dispatch"));
6404
6405 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6406 // not have to preserve the canonical invariant.
6407 BasicBlock *DispatchBody = DispatchCLI->getBody();
6408 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6409 BasicBlock *DispatchExit = DispatchCLI->getExit();
6410 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6411 DispatchCLI->invalidate();
6412
6413 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6414 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6415 redirectTo(CLI->getExit(), DispatchLatch, DL);
6416 redirectTo(DispatchBody, DispatchEnter, DL);
6417
6418 // Prepare the prolog of the chunk loop.
6419 Builder.restoreIP(CLI->getPreheaderIP());
6420 Builder.SetCurrentDebugLocation(DL);
6421
6422 // Compute the number of iterations of the chunk loop.
6423 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6424 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6425 Value *IsLastChunk =
6426 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6427 Value *CountUntilOrigTripCount =
6428 Builder.CreateSub(CastedTripCount, DispatchCounter);
6429 Value *ChunkTripCount = Builder.CreateSelect(
6430 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6431 Value *BackcastedChunkTC =
6432 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6433 CLI->setTripCount(BackcastedChunkTC);
6434
6435 // Update all uses of the induction variable except the one in the condition
6436 // block that compares it with the actual upper bound, and the increment in
6437 // the latch block.
6438 Value *BackcastedDispatchCounter =
6439 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6440 CLI->mapIndVar([&](Instruction *) -> Value * {
6441 Builder.restoreIP(CLI->getBodyIP());
6442 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6443 });
6444
6445 // In the "exit" block, call the "fini" function.
6446 Builder.SetInsertPoint(DispatchExit->getFirstInsertionPt());
6447 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6448
6449 // Add the barrier if requested.
6450 if (NeedsBarrier) {
6451 InsertPointOrErrorTy AfterIP =
6452 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6453 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6454 if (!AfterIP)
6455 return AfterIP.takeError();
6456 }
6457
6458#ifndef NDEBUG
6459 // Even though we currently do not support applying additional methods to it,
6460 // the chunk loop should remain a canonical loop.
6461 CLI->assertOK();
6462#endif
6463
6464 return DispatchAfter->getFirstInsertionPt();
6465}
6466
6467// Returns an LLVM function to call for executing an OpenMP static worksharing
6468// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6469// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6470static FunctionCallee
6472 WorksharingLoopType LoopType) {
6473 unsigned Bitwidth = Ty->getIntegerBitWidth();
6474 Module &M = OMPBuilder->M;
6475 switch (LoopType) {
6476 case WorksharingLoopType::ForStaticLoop:
6477 if (Bitwidth == 32)
6478 return OMPBuilder->getOrCreateRuntimeFunction(
6479 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6480 if (Bitwidth == 64)
6481 return OMPBuilder->getOrCreateRuntimeFunction(
6482 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6483 break;
6484 case WorksharingLoopType::DistributeStaticLoop:
6485 if (Bitwidth == 32)
6486 return OMPBuilder->getOrCreateRuntimeFunction(
6487 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6488 if (Bitwidth == 64)
6489 return OMPBuilder->getOrCreateRuntimeFunction(
6490 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6491 break;
6492 case WorksharingLoopType::DistributeForStaticLoop:
6493 if (Bitwidth == 32)
6494 return OMPBuilder->getOrCreateRuntimeFunction(
6495 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6496 if (Bitwidth == 64)
6497 return OMPBuilder->getOrCreateRuntimeFunction(
6498 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6499 break;
6500 }
6501 if (Bitwidth != 32 && Bitwidth != 64) {
6502 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6503 }
6504 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6505}
6506
6507// Inserts a call to proper OpenMP Device RTL function which handles
6508// loop worksharing.
6510 WorksharingLoopType LoopType,
6511 BasicBlock *InsertBlock, Value *Ident,
6512 Value *LoopBodyArg, Value *TripCount,
6513 Function &LoopBodyFn, bool NoLoop) {
6514 Type *TripCountTy = TripCount->getType();
6515 Module &M = OMPBuilder->M;
6516 IRBuilder<> &Builder = OMPBuilder->Builder;
6517 FunctionCallee RTLFn =
6518 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6519 SmallVector<Value *, 8> RealArgs;
6520 RealArgs.push_back(Ident);
6521 RealArgs.push_back(&LoopBodyFn);
6522 RealArgs.push_back(LoopBodyArg);
6523 RealArgs.push_back(TripCount);
6524 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6525 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6526 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6527 Builder.restoreIP(std::prev(InsertBlock->end()));
6528 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6529 return;
6530 }
6531 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6532 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6533 Builder.restoreIP(std::prev(InsertBlock->end()));
6534 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6535
6536 RealArgs.push_back(
6537 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6538 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6539 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6540 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6541 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6542 } else {
6543 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6544 }
6545
6546 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6547}
6548
6550 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6551 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6552 WorksharingLoopType LoopType, bool NoLoop) {
6553 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6554 BasicBlock *Preheader = CLI->getPreheader();
6555 Value *TripCount = CLI->getTripCount();
6556
6557 // After loop body outling, the loop body contains only set up
6558 // of loop body argument structure and the call to the outlined
6559 // loop body function. Firstly, we need to move setup of loop body args
6560 // into loop preheader.
6561 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6562 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6563
6564 // The next step is to remove the whole loop. We do not it need anymore.
6565 // That's why make an unconditional branch from loop preheader to loop
6566 // exit block
6567 Builder.restoreIP(Preheader->end());
6568 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6569 Preheader->getTerminator()->eraseFromParent();
6570 Builder.CreateBr(CLI->getExit());
6571
6572 // Delete dead loop blocks
6573 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6574 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6575 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6576 CleanUpInfo.EntryBB = CLI->getHeader();
6577 CleanUpInfo.ExitBB = CLI->getExit();
6578 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6579 DeleteDeadBlocks(BlocksToBeRemoved);
6580
6581 // Find the instruction which corresponds to loop body argument structure
6582 // and remove the call to loop body function instruction.
6583 Value *LoopBodyArg;
6584 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6585 assert(OutlinedFnUser &&
6586 "Expected unique undroppable user of outlined function");
6587 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6588 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6589 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6590 "Expected outlined function call to be located in loop preheader");
6591 // Check in case no argument structure has been passed.
6592 if (OutlinedFnCallInstruction->arg_size() > 1)
6593 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6594 else
6595 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6596 OutlinedFnCallInstruction->eraseFromParent();
6597
6598 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6599 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6600
6601 for (auto &ToBeDeletedItem : ToBeDeleted)
6602 ToBeDeletedItem->eraseFromParent();
6603 CLI->invalidate();
6604}
6605
6606OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6607 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6608 WorksharingLoopType LoopType, bool NeedsBarrier, bool NoLoop,
6609 bool NeedsLastIter) {
6610 uint32_t SrcLocStrSize;
6611 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6612
6613 // Mirrors host runtime reporting of last iteration by in-body computation.
6614 if (NeedsLastIter) {
6615 Type *I32Type = Type::getInt32Ty(M.getContext());
6616 Builder.restoreIP(AllocaIP);
6617 AllocaInst *PLastIter =
6618 Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6619 CLI->setLastIter(PLastIter);
6620
6621 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6622 Builder.CreateStore(ConstantInt::get(I32Type, 0), PLastIter);
6623
6624 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6625 Value *TripCount = CLI->getTripCount();
6626 Value *LastIter =
6627 Builder.CreateSub(TripCount, ConstantInt::get(TripCount->getType(), 1));
6628 Value *IsLast =
6629 Builder.CreateICmpEQ(CLI->getIndVar(), LastIter, "omp.is_last_iter");
6630 Builder.CreateStore(Builder.CreateZExt(IsLast, I32Type), PLastIter);
6631 }
6632
6634 switch (LoopType) {
6635 case WorksharingLoopType::ForStaticLoop:
6636 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6637 break;
6638 case WorksharingLoopType::DistributeStaticLoop:
6639 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6640 break;
6641 case WorksharingLoopType::DistributeForStaticLoop:
6642 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6643 break;
6644 }
6645 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6646
6647 auto OI = std::make_unique<OutlineInfo>();
6648 OI->OuterAllocBB = CLI->getPreheader();
6649 Function *OuterFn = CLI->getPreheader()->getParent();
6650
6651 // Instructions which need to be deleted at the end of code generation
6652 SmallVector<Instruction *, 4> ToBeDeleted;
6653
6654 OI->OuterAllocBB = AllocaIP.getNodeParent();
6655
6656 // Mark the body loop as region which needs to be extracted
6657 OI->EntryBB = CLI->getBody();
6658 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6659 "omp.prelatch");
6660
6661 // Prepare loop body for extraction
6662 Builder.restoreIP(CLI->getPreheader()->begin());
6663
6664 // Insert new loop counter variable which will be used only in loop
6665 // body.
6666 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6667 Instruction *NewLoopCntLoad =
6668 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6669 // New loop counter instructions are redundant in the loop preheader when
6670 // code generation for workshare loop is finshed. That's why mark them as
6671 // ready for deletion.
6672 ToBeDeleted.push_back(NewLoopCntLoad);
6673 ToBeDeleted.push_back(NewLoopCnt);
6674
6675 // Analyse loop body region. Find all input variables which are used inside
6676 // loop body region.
6677 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6679 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6680
6681 CodeExtractorAnalysisCache CEAC(*OuterFn);
6682 CodeExtractor Extractor(Blocks,
6683 /* DominatorTree */ nullptr,
6684 /* AggregateArgs */ true,
6685 /* BlockFrequencyInfo */ nullptr,
6686 /* BranchProbabilityInfo */ nullptr,
6687 /* AssumptionCache */ nullptr,
6688 /* AllowVarArgs */ true,
6689 /* AllowAlloca */ true,
6690 /* AllocationBlock */ CLI->getPreheader(),
6691 /* DeallocationBlocks */ {},
6692 /* Suffix */ ".omp_wsloop",
6693 /* AggrArgsIn0AddrSpace */ true);
6694
6695 BasicBlock *CommonExit = nullptr;
6696 SetVector<Value *> SinkingCands, HoistingCands;
6697
6698 // Find allocas outside the loop body region which are used inside loop
6699 // body
6700 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6701
6702 // We need to model loop body region as the function f(cnt, loop_arg).
6703 // That's why we replace loop induction variable by the new counter
6704 // which will be one of loop body function argument
6706 CLI->getIndVar()->user_end());
6707 for (auto Use : Users) {
6708 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6709 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6710 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6711 }
6712 }
6713 }
6714 // Make sure that loop counter variable is not merged into loop body
6715 // function argument structure and it is passed as separate variable
6716 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6717
6718 // PostOutline CB is invoked when loop body function is outlined and
6719 // loop body is replaced by call to outlined function. We need to add
6720 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6721 // function will handle loop control logic.
6722 //
6723 OI->PostOutlineCB = [=, ToBeDeletedVec =
6724 std::move(ToBeDeleted)](Function &OutlinedFn) {
6725 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6726 LoopType, NoLoop);
6727 };
6728 addOutlineInfo(std::move(OI));
6729
6730 // Keep the barrier outside the outlined loop body so that every thread
6731 // encounters it, including threads that execute no iterations.
6732 if (NeedsBarrier) {
6733 Builder.SetInsertPoint(CLI->getExit()->getTerminator());
6734 // Standalone distribute loops never request a barrier. For both regular
6735 // worksharing loops and combined distribute/for loops, the barrier is
6736 // associated with the worksharing loop, hence OMPD_for.
6737 InsertPointOrErrorTy BarrierIP =
6738 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6739 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6740 if (!BarrierIP)
6741 return BarrierIP.takeError();
6742 }
6743 return CLI->getAfterIP();
6744}
6745
6748 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6749 bool HasSimdModifier, bool HasMonotonicModifier,
6750 bool HasNonmonotonicModifier, bool HasOrderedClause,
6751 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6752 Value *DistScheduleChunkSize, bool NeedsLastIter) {
6753 if (Config.isTargetDevice())
6754 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6755 NoLoop, NeedsLastIter);
6756 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6757 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6758 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6759
6760 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6761 OMPScheduleType::ModifierOrdered;
6762 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6763 if (HasDistSchedule) {
6764 DistScheduleSchedType = DistScheduleChunkSize
6765 ? OMPScheduleType::OrderedDistributeChunked
6766 : OMPScheduleType::OrderedDistribute;
6767 }
6768 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6769 case OMPScheduleType::BaseStatic:
6770 case OMPScheduleType::BaseDistribute:
6771 assert((!ChunkSize || !DistScheduleChunkSize) &&
6772 "No chunk size with static-chunked schedule");
6773 if (IsOrdered && !HasDistSchedule)
6774 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6775 NeedsBarrier, ChunkSize);
6776 // FIXME: Monotonicity ignored?
6777 if (DistScheduleChunkSize)
6778 return applyStaticChunkedWorkshareLoop(
6779 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6780 DistScheduleChunkSize, DistScheduleSchedType);
6781 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6782 HasDistSchedule);
6783
6784 case OMPScheduleType::BaseStaticChunked:
6785 case OMPScheduleType::BaseDistributeChunked:
6786 if (IsOrdered && !HasDistSchedule)
6787 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6788 NeedsBarrier, ChunkSize);
6789 // FIXME: Monotonicity ignored?
6790 return applyStaticChunkedWorkshareLoop(
6791 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6792 DistScheduleChunkSize, DistScheduleSchedType);
6793
6794 case OMPScheduleType::BaseRuntime:
6795 case OMPScheduleType::BaseAuto:
6796 case OMPScheduleType::BaseGreedy:
6797 case OMPScheduleType::BaseBalanced:
6798 case OMPScheduleType::BaseSteal:
6799 case OMPScheduleType::BaseRuntimeSimd:
6800 assert(!ChunkSize &&
6801 "schedule type does not support user-defined chunk sizes");
6802 [[fallthrough]];
6803 case OMPScheduleType::BaseGuidedSimd:
6804 case OMPScheduleType::BaseDynamicChunked:
6805 case OMPScheduleType::BaseGuidedChunked:
6806 case OMPScheduleType::BaseGuidedIterativeChunked:
6807 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6808 case OMPScheduleType::BaseStaticBalancedChunked:
6809 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6810 NeedsBarrier, ChunkSize);
6811
6812 default:
6813 llvm_unreachable("Unknown/unimplemented schedule kind");
6814 }
6815}
6816
6817/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6818/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6819/// the runtime. Always interpret integers as unsigned similarly to
6820/// CanonicalLoopInfo.
6821static FunctionCallee
6823 unsigned Bitwidth = Ty->getIntegerBitWidth();
6824 if (Bitwidth == 32)
6825 return OMPBuilder.getOrCreateRuntimeFunction(
6826 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6827 if (Bitwidth == 64)
6828 return OMPBuilder.getOrCreateRuntimeFunction(
6829 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6830 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6831}
6832
6833/// Returns an LLVM function to call for updating the next loop using OpenMP
6834/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6835/// the runtime. Always interpret integers as unsigned similarly to
6836/// CanonicalLoopInfo.
6837static FunctionCallee
6839 unsigned Bitwidth = Ty->getIntegerBitWidth();
6840 if (Bitwidth == 32)
6841 return OMPBuilder.getOrCreateRuntimeFunction(
6842 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6843 if (Bitwidth == 64)
6844 return OMPBuilder.getOrCreateRuntimeFunction(
6845 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6846 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6847}
6848
6849/// Returns an LLVM function to call for finalizing the dynamic loop using
6850/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6851/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6852static FunctionCallee
6854 unsigned Bitwidth = Ty->getIntegerBitWidth();
6855 if (Bitwidth == 32)
6856 return OMPBuilder.getOrCreateRuntimeFunction(
6857 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6858 if (Bitwidth == 64)
6859 return OMPBuilder.getOrCreateRuntimeFunction(
6860 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6861 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6862}
6863
6865OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6866 InsertPointTy AllocaIP,
6867 OMPScheduleType SchedType,
6868 bool NeedsBarrier, Value *Chunk) {
6869 assert(CLI->isValid() && "Requires a valid canonical loop");
6870 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6871 "Require dedicated allocate IP");
6873 "Require valid schedule type");
6874
6875 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6876 OMPScheduleType::ModifierOrdered;
6877
6878 // Set up the source location value for OpenMP runtime.
6879 Builder.SetCurrentDebugLocation(DL);
6880
6881 uint32_t SrcLocStrSize;
6882 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6883 Value *SrcLoc =
6884 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6885
6886 // Declare useful OpenMP runtime functions.
6887 Value *IV = CLI->getIndVar();
6888 Type *IVTy = IV->getType();
6889 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6890 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6891
6892 // Allocate space for computed loop bounds as expected by the "init" function.
6893 Builder.SetInsertPoint(
6894 AllocaIP.getNodeParent()->getFirstNonPHIOrDbgOrAlloca());
6895 Type *I32Type = Type::getInt32Ty(M.getContext());
6896 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6897 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6898 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6899 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6900 CLI->setLastIter(PLastIter);
6901
6902 // At the end of the preheader, prepare for calling the "init" function by
6903 // storing the current loop bounds into the allocated space. A canonical loop
6904 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6905 // and produces an inclusive upper bound.
6906 BasicBlock *PreHeader = CLI->getPreheader();
6907 Builder.SetInsertPoint(PreHeader->getTerminator());
6908 Constant *One = ConstantInt::get(IVTy, 1);
6909 Builder.CreateStore(One, PLowerBound);
6910 Value *UpperBound = CLI->getTripCount();
6911 Builder.CreateStore(UpperBound, PUpperBound);
6912 Builder.CreateStore(One, PStride);
6913
6914 BasicBlock *Header = CLI->getHeader();
6915 BasicBlock *Exit = CLI->getExit();
6916 BasicBlock *Cond = CLI->getCond();
6917 BasicBlock *Latch = CLI->getLatch();
6918 InsertPointTy AfterIP = CLI->getAfterIP();
6919
6920 // The CLI will be "broken" in the code below, as the loop is no longer
6921 // a valid canonical loop.
6922
6923 if (!Chunk)
6924 Chunk = One;
6925
6926 Value *ThreadNum =
6927 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6928
6929 Constant *SchedulingType =
6930 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6931
6932 // Call the "init" function.
6933 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6934 /* LowerBound */ One, UpperBound,
6935 /* step */ One, Chunk});
6936
6937 // An outer loop around the existing one.
6938 BasicBlock *OuterCond = BasicBlock::Create(
6939 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6940 PreHeader->getParent());
6941 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6942 Builder.SetInsertPoint(OuterCond->getFirstInsertionPt());
6944 DynamicNext,
6945 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6946 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6947 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6948 Value *LowerBound =
6949 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6950 Builder.CreateCondBr(MoreWork, Header, Exit);
6951
6952 // Change PHI-node in loop header to use outer cond rather than preheader,
6953 // and set IV to the LowerBound.
6954 Instruction *Phi = &Header->front();
6955 auto *PI = cast<PHINode>(Phi);
6956 PI->setIncomingBlock(0, OuterCond);
6957 PI->setIncomingValue(0, LowerBound);
6958
6959 // Then set the pre-header to jump to the OuterCond
6960 Instruction *Term = PreHeader->getTerminator();
6961 auto *Br = cast<UncondBrInst>(Term);
6962 Br->setSuccessor(OuterCond);
6963
6964 // Modify the inner condition:
6965 // * Use the UpperBound returned from the DynamicNext call.
6966 // * jump to the loop outer loop when done with one of the inner loops.
6967 Builder.SetInsertPoint(Cond->getFirstInsertionPt());
6968 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6969 Instruction *Comp = &*Builder.GetInsertPoint();
6970 auto *CI = cast<CmpInst>(Comp);
6971 CI->setOperand(1, UpperBound);
6972 // Redirect the inner exit to branch to outer condition.
6973 Instruction *Branch = &Cond->back();
6974 auto *BI = cast<CondBrInst>(Branch);
6975 assert(BI->getSuccessor(1) == Exit);
6976 BI->setSuccessor(1, OuterCond);
6977
6978 // Call the "fini" function if "ordered" is present in wsloop directive.
6979 if (Ordered) {
6980 Builder.SetInsertPoint(&Latch->back());
6981 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6982 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6983 }
6984
6985 // Add the barrier if requested.
6986 if (NeedsBarrier) {
6987 Builder.SetInsertPoint(&Exit->back());
6988 InsertPointOrErrorTy BarrierIP =
6990 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6991 /* CheckCancelFlag */ false);
6992 if (!BarrierIP)
6993 return BarrierIP.takeError();
6994 }
6995
6996 CLI->invalidate();
6997 return AfterIP;
6998}
6999
7000/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
7001/// after this \p OldTarget will be orphaned.
7003 BasicBlock *NewTarget, DebugLoc DL) {
7004 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
7005 redirectTo(Pred, NewTarget, DL);
7006}
7007
7009 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
7010 // We add a block to BBsToKeep iff we have proven it has an external use.
7012
7013 while (true) {
7014 bool Changed = false;
7015
7016 for (BasicBlock *BB : BBs) {
7017 if (BBsToKeep.contains(BB))
7018 continue;
7019
7020 for (Use &U : BB->uses()) {
7021 auto *UseInst = dyn_cast<Instruction>(U.getUser());
7022 if (!UseInst)
7023 continue;
7024 BasicBlock *UseBB = UseInst->getParent();
7025 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
7026 BBsToKeep.insert(BB);
7027 Changed = true;
7028 break;
7029 }
7030 }
7031 }
7032
7033 if (!Changed)
7034 break;
7035 }
7036
7038 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7039 DeleteDeadBlocks(BBsToDelete);
7040}
7041
7042CanonicalLoopInfo *
7044 InsertPointTy ComputeIP) {
7045 assert(Loops.size() >= 1 && "At least one loop required");
7046 size_t NumLoops = Loops.size();
7047
7048 // Nothing to do if there is already just one loop.
7049 if (NumLoops == 1)
7050 return Loops.front();
7051
7052 CanonicalLoopInfo *Outermost = Loops.front();
7053 CanonicalLoopInfo *Innermost = Loops.back();
7054 BasicBlock *OrigPreheader = Outermost->getPreheader();
7055 BasicBlock *OrigAfter = Outermost->getAfter();
7056 Function *F = OrigPreheader->getParent();
7057
7058 // Loop control blocks that may become orphaned later.
7059 SmallVector<BasicBlock *, 12> OldControlBBs;
7060 OldControlBBs.reserve(6 * Loops.size());
7062 Loop->collectControlBlocks(OldControlBBs);
7063
7064 // Setup the IRBuilder for inserting the trip count computation.
7065 Builder.SetCurrentDebugLocation(DL);
7066 if (ComputeIP.isValid())
7067 Builder.restoreIP(ComputeIP);
7068 else
7069 Builder.restoreIP(Outermost->getPreheaderIP());
7070
7071 // Derive the collapsed' loop trip count.
7072 // TODO: Find common/largest indvar type.
7073 Value *CollapsedTripCount = nullptr;
7074 for (CanonicalLoopInfo *L : Loops) {
7075 assert(L->isValid() &&
7076 "All loops to collapse must be valid canonical loops");
7077 Value *OrigTripCount = L->getTripCount();
7078 if (!CollapsedTripCount) {
7079 CollapsedTripCount = OrigTripCount;
7080 continue;
7081 }
7082
7083 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7084 CollapsedTripCount =
7085 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7086 }
7087
7088 // Create the collapsed loop control flow.
7089 CanonicalLoopInfo *Result =
7090 createLoopSkeleton(DL, CollapsedTripCount, F,
7091 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7092 /*IsCollapsed=*/true);
7093
7094 // Build the collapsed loop body code.
7095 // Start with deriving the input loop induction variables from the collapsed
7096 // one, using a divmod scheme. To preserve the original loops' order, the
7097 // innermost loop use the least significant bits.
7098 Builder.restoreIP(Result->getBodyIP());
7099
7100 Value *Leftover = Result->getIndVar();
7101 SmallVector<Value *> NewIndVars;
7102 NewIndVars.resize(NumLoops);
7103 for (int i = NumLoops - 1; i >= 1; --i) {
7104 Value *OrigTripCount = Loops[i]->getTripCount();
7105
7106 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7107 NewIndVars[i] = NewIndVar;
7108
7109 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7110 }
7111 // Outermost loop gets all the remaining bits.
7112 NewIndVars[0] = Leftover;
7113
7114 // Construct the loop body control flow.
7115 // We progressively construct the branch structure following in direction of
7116 // the control flow, from the leading in-between code, the loop nest body, the
7117 // trailing in-between code, and rejoining the collapsed loop's latch.
7118 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7119 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7120 // its predecessors as sources.
7121 BasicBlock *ContinueBlock = Result->getBody();
7122 BasicBlock *ContinuePred = nullptr;
7123 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7124 BasicBlock *NextSrc) {
7125 if (ContinueBlock)
7126 redirectTo(ContinueBlock, Dest, DL);
7127 else
7128 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7129
7130 ContinueBlock = nullptr;
7131 ContinuePred = NextSrc;
7132 };
7133
7134 // The code before the nested loop of each level.
7135 // Because we are sinking it into the nest, it will be executed more often
7136 // that the original loop. More sophisticated schemes could keep track of what
7137 // the in-between code is and instantiate it only once per thread.
7138 for (size_t i = 0; i < NumLoops - 1; ++i)
7139 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7140
7141 // Connect the loop nest body.
7142 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7143
7144 // The code after the nested loop at each level.
7145 for (size_t i = NumLoops - 1; i > 0; --i)
7146 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7147
7148 // Connect the finished loop to the collapsed loop latch.
7149 ContinueWith(Result->getLatch(), nullptr);
7150
7151 // Replace the input loops with the new collapsed loop.
7152 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7153 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7154
7155 // Replace the input loop indvars with the derived ones.
7156 for (size_t i = 0; i < NumLoops; ++i)
7157 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7158
7159 // Remove unused parts of the input loops.
7160 removeUnusedBlocksFromParent(OldControlBBs);
7161
7162 for (CanonicalLoopInfo *L : Loops)
7163 L->invalidate();
7164
7165#ifndef NDEBUG
7166 Result->assertOK();
7167#endif
7168 return Result;
7169}
7170
7171std::vector<CanonicalLoopInfo *>
7173 ArrayRef<Value *> TileSizes) {
7174 assert(TileSizes.size() == Loops.size() &&
7175 "Must pass as many tile sizes as there are loops");
7176 int NumLoops = Loops.size();
7177 assert(NumLoops >= 1 && "At least one loop to tile required");
7178
7179 CanonicalLoopInfo *OutermostLoop = Loops.front();
7180 CanonicalLoopInfo *InnermostLoop = Loops.back();
7181 Function *F = OutermostLoop->getBody()->getParent();
7182 BasicBlock *InnerEnter = InnermostLoop->getBody();
7183 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7184
7185 // Loop control blocks that may become orphaned later.
7186 SmallVector<BasicBlock *, 12> OldControlBBs;
7187 OldControlBBs.reserve(6 * Loops.size());
7189 Loop->collectControlBlocks(OldControlBBs);
7190
7191 // Collect original trip counts and induction variable to be accessible by
7192 // index. Also, the structure of the original loops is not preserved during
7193 // the construction of the tiled loops, so do it before we scavenge the BBs of
7194 // any original CanonicalLoopInfo.
7195 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7196 for (CanonicalLoopInfo *L : Loops) {
7197 assert(L->isValid() && "All input loops must be valid canonical loops");
7198 OrigTripCounts.push_back(L->getTripCount());
7199 OrigIndVars.push_back(L->getIndVar());
7200 }
7201
7202 // Collect the code between loop headers. These may contain SSA definitions
7203 // that are used in the loop nest body. To be usable with in the innermost
7204 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7205 // these instructions may be executed more often than before the tiling.
7206 // TODO: It would be sufficient to only sink them into body of the
7207 // corresponding tile loop.
7209 for (int i = 0; i < NumLoops - 1; ++i) {
7210 CanonicalLoopInfo *Surrounding = Loops[i];
7211 CanonicalLoopInfo *Nested = Loops[i + 1];
7212
7213 BasicBlock *EnterBB = Surrounding->getBody();
7214 BasicBlock *ExitBB = Nested->getHeader();
7215 InbetweenCode.emplace_back(EnterBB, ExitBB);
7216 }
7217
7218 // Compute the trip counts of the floor loops.
7219 Builder.SetCurrentDebugLocation(DL);
7220 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7221 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7222 for (int i = 0; i < NumLoops; ++i) {
7223 Value *TileSize = TileSizes[i];
7224 Value *OrigTripCount = OrigTripCounts[i];
7225 Type *IVType = OrigTripCount->getType();
7226
7227 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7228 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7229
7230 // 0 if tripcount divides the tilesize, 1 otherwise.
7231 // 1 means we need an additional iteration for a partial tile.
7232 //
7233 // Unfortunately we cannot just use the roundup-formula
7234 // (tripcount + tilesize - 1)/tilesize
7235 // because the summation might overflow. We do not want introduce undefined
7236 // behavior when the untiled loop nest did not.
7237 Value *FloorTripOverflow =
7238 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7239
7240 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7241 Value *FloorTripCount =
7242 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7243 "omp_floor" + Twine(i) + ".tripcount", true);
7244
7245 // Remember some values for later use.
7246 FloorCompleteCount.push_back(FloorCompleteTripCount);
7247 FloorCount.push_back(FloorTripCount);
7248 FloorRems.push_back(FloorTripRem);
7249 }
7250
7251 // Generate the new loop nest, from the outermost to the innermost.
7252 std::vector<CanonicalLoopInfo *> Result;
7253 Result.reserve(NumLoops * 2);
7254
7255 // The basic block of the surrounding loop that enters the nest generated
7256 // loop.
7257 BasicBlock *Enter = OutermostLoop->getPreheader();
7258
7259 // The basic block of the surrounding loop where the inner code should
7260 // continue.
7261 BasicBlock *Continue = OutermostLoop->getAfter();
7262
7263 // Where the next loop basic block should be inserted.
7264 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7265
7266 auto EmbeddNewLoop =
7267 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7268 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7269 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7270 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7271 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7272 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7273
7274 // Setup the position where the next embedded loop connects to this loop.
7275 Enter = EmbeddedLoop->getBody();
7276 Continue = EmbeddedLoop->getLatch();
7277 OutroInsertBefore = EmbeddedLoop->getLatch();
7278 return EmbeddedLoop;
7279 };
7280
7281 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7282 const Twine &NameBase) {
7283 for (auto P : enumerate(TripCounts)) {
7284 CanonicalLoopInfo *EmbeddedLoop =
7285 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7286 Result.push_back(EmbeddedLoop);
7287 }
7288 };
7289
7290 EmbeddNewLoops(FloorCount, "floor");
7291
7292 // Within the innermost floor loop, emit the code that computes the tile
7293 // sizes.
7294 Builder.SetInsertPoint(Enter->getTerminator());
7295 SmallVector<Value *, 4> TileCounts;
7296 for (int i = 0; i < NumLoops; ++i) {
7297 CanonicalLoopInfo *FloorLoop = Result[i];
7298 Value *TileSize = TileSizes[i];
7299
7300 Value *FloorIsEpilogue =
7301 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7302 Value *TileTripCount =
7303 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7304
7305 TileCounts.push_back(TileTripCount);
7306 }
7307
7308 // Create the tile loops.
7309 EmbeddNewLoops(TileCounts, "tile");
7310
7311 // Insert the inbetween code into the body.
7312 BasicBlock *BodyEnter = Enter;
7313 BasicBlock *BodyEntered = nullptr;
7314 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7315 BasicBlock *EnterBB = P.first;
7316 BasicBlock *ExitBB = P.second;
7317
7318 if (BodyEnter)
7319 redirectTo(BodyEnter, EnterBB, DL);
7320 else
7321 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7322
7323 BodyEnter = nullptr;
7324 BodyEntered = ExitBB;
7325 }
7326
7327 // Append the original loop nest body into the generated loop nest body.
7328 if (BodyEnter)
7329 redirectTo(BodyEnter, InnerEnter, DL);
7330 else
7331 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7333
7334 // Replace the original induction variable with an induction variable computed
7335 // from the tile and floor induction variables.
7336 Builder.restoreIP(Result.back()->getBodyIP());
7337 for (int i = 0; i < NumLoops; ++i) {
7338 CanonicalLoopInfo *FloorLoop = Result[i];
7339 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7340 Value *OrigIndVar = OrigIndVars[i];
7341 Value *Size = TileSizes[i];
7342
7343 Value *Scale =
7344 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7345 Value *Shift =
7346 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7347 OrigIndVar->replaceAllUsesWith(Shift);
7348 }
7349
7350 // Remove unused parts of the original loops.
7351 removeUnusedBlocksFromParent(OldControlBBs);
7352
7353 for (CanonicalLoopInfo *L : Loops)
7354 L->invalidate();
7355
7356#ifndef NDEBUG
7357 for (CanonicalLoopInfo *GenL : Result)
7358 GenL->assertOK();
7359#endif
7360 return Result;
7361}
7362
7363/// Attach metadata \p Properties to the basic block described by \p BB. If the
7364/// basic block already has metadata, the basic block properties are appended.
7367 // Nothing to do if no property to attach.
7368 if (Properties.empty())
7369 return;
7370
7371 LLVMContext &Ctx = BB->getContext();
7372 SmallVector<Metadata *> NewProperties;
7373 NewProperties.push_back(nullptr);
7374
7375 // If the basic block already has metadata, prepend it to the new metadata.
7376 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7377 if (Existing)
7378 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7379
7380 append_range(NewProperties, Properties);
7381 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7382 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7383
7384 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7385}
7386
7387/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7388/// loop already has metadata, the loop properties are appended.
7391 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7392
7393 // Attach metadata to the loop's latch
7394 BasicBlock *Latch = Loop->getLatch();
7395 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7397}
7398
7399/// Attach llvm.access.group metadata to the memref instructions of \p Block
7401 LoopInfo &LI) {
7402 for (Instruction &I : *Block) {
7403 if (I.mayReadOrWriteMemory()) {
7404 // TODO: This instruction may already have access group from
7405 // other pragmas e.g. #pragma clang loop vectorize. Append
7406 // so that the existing metadata is not overwritten.
7407 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7408 }
7409 }
7410}
7411
7412CanonicalLoopInfo *
7414 CanonicalLoopInfo *firstLoop = Loops.front();
7415 CanonicalLoopInfo *lastLoop = Loops.back();
7416 Function *F = firstLoop->getPreheader()->getParent();
7417
7418 // Loop control blocks that will become orphaned later
7419 SmallVector<BasicBlock *> oldControlBBs;
7421 Loop->collectControlBlocks(oldControlBBs);
7422
7423 // Collect original trip counts
7424 SmallVector<Value *> origTripCounts;
7425 for (CanonicalLoopInfo *L : Loops) {
7426 assert(L->isValid() && "All input loops must be valid canonical loops");
7427 origTripCounts.push_back(L->getTripCount());
7428 }
7429
7430 Builder.SetCurrentDebugLocation(DL);
7431
7432 // Compute max trip count.
7433 // The fused loop will be from 0 to max(origTripCounts)
7434 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7435 F, firstLoop->getHeader());
7436 Builder.SetInsertPoint(TCBlock);
7437 Value *fusedTripCount = nullptr;
7438 for (CanonicalLoopInfo *L : Loops) {
7439 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7440 Value *origTripCount = L->getTripCount();
7441 if (!fusedTripCount) {
7442 fusedTripCount = origTripCount;
7443 continue;
7444 }
7445 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7446 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7447 ".omp.fuse.tc");
7448 }
7449
7450 // Generate new loop
7451 CanonicalLoopInfo *fused =
7452 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7453 lastLoop->getLatch(), "fused");
7454
7455 // Replace original loops with the fused loop
7456 // Preheader and After are not considered inside the CLI.
7457 // These are used to compute the individual TCs of the loops
7458 // so they have to be put before the resulting fused loop.
7459 // Moving them up for readability.
7460 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7461 Loops[i]->getPreheader()->moveBefore(TCBlock);
7462 Loops[i]->getAfter()->moveBefore(TCBlock);
7463 }
7464 lastLoop->getPreheader()->moveBefore(TCBlock);
7465
7466 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7467 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7468 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7469 }
7470 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7471 redirectTo(TCBlock, fused->getPreheader(), DL);
7472 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7473
7474 // Build the fused body
7475 // Create new Blocks with conditions that jump to the original loop bodies
7477 SmallVector<Value *> condValues;
7478 for (size_t i = 0; i < Loops.size(); ++i) {
7479 BasicBlock *condBlock = BasicBlock::Create(
7480 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7481 Builder.SetInsertPoint(condBlock);
7482 Value *condValue =
7483 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7484 condBBs.push_back(condBlock);
7485 condValues.push_back(condValue);
7486 }
7487 // Join the condition blocks with the bodies of the original loops
7488 redirectTo(fused->getBody(), condBBs[0], DL);
7489 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7490 Builder.SetInsertPoint(condBBs[i]);
7491 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7492 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7493 // Replace the IV with the fused IV
7494 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7495 }
7496 // Last body jumps to the created end body block
7497 Builder.SetInsertPoint(condBBs.back());
7498 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7499 fused->getLatch());
7500 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7501 // Replace the IV with the fused IV
7502 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7503
7504 // The loop latch must have only one predecessor. Currently it is branched to
7505 // from both the last condition block and the last loop body
7506 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7507 "omp.fused.pre_latch");
7508
7509 // Remove unused parts
7510 removeUnusedBlocksFromParent(oldControlBBs);
7511
7512 // Invalidate old CLIs
7513 for (CanonicalLoopInfo *L : Loops)
7514 L->invalidate();
7515
7516#ifndef NDEBUG
7517 fused->assertOK();
7518#endif
7519 return fused;
7520}
7521
7523 LLVMContext &Ctx = Builder.getContext();
7525 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7526 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7527}
7528
7530 LLVMContext &Ctx = Builder.getContext();
7532 Loop, {
7533 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7534 });
7535}
7536
7537void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7538 Value *IfCond, ValueToValueMapTy &VMap,
7539 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7540 const Twine &NamePrefix) {
7541 Function *F = CanonicalLoop->getFunction();
7542
7543 // We can't do
7544 // if (cond) {
7545 // simd_loop;
7546 // } else {
7547 // non_simd_loop;
7548 // }
7549 // because then the CanonicalLoopInfo would only point to one of the loops:
7550 // leading to other constructs operating on the same loop to malfunction.
7551 // Instead generate
7552 // while (...) {
7553 // if (cond) {
7554 // simd_body;
7555 // } else {
7556 // not_simd_body;
7557 // }
7558 // }
7559 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7560 // body at -O3
7561
7562 // Define where if branch should be inserted
7563 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7564
7565 // Create additional blocks for the if statement
7566 BasicBlock *Cond = SplitBeforeIt->getParent();
7567 llvm::LLVMContext &C = Cond->getContext();
7569 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7571 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7572
7573 // Create if condition branch.
7574 Builder.SetInsertPoint(SplitBeforeIt);
7575 Instruction *BrInstr =
7576 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7577 InsertPointTy IP(++BrInstr->getIterator());
7578 // Then block contains branch to omp loop body which needs to be vectorized
7579 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7580 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7581
7582 Builder.SetInsertPoint(ElseBlock);
7583
7584 // Clone loop for the else branch
7586
7587 SmallVector<BasicBlock *, 8> ExistingBlocks;
7588 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7589 ExistingBlocks.push_back(ThenBlock);
7590 ExistingBlocks.append(L->block_begin(), L->block_end());
7591 // Cond is the block that has the if clause condition
7592 // LoopCond is omp_loop.cond
7593 // LoopHeader is omp_loop.header
7594 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7595 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7596 assert(LoopCond && LoopHeader && "Invalid loop structure");
7597 for (BasicBlock *Block : ExistingBlocks) {
7598 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7599 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7600 continue;
7601 }
7602 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7603
7604 // fix name not to be omp.if.then
7605 if (Block == ThenBlock)
7606 NewBB->setName(NamePrefix + ".if.else");
7607
7608 NewBB->moveBefore(CanonicalLoop->getExit());
7609 VMap[Block] = NewBB;
7610 NewBlocks.push_back(NewBB);
7611 }
7612 remapInstructionsInBlocks(NewBlocks, VMap);
7613 Builder.CreateBr(NewBlocks.front());
7614
7615 // The loop latch must have only one predecessor. Currently it is branched to
7616 // from both the 'then' and 'else' branches.
7617 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7618 NamePrefix + ".pre_latch");
7619
7620 // Ensure that the then block is added to the loop so we add the attributes in
7621 // the next step
7622 L->addBasicBlockToLoop(ThenBlock, LI);
7623}
7624
7625unsigned
7627 const StringMap<bool> &Features) {
7628 if (TargetTriple.isX86()) {
7629 if (Features.lookup("avx512f"))
7630 return 512;
7631 else if (Features.lookup("avx"))
7632 return 256;
7633 return 128;
7634 }
7635 if (TargetTriple.isPPC())
7636 return 128;
7637 if (TargetTriple.isWasm())
7638 return 128;
7639 if (TargetTriple.isSystemZ())
7640 return 64;
7641 return 0;
7642}
7643
7645 MapVector<Value *, Value *> AlignedVars,
7646 Value *IfCond, OrderKind Order,
7647 ConstantInt *Simdlen, ConstantInt *Safelen) {
7648 LLVMContext &Ctx = Builder.getContext();
7649
7650 Function *F = CanonicalLoop->getFunction();
7651
7652 // Blocks must have terminators.
7653 // FIXME: Don't run analyses on incomplete/invalid IR.
7655 for (BasicBlock &BB : *F)
7656 if (!BB.hasTerminator())
7657 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7658
7659 // TODO: We should not rely on pass manager. Currently we use pass manager
7660 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7661 // object. We should have a method which returns all blocks between
7662 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7664 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7665 FAM.registerPass([]() { return LoopAnalysis(); });
7666 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7667
7668 LoopAnalysis LIA;
7669 LoopInfo &&LI = LIA.run(*F, FAM);
7670
7671 for (Instruction *I : UIs)
7672 I->eraseFromParent();
7673
7674 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7675 if (AlignedVars.size()) {
7676 InsertPointTy IP = Builder.saveIP();
7677 for (auto &AlignedItem : AlignedVars) {
7678 Value *AlignedPtr = AlignedItem.first;
7679 Value *Alignment = AlignedItem.second;
7680 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7681 Builder.SetInsertPoint(loadInst->getNextNode());
7682 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7683 Alignment);
7684 }
7685 Builder.restoreIP(IP);
7686 }
7687
7688 if (IfCond) {
7689 ValueToValueMapTy VMap;
7690 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7691 }
7692
7694
7695 // Get the basic blocks from the loop in which memref instructions
7696 // can be found.
7697 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7698 // preferably without running any passes.
7699 for (BasicBlock *Block : L->getBlocks()) {
7700 if (Block == CanonicalLoop->getCond() ||
7701 Block == CanonicalLoop->getHeader())
7702 continue;
7703 Reachable.insert(Block);
7704 }
7705
7706 SmallVector<Metadata *> LoopMDList;
7707
7708 // In presence of finite 'safelen', it may be unsafe to mark all
7709 // the memory instructions parallel, because loop-carried
7710 // dependences of 'safelen' iterations are possible.
7711 // If clause order(concurrent) is specified then the memory instructions
7712 // are marked parallel even if 'safelen' is finite.
7713 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7714 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7715
7716 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7717 // versions so we can't add the loop attributes in that case.
7718 if (IfCond) {
7719 // we can still add llvm.loop.parallel_access
7720 addLoopMetadata(CanonicalLoop, LoopMDList);
7721 return;
7722 }
7723
7724 // Use the above access group metadata to create loop level
7725 // metadata, which should be distinct for each loop.
7726 LoopMDList.push_back(
7727 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7728
7729 if (Simdlen || Safelen) {
7730 // If both simdlen and safelen clauses are specified, the value of the
7731 // simdlen parameter must be less than or equal to the value of the safelen
7732 // parameter. Therefore, use safelen only in the absence of simdlen.
7733 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7734 LoopMDList.push_back(
7735 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7736 ConstantAsMetadata::get(VectorizeWidth)}));
7737 }
7738
7739 addLoopMetadata(CanonicalLoop, LoopMDList);
7740}
7741
7742/// Create the TargetMachine object to query the backend for optimization
7743/// preferences.
7744///
7745/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7746/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7747/// needed for the LLVM pass pipline. We use some default options to avoid
7748/// having to pass too many settings from the frontend that probably do not
7749/// matter.
7750///
7751/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7752/// method. If we are going to use TargetMachine for more purposes, especially
7753/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7754/// might become be worth requiring front-ends to pass on their TargetMachine,
7755/// or at least cache it between methods. Note that while fontends such as Clang
7756/// have just a single main TargetMachine per translation unit, "target-cpu" and
7757/// "target-features" that determine the TargetMachine are per-function and can
7758/// be overrided using __attribute__((target("OPTIONS"))).
7759static std::unique_ptr<TargetMachine>
7761 Module *M = F->getParent();
7762
7763 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7764 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7765 const llvm::Triple &Triple = M->getTargetTriple();
7766
7767 std::string Error;
7769 if (!TheTarget)
7770 return {};
7771
7773 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7774 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7775 /*CodeModel=*/std::nullopt, OptLevel));
7776}
7777
7778/// Heuristically determine the best-performant unroll factor for \p CLI. This
7779/// depends on the target processor. We are re-using the same heuristics as the
7780/// LoopUnrollPass.
7782 Function *F = CLI->getFunction();
7783
7784 // Assume the user requests the most aggressive unrolling, even if the rest of
7785 // the code is optimized using a lower setting.
7787 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7788
7789 // Blocks must have terminators.
7790 // FIXME: Don't run analyses on incomplete/invalid IR.
7792 for (BasicBlock &BB : *F)
7793 if (!BB.hasTerminator())
7794 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7795
7797 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7798 FAM.registerPass([]() { return AssumptionAnalysis(); });
7799 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7800 FAM.registerPass([]() { return LoopAnalysis(); });
7801 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7802 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7803 TargetIRAnalysis TIRA;
7804 if (TM)
7805 TIRA = TargetIRAnalysis(
7806 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7807 FAM.registerPass([&]() { return TIRA; });
7808
7809 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7811 ScalarEvolution &&SE = SEA.run(*F, FAM);
7813 DominatorTree &&DT = DTA.run(*F, FAM);
7814 LoopAnalysis LIA;
7815 LoopInfo &&LI = LIA.run(*F, FAM);
7817 AssumptionCache &&AC = ACT.run(*F, FAM);
7819
7820 for (Instruction *I : UIs)
7821 I->eraseFromParent();
7822
7823 Loop *L = LI.getLoopFor(CLI->getHeader());
7824 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7825
7827 L, SE, TTI,
7828 /*BlockFrequencyInfo=*/nullptr,
7829 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7830 /*UserThreshold=*/std::nullopt,
7831 /*UserAllowPartial=*/true,
7832 /*UserAllowRuntime=*/true,
7833 /*UserUpperBound=*/std::nullopt,
7834 /*UserFullUnrollMaxCount=*/std::nullopt);
7835
7836 UP.Force = true;
7837
7838 // Account for additional optimizations taking place before the LoopUnrollPass
7839 // would unroll the loop.
7842
7843 // Use normal unroll factors even if the rest of the code is optimized for
7844 // size.
7847
7848 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7849 << " Threshold=" << UP.Threshold << "\n"
7850 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7851 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7852 << " PartialOptSizeThreshold="
7853 << UP.PartialOptSizeThreshold << "\n");
7854
7855 // Disable peeling.
7858 /*UserAllowPeeling=*/false,
7859 /*UserAllowProfileBasedPeeling=*/false,
7860 /*UnrollingSpecficValues=*/false);
7861
7863 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7864
7865 // Assume that reads and writes to stack variables can be eliminated by
7866 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7867 // size.
7868 for (BasicBlock *BB : L->blocks()) {
7869 for (Instruction &I : *BB) {
7870 Value *Ptr;
7871 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7872 Ptr = Load->getPointerOperand();
7873 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7874 Ptr = Store->getPointerOperand();
7875 } else
7876 continue;
7877
7878 Ptr = Ptr->stripPointerCasts();
7879
7880 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7881 if (Alloca->getParent() == &F->getEntryBlock())
7882 EphValues.insert(&I);
7883 }
7884 }
7885 }
7886
7887 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7888
7889 // Loop is not unrollable if the loop contains certain instructions.
7890 if (!UCE.canUnroll()) {
7891 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7892 return 1;
7893 }
7894
7895 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7896 << "\n");
7897
7898 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7899 // be able to use it.
7900 int TripCount = 0;
7901 int MaxTripCount = 0;
7902 bool MaxOrZero = false;
7903 unsigned TripMultiple = 0;
7904
7905 unsigned Factor =
7906 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7907 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7908 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7909
7910 // This function returns 1 to signal to not unroll a loop.
7911 if (Factor == 0)
7912 return 1;
7913 return Factor;
7914}
7915
7917 int32_t Factor,
7918 CanonicalLoopInfo **UnrolledCLI) {
7919 assert(Factor >= 0 && "Unroll factor must not be negative");
7920
7921 Function *F = Loop->getFunction();
7922 LLVMContext &Ctx = F->getContext();
7923
7924 // If the unrolled loop is not used for another loop-associated directive, it
7925 // is sufficient to add metadata for the LoopUnrollPass.
7926 if (!UnrolledCLI) {
7927 SmallVector<Metadata *, 2> LoopMetadata;
7928 LoopMetadata.push_back(
7929 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7930
7931 if (Factor >= 1) {
7933 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7934 LoopMetadata.push_back(MDNode::get(
7935 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7936 }
7937
7938 addLoopMetadata(Loop, LoopMetadata);
7939 return;
7940 }
7941
7942 // Heuristically determine the unroll factor.
7943 if (Factor == 0)
7945
7946 // No change required with unroll factor 1.
7947 if (Factor == 1) {
7948 *UnrolledCLI = Loop;
7949 return;
7950 }
7951
7952 assert(Factor >= 2 &&
7953 "unrolling only makes sense with a factor of 2 or larger");
7954
7955 Type *IndVarTy = Loop->getIndVarType();
7956
7957 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7958 // unroll the inner loop.
7959 Value *FactorVal =
7960 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7961 /*isSigned=*/false));
7962 std::vector<CanonicalLoopInfo *> LoopNest =
7963 tileLoops(DL, {Loop}, {FactorVal});
7964 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7965 *UnrolledCLI = LoopNest[0];
7966 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7967
7968 // LoopUnrollPass can only fully unroll loops with constant trip count.
7969 // Unroll by the unroll factor with a fallback epilog for the remainder
7970 // iterations if necessary.
7972 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7974 InnerLoop,
7975 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7977 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7978
7979#ifndef NDEBUG
7980 (*UnrolledCLI)->assertOK();
7981#endif
7982}
7983
7986 llvm::Value *BufSize, llvm::Value *CpyBuf,
7987 llvm::Value *CpyFn, llvm::Value *DidIt) {
7988 if (!updateToLocation(Loc))
7989 return Loc.IP;
7990
7991 uint32_t SrcLocStrSize;
7992 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7993 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7994 Value *ThreadId = getOrCreateThreadID(Ident);
7995
7996 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7997
7998 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7999
8000 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
8001 createRuntimeFunctionCall(Fn, Args);
8002
8003 return Builder.saveIP();
8004}
8005
8007 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8008 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
8010
8011 if (!updateToLocation(Loc))
8012 return Loc.IP;
8013
8014 // If needed allocate and initialize `DidIt` with 0.
8015 // DidIt: flag variable: 1=single thread; 0=not single thread.
8016 llvm::Value *DidIt = nullptr;
8017 if (!CPVars.empty()) {
8018 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
8019 Builder.CreateStore(Builder.getInt32(0), DidIt);
8020 }
8021
8022 Directive OMPD = Directive::OMPD_single;
8023 uint32_t SrcLocStrSize;
8024 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8025 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8026 Value *ThreadId = getOrCreateThreadID(Ident);
8027 Value *Args[] = {Ident, ThreadId};
8028
8029 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8030 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8031
8032 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8033 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8034
8035 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8036 if (Error Err = FiniCB(IP))
8037 return Err;
8038
8039 // The thread that executes the single region must set `DidIt` to 1.
8040 // This is used by __kmpc_copyprivate, to know if the caller is the
8041 // single thread or not.
8042 if (DidIt)
8043 Builder.CreateStore(Builder.getInt32(1), DidIt);
8044
8045 return Error::success();
8046 };
8047
8048 // generates the following:
8049 // if (__kmpc_single()) {
8050 // .... single region ...
8051 // __kmpc_end_single
8052 // }
8053 // __kmpc_copyprivate
8054 // __kmpc_barrier
8055
8056 InsertPointOrErrorTy AfterIP =
8057 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8058 /*Conditional*/ true,
8059 /*hasFinalize*/ true);
8060 if (!AfterIP)
8061 return AfterIP.takeError();
8062
8063 if (DidIt) {
8064 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8065 // NOTE BufSize is currently unused, so just pass 0.
8067 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8068 CPFuncs[I], DidIt);
8069 // NOTE __kmpc_copyprivate already inserts a barrier
8070 } else if (!IsNowait) {
8071 InsertPointOrErrorTy AfterIP =
8073 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8074 /* CheckCancelFlag */ false);
8075 if (!AfterIP)
8076 return AfterIP.takeError();
8077 }
8078 return Builder.saveIP();
8079}
8080
8083 BodyGenCallbackTy BodyGenCB,
8084 FinalizeCallbackTy FiniCB, bool IsNowait) {
8085
8086 if (!updateToLocation(Loc))
8087 return Loc.IP;
8088
8089 // All threads execute the scope body — no conditional entry.
8090 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8091 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8092 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8093 /*IsCancellable=*/false);
8094 if (!AfterIP)
8095 return AfterIP.takeError();
8096
8097 Builder.restoreIP(*AfterIP);
8098 if (!IsNowait) {
8099 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8100 omp::Directive::OMPD_unknown,
8101 /*ForceSimpleCall=*/false,
8102 /*CheckCancelFlag=*/false);
8103 if (!AfterIP)
8104 return AfterIP.takeError();
8105 }
8106 return Builder.saveIP();
8107}
8108
8110 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8111 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8112
8113 if (!updateToLocation(Loc))
8114 return Loc.IP;
8115
8116 Directive OMPD = Directive::OMPD_critical;
8117 uint32_t SrcLocStrSize;
8118 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8119 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8120 Value *ThreadId = getOrCreateThreadID(Ident);
8121 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8122 Value *Args[] = {Ident, ThreadId, LockVar};
8123
8124 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8125 Function *RTFn = nullptr;
8126 if (HintInst) {
8127 // Add Hint to entry Args and create call
8128 EnterArgs.push_back(HintInst);
8129 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8130 } else {
8131 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8132 }
8133 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8134
8135 Function *ExitRTLFn =
8136 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8137 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8138
8139 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8140 /*Conditional*/ false, /*hasFinalize*/ true);
8141}
8142
8145 InsertPointTy AllocaIP, unsigned NumLoops,
8146 ArrayRef<llvm::Value *> StoreValues,
8147 const Twine &Name, bool IsDependSource) {
8148 assert(
8149 llvm::all_of(StoreValues,
8150 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8151 "OpenMP runtime requires depend vec with i64 type");
8152
8153 if (!updateToLocation(Loc))
8154 return Loc.IP;
8155
8156 // Allocate space for vector and generate alloc instruction.
8157 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8158 Builder.restoreIP(AllocaIP);
8159 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8160 ArgsBase->setAlignment(Align(8));
8162
8163 // Store the index value with offset in depend vector.
8164 for (unsigned I = 0; I < NumLoops; ++I) {
8165 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8166 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8167 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8168 STInst->setAlignment(Align(8));
8169 }
8170
8171 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8172 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8173
8174 uint32_t SrcLocStrSize;
8175 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8176 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8177 Value *ThreadId = getOrCreateThreadID(Ident);
8178 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8179
8180 Function *RTLFn = nullptr;
8181 if (IsDependSource)
8182 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8183 else
8184 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8185 createRuntimeFunctionCall(RTLFn, Args);
8186
8187 return Builder.saveIP();
8188}
8189
8191 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8192 FinalizeCallbackTy FiniCB, bool IsThreads) {
8193 if (!updateToLocation(Loc))
8194 return Loc.IP;
8195
8196 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8197 Instruction *EntryCall = nullptr;
8198 Instruction *ExitCall = nullptr;
8199
8200 if (IsThreads) {
8201 uint32_t SrcLocStrSize;
8202 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8203 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8204 Value *ThreadId = getOrCreateThreadID(Ident);
8205 Value *Args[] = {Ident, ThreadId};
8206
8207 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8208 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8209
8210 Function *ExitRTLFn =
8211 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8212 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8213 }
8214
8215 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8216 /*Conditional*/ false, /*hasFinalize*/ true);
8217}
8218
8219OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8220 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8221 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8222 bool HasFinalize, bool IsCancellable) {
8223
8224 if (HasFinalize)
8225 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8226
8227 // Create inlined region's entry and body blocks, in preparation
8228 // for conditional creation
8229 BasicBlock *EntryBB = Builder.GetInsertBlock();
8230 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8232 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8233 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8234 BasicBlock *FiniBB =
8235 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8236
8237 Builder.SetInsertPoint(EntryBB->getTerminator());
8238 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8239
8240 // generate body
8241 if (Error Err =
8242 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8243 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8244 return Err;
8245
8246 // emit exit call and do any needed finalization.
8247 auto FinIP = FiniBB->getFirstInsertionPt();
8248 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8249 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8250 "Unexpected control flow graph state!!");
8251 InsertPointOrErrorTy AfterIP =
8252 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8253 if (!AfterIP)
8254 return AfterIP.takeError();
8255
8256 // If we are skipping the region of a non conditional, remove the exit
8257 // block, and clear the builder's insertion point.
8258 assert(SplitPos->getParent() == ExitBB &&
8259 "Unexpected Insertion point location!");
8260 auto merged = MergeBlockIntoPredecessor(ExitBB);
8261 BasicBlock *ExitPredBB = SplitPos->getParent();
8262 auto InsertBB = merged ? ExitPredBB : ExitBB;
8264 SplitPos->eraseFromParent();
8265 Builder.SetInsertPoint(InsertBB);
8266
8267 return Builder.saveIP();
8268}
8269
8270OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8271 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8272 // if nothing to do, Return current insertion point.
8273 if (!Conditional || !EntryCall)
8274 return Builder.saveIP();
8275
8276 BasicBlock *EntryBB = Builder.GetInsertBlock();
8277 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8278 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8279 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8280
8281 // Emit thenBB and set the Builder's insertion point there for
8282 // body generation next. Place the block after the current block.
8283 Function *CurFn = EntryBB->getParent();
8284 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8285
8286 // Move Entry branch to end of ThenBB, and replace with conditional
8287 // branch (If-stmt)
8288 Instruction *EntryBBTI = EntryBB->getTerminator();
8289 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8290 EntryBBTI->removeFromParent();
8291 Builder.SetInsertPoint(UI);
8292 Builder.Insert(EntryBBTI);
8293 UI->eraseFromParent();
8294 Builder.SetInsertPoint(ThenBB->getTerminator());
8295
8296 // return an insertion point to ExitBB.
8297 return ExitBB->getFirstInsertionPt();
8298}
8299
8300OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8301 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8302 bool HasFinalize) {
8303
8304 Builder.restoreIP(FinIP);
8305
8306 // If there is finalization to do, emit it before the exit call
8307 if (HasFinalize) {
8308 assert(!FinalizationStack.empty() &&
8309 "Unexpected finalization stack state!");
8310
8311 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8312 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8313
8314 BasicBlock *FinBB = FinIP.getNodeParent();
8315 if (Error Err = Fi.mergeFiniBB(Builder, FinBB))
8316 return std::move(Err);
8317
8318 // Exit condition: insertion point is before the terminator of the new Fini
8319 // block
8320 Builder.SetInsertPoint(FinBB->getTerminator());
8321 }
8322
8323 if (!ExitCall)
8324 return Builder.saveIP();
8325
8326 // place the Exitcall as last instruction before Finalization block terminator
8327 ExitCall->removeFromParent();
8328 Builder.Insert(ExitCall);
8329
8330 return ExitCall->getIterator();
8331}
8332
8334 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8335 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8336 if (!IP.isValid())
8337 return IP;
8338
8340
8341 // creates the following CFG structure
8342 // OMP_Entry : (MasterAddr != PrivateAddr)?
8343 // F T
8344 // | \
8345 // | copin.not.master
8346 // | /
8347 // v /
8348 // copyin.not.master.end
8349 // |
8350 // v
8351 // OMP.Entry.Next
8352
8353 BasicBlock *OMP_Entry = IP.getNodeParent();
8354 Function *CurFn = OMP_Entry->getParent();
8355 BasicBlock *CopyBegin =
8356 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8357 BasicBlock *CopyEnd = nullptr;
8358
8359 // If entry block is terminated, split to preserve the branch to following
8360 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8362 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8363 "copyin.not.master.end");
8364 OMP_Entry->getTerminator()->eraseFromParent();
8365 } else {
8366 CopyEnd =
8367 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8368 }
8369
8370 Builder.SetInsertPoint(OMP_Entry);
8371 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8372 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8373 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8374 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8375
8376 Builder.SetInsertPoint(CopyBegin);
8377 if (BranchtoEnd)
8378 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8379
8380 return Builder.saveIP();
8381}
8382
8384 Value *Size, Value *Allocator,
8385 std::string Name) {
8387 if (!updateToLocation(Loc))
8388 return nullptr;
8389
8390 uint32_t SrcLocStrSize;
8391 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8392 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8393 Value *ThreadId = getOrCreateThreadID(Ident);
8394 Value *Args[] = {ThreadId, Size, Allocator};
8395
8396 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8397
8398 return createRuntimeFunctionCall(Fn, Args, Name);
8399}
8400
8402 Value *Align, Value *Size,
8403 Value *Allocator,
8404 std::string Name) {
8406 if (!updateToLocation(Loc))
8407 return nullptr;
8408
8409 uint32_t SrcLocStrSize;
8410 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8411 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8412 Value *ThreadId = getOrCreateThreadID(Ident);
8413 Value *Args[] = {ThreadId, Align, Size, Allocator};
8414
8415 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8416
8417 return Builder.CreateCall(Fn, Args, Name);
8418}
8419
8421 Value *Addr, Value *Allocator,
8422 std::string Name) {
8424 if (!updateToLocation(Loc))
8425 return nullptr;
8426
8427 uint32_t SrcLocStrSize;
8428 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8429 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8430 Value *ThreadId = getOrCreateThreadID(Ident);
8431 Value *Args[] = {ThreadId, Addr, Allocator};
8432 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8433 return createRuntimeFunctionCall(Fn, Args, Name);
8434}
8435
8437 Value *Size,
8438 const Twine &Name) {
8441
8442 Value *Args[] = {Size};
8443 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8444 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8446 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8447 return Call;
8448}
8449
8451 Type *VarType,
8452 const Twine &Name) {
8453 return createOMPAllocShared(
8454 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8455}
8456
8458 Value *Addr, Value *Size,
8459 const Twine &Name) {
8462
8463 Value *Args[] = {Addr, Size};
8464 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8465 return Builder.CreateCall(Fn, Args, Name);
8466}
8467
8469 Value *Addr, Type *VarType,
8470 const Twine &Name) {
8471 return createOMPFreeShared(
8472 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8473 Name);
8474}
8475
8477 const LocationDescription &Loc, Value *InteropVar,
8479 Value *DependenceAddress, bool HaveNowaitClause) {
8482
8483 uint32_t SrcLocStrSize;
8484 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8485 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8486 Value *ThreadId = getOrCreateThreadID(Ident);
8487 if (Device == nullptr)
8489 else if (Device->getType() != Int32)
8490 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8491 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
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, InteropTypeVal,
8500 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8501
8502 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8503
8504 return createRuntimeFunctionCall(Fn, Args);
8505}
8506
8508 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8509 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8512
8513 uint32_t SrcLocStrSize;
8514 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8515 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8516 Value *ThreadId = getOrCreateThreadID(Ident);
8517 if (Device == nullptr)
8519 else if (Device->getType() != Int32)
8520 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8521 if (NumDependences == nullptr) {
8522 NumDependences = ConstantInt::get(Int32, 0);
8523 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8524 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8525 }
8526 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8527 Value *Args[] = {
8528 Ident, ThreadId, InteropVar, Device,
8529 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8530
8531 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8532
8533 return createRuntimeFunctionCall(Fn, Args);
8534}
8535
8537 Value *InteropVar, Value *Device,
8538 Value *NumDependences,
8539 Value *DependenceAddress,
8540 bool HaveNowaitClause) {
8543 uint32_t SrcLocStrSize;
8544 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8545 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8546 Value *ThreadId = getOrCreateThreadID(Ident);
8547 if (Device == nullptr)
8549 else if (Device->getType() != Int32)
8550 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8551 if (NumDependences == nullptr) {
8552 NumDependences = ConstantInt::get(Int32, 0);
8553 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8554 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8555 }
8556 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8557 Value *Args[] = {
8558 Ident, ThreadId, InteropVar, Device,
8559 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8560
8561 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8562
8563 return createRuntimeFunctionCall(Fn, Args);
8564}
8565
8568 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8571
8572 uint32_t SrcLocStrSize;
8573 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8574 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8575 Value *ThreadId = getOrCreateThreadID(Ident);
8576 Constant *ThreadPrivateCache =
8577 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8578 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8579
8580 Function *Fn =
8581 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8582
8583 return createRuntimeFunctionCall(Fn, Args);
8584}
8585
8587 const LocationDescription &Loc,
8589 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8590 "expected num_threads and num_teams to be specified");
8591
8592 if (!updateToLocation(Loc))
8593 return nullptr;
8594
8595 uint32_t SrcLocStrSize;
8596 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8597 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8598 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8599 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8600 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8601 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8602 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8603 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8604
8605 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8606 Function *Kernel = DebugKernelWrapper;
8607
8608 // We need to strip the debug prefix to get the correct kernel name.
8609 StringRef KernelName = Kernel->getName();
8610 const std::string DebugPrefix = "_debug__";
8611 if (KernelName.ends_with(DebugPrefix)) {
8612 KernelName = KernelName.drop_back(DebugPrefix.length());
8613 Kernel = M.getFunction(KernelName);
8614 assert(Kernel && "Expected the real kernel to exist");
8615 }
8616
8617 // Manifest the launch configuration in the metadata matching the kernel
8618 // environment.
8619 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8620 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8621 Attrs.MaxTeams.front());
8622
8623 // Don't derive or write thread bounds for Bare kernels.
8624 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8625 if (Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_BARE) {
8626 // If MaxThreads is not set and needs adjustment, select the maximum
8627 // between the default workgroup size and the MinThreads value. This is
8628 // only meaningful for targets with a known grid value (i.e. GPUs); for
8629 // other targets (e.g. host kernels) leave it unset so the runtime falls
8630 // back to its own device-specific default.
8631 if (MaxThreadsVal < 0 && UseDefaultMaxThreads && hasGridValue(T))
8632 MaxThreadsVal =
8633 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8634 Attrs.MinThreads.front());
8635
8636 // Generic mode runs the main thread on a warp of its own, past
8637 // thread_limit. Reserve the widest warp any target has. Not on SPIR-V,
8638 // causes problems with Level Zero.
8639 if (MaxThreadsVal > 0 &&
8640 Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC && hasGridValue(T) &&
8641 !T.isSPIRV())
8642 MaxThreadsVal = int32_t(
8643 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8644 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8645
8646 if (MaxThreadsVal > 0)
8647 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8648 MaxThreadsVal);
8649 }
8650
8651 Constant *MinThreads =
8652 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8653 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8654 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8655 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8656 Constant *ReductionDataSize =
8657 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8658
8659 const DataLayout &DL = M.getDataLayout();
8660
8661 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8662 Constant *DynamicEnvironmentInitializer =
8663 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8664 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8665 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8666 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8667 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8668 DL.getDefaultGlobalsAddressSpace());
8669 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8670
8671 Constant *DynamicEnvironment =
8672 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8673 ? DynamicEnvironmentGV
8674 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8675 DynamicEnvironmentPtr);
8676
8677 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8678 ConfigurationEnvironment, {
8679 UseGenericStateMachineVal,
8680 MayUseNestedParallelismVal,
8681 IsSPMDVal,
8682 MinThreads,
8683 MaxThreads,
8684 MinTeams,
8685 MaxTeams,
8686 ReductionDataSize,
8687 });
8688 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8689 KernelEnvironment, {
8690 ConfigurationEnvironmentInitializer,
8691 Ident,
8692 DynamicEnvironment,
8693 });
8694 std::string KernelEnvironmentName =
8695 (KernelName + "_kernel_environment").str();
8696 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8697 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8698 KernelEnvironmentInitializer, KernelEnvironmentName,
8699 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8700 DL.getDefaultGlobalsAddressSpace());
8701 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8702
8703 return KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8704 ? KernelEnvironmentGV
8705 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8706 KernelEnvironmentPtr);
8707}
8708
8710 const LocationDescription &Loc,
8712 Constant *KernelEnvironment = emitKernelEnvironment(Loc, Attrs);
8713 if (!KernelEnvironment)
8714 return Loc.IP;
8715
8716 if (!updateToLocation(Loc))
8717 return Loc.IP;
8718
8719 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8721 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8722
8723 Value *KernelLaunchEnvironment =
8724 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8725 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8726 KernelLaunchEnvironment =
8727 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8728 ? KernelLaunchEnvironment
8729 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8730 KernelLaunchEnvParamTy);
8731 CallInst *ThreadKind = createRuntimeFunctionCall(
8732 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8733
8734 Value *ExecUserCode = Builder.CreateICmpEQ(
8735 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8736 "exec_user_code");
8737
8738 // ThreadKind = __kmpc_target_init(...)
8739 // if (ThreadKind == -1)
8740 // user_code
8741 // else
8742 // return;
8743
8744 auto *UI = Builder.CreateUnreachable();
8745 BasicBlock *CheckBB = UI->getParent();
8746 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8747
8748 BasicBlock *WorkerExitBB = BasicBlock::Create(
8749 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8750 Builder.SetInsertPoint(WorkerExitBB);
8751 Builder.CreateRetVoid();
8752
8753 auto *CheckBBTI = CheckBB->getTerminator();
8754 Builder.SetInsertPoint(CheckBBTI);
8755 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8756
8757 CheckBBTI->eraseFromParent();
8758 UI->eraseFromParent();
8759
8760 // Continue in the "user_code" block, see diagram above and in
8761 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8762 return UserCodeEntryBB->getFirstInsertionPt();
8763}
8764
8766 int32_t TeamsReductionDataSize) {
8767 if (!updateToLocation(Loc))
8768 return;
8769
8771 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8772
8774
8775 if (!TeamsReductionDataSize)
8776 return;
8777
8778 Function *Kernel = Builder.GetInsertBlock()->getParent();
8779 // We need to strip the debug prefix to get the correct kernel name.
8780 StringRef KernelName = Kernel->getName();
8781 const std::string DebugPrefix = "_debug__";
8782 if (KernelName.ends_with(DebugPrefix))
8783 KernelName = KernelName.drop_back(DebugPrefix.length());
8784 auto *KernelEnvironmentGV =
8785 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8786 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8787 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8788 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8789 KernelEnvironmentInitializer,
8790 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8791 KernelEnvironmentGV->setInitializer(NewInitializer);
8792}
8793
8794static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8795 bool Min) {
8796 if (Kernel.hasFnAttribute(Name)) {
8797 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8798 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8799 }
8800 Kernel.addFnAttr(Name, llvm::utostr(Value));
8801}
8802
8803std::pair<int32_t, int32_t>
8805 int32_t ThreadLimit =
8806 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8807
8808 if (T.isAMDGPU()) {
8809 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8810 if (!Attr.isValid() || !Attr.isStringAttribute())
8811 return {0, ThreadLimit};
8812 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8813 int32_t LB, UB;
8814 if (!llvm::to_integer(UBStr, UB, 10))
8815 return {0, ThreadLimit};
8816 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8817 if (!llvm::to_integer(LBStr, LB, 10))
8818 return {0, UB};
8819 return {LB, UB};
8820 }
8821
8822 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8823 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8824 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8825 }
8826 return {0, ThreadLimit};
8827}
8828
8830 Function &Kernel, int32_t LB,
8831 int32_t UB) {
8832 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8833
8834 if (T.isAMDGPU()) {
8835 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8836 llvm::utostr(LB) + "," + llvm::utostr(UB));
8837 return;
8838 }
8839
8841}
8842
8843std::pair<int32_t, int32_t>
8845 // TODO: Read from backend annotations if available.
8846 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8847}
8848
8850 int32_t LB, int32_t UB) {
8851 if (UB > 0) {
8852 if (T.isNVPTX())
8854 if (T.isAMDGPU())
8855 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8856 }
8857
8858 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8859}
8860
8861void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8862 Function *OutlinedFn) {
8863 if (Config.isTargetDevice()) {
8865 // TODO: Determine if DSO local can be set to true.
8866 OutlinedFn->setDSOLocal(false);
8868 if (T.isAMDGCN())
8870 else if (T.isNVPTX())
8872 else if (T.isSPIRV())
8874 }
8875}
8876
8877Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8878 StringRef EntryFnIDName) {
8879 if (Config.isTargetDevice()) {
8880 assert(OutlinedFn && "The outlined function must exist if embedded");
8881 return OutlinedFn;
8882 }
8883
8884 return new GlobalVariable(
8885 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8886 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8887}
8888
8889Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8890 StringRef EntryFnName) {
8891 if (OutlinedFn)
8892 return OutlinedFn;
8893
8894 assert(!M.getGlobalVariable(EntryFnName, true) &&
8895 "Named kernel already exists?");
8896 return new GlobalVariable(
8897 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8898 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8899}
8900
8902 TargetRegionEntryInfo &EntryInfo,
8903 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8904 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8905
8906 SmallString<64> EntryFnName;
8907 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8908
8909 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8910 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8911 if (!CBResult)
8912 return CBResult.takeError();
8913 OutlinedFn = *CBResult;
8914 } else {
8915 OutlinedFn = nullptr;
8916 }
8917
8918 // If this target outline function is not an offload entry, we don't need to
8919 // register it. This may be in the case of a false if clause, or if there are
8920 // no OpenMP targets.
8921 if (!IsOffloadEntry)
8922 return Error::success();
8923
8924 std::string EntryFnIDName =
8925 Config.isTargetDevice()
8926 ? std::string(EntryFnName)
8927 : createPlatformSpecificName({EntryFnName, "region_id"});
8928
8929 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8930 EntryFnName, EntryFnIDName);
8931 return Error::success();
8932}
8933
8935 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8936 StringRef EntryFnName, StringRef EntryFnIDName) {
8937 if (OutlinedFn)
8938 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8939 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8940 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8941 OffloadInfoManager.registerTargetRegionEntryInfo(
8942 EntryInfo, EntryAddr, OutlinedFnID,
8944 return OutlinedFnID;
8945}
8946
8948 const LocationDescription &Loc, InsertPointTy AllocaIP,
8949 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8950 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8951 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8952 omp::RuntimeFunction *MapperFunc,
8954 BodyGenTy BodyGenType)>
8955 BodyGenCB,
8956 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8957 if (!updateToLocation(Loc))
8958 return InsertPointTy();
8959
8960 Builder.restoreIP(CodeGenIP);
8961
8962 bool IsStandAlone = !BodyGenCB;
8963 MapInfosTy *MapInfo;
8964 // Generate the code for the opening of the data environment. Capture all the
8965 // arguments of the runtime call by reference because they are used in the
8966 // closing of the region.
8967 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8968 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8969 MapInfo = &GenMapInfoCB(Builder.saveIP());
8970 if (Error Err = emitOffloadingArrays(
8971 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8972 /*IsNonContiguous=*/true, DeviceAddrCB))
8973 return Err;
8974
8975 TargetDataRTArgs RTArgs;
8977
8978 // Emit the number of elements in the offloading arrays.
8979 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8980
8981 // Source location for the ident struct
8982 if (!SrcLocInfo) {
8983 uint32_t SrcLocStrSize;
8984 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8985 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8986 }
8987
8988 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8989 SrcLocInfo, DeviceID,
8990 PointerNum, RTArgs.BasePointersArray,
8991 RTArgs.PointersArray, RTArgs.SizesArray,
8992 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8993 RTArgs.MappersArray};
8994
8995 if (IsStandAlone) {
8996 assert(MapperFunc && "MapperFunc missing for standalone target data");
8997
8998 auto TaskBodyCB = [&](Value *, Value *,
9000 if (Info.HasNoWait) {
9001 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
9005 }
9006
9008 OffloadingArgs);
9009
9010 if (Info.HasNoWait) {
9011 BasicBlock *OffloadContBlock =
9012 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
9013 Function *CurFn = Builder.GetInsertBlock()->getParent();
9014 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
9015 Builder.restoreIP(Builder.saveIP());
9016 }
9017 return Error::success();
9018 };
9019
9020 bool RequiresOuterTargetTask = Info.HasNoWait;
9021 if (!RequiresOuterTargetTask)
9022 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
9023 /*TargetTaskAllocaIP=*/{}));
9024 else
9025 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
9026 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
9027 } else {
9028 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
9029 omp::OMPRTL___tgt_target_data_begin_mapper);
9030
9031 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
9032
9033 for (auto DeviceMap : Info.DevicePtrInfoMap) {
9034 if (isa<AllocaInst>(DeviceMap.second.second)) {
9035 auto *LI =
9036 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
9037 Builder.CreateStore(LI, DeviceMap.second.second);
9038 }
9039 }
9040
9041 // If device pointer privatization is required, emit the body of the
9042 // region here. It will have to be duplicated: with and without
9043 // privatization.
9044 InsertPointOrErrorTy AfterIP =
9045 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9046 if (!AfterIP)
9047 return AfterIP.takeError();
9048 Builder.restoreIP(*AfterIP);
9049 }
9050 return Error::success();
9051 };
9052
9053 // If we need device pointer privatization, we need to emit the body of the
9054 // region with no privatization in the 'else' branch of the conditional.
9055 // Otherwise, we don't have to do anything.
9056 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9057 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9058 InsertPointOrErrorTy AfterIP =
9059 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9060 if (!AfterIP)
9061 return AfterIP.takeError();
9062 Builder.restoreIP(*AfterIP);
9063 return Error::success();
9064 };
9065
9066 // Generate code for the closing of the data region.
9067 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9068 ArrayRef<BasicBlock *> DeallocBlocks) {
9069 TargetDataRTArgs RTArgs;
9070 Info.EmitDebug = !MapInfo->Names.empty();
9071 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9072
9073 // Emit the number of elements in the offloading arrays.
9074 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9075
9076 // Source location for the ident struct
9077 if (!SrcLocInfo) {
9078 uint32_t SrcLocStrSize;
9079 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9080 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9081 }
9082
9083 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9084 PointerNum, RTArgs.BasePointersArray,
9085 RTArgs.PointersArray, RTArgs.SizesArray,
9086 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9087 RTArgs.MappersArray};
9088 Function *EndMapperFunc =
9089 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9090
9091 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9092 return Error::success();
9093 };
9094
9095 // We don't have to do anything to close the region if the if clause evaluates
9096 // to false.
9097 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9098 ArrayRef<BasicBlock *> DeallocBlocks) {
9099 return Error::success();
9100 };
9101
9102 Error Err = [&]() -> Error {
9103 if (BodyGenCB) {
9104 Error Err = [&]() {
9105 if (IfCond)
9106 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9107 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9108 }();
9109
9110 if (Err)
9111 return Err;
9112
9113 // If we don't require privatization of device pointers, we emit the body
9114 // in between the runtime calls. This avoids duplicating the body code.
9115 InsertPointOrErrorTy AfterIP =
9116 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9117 if (!AfterIP)
9118 return AfterIP.takeError();
9119 restoreIPandDebugLoc(Builder, *AfterIP);
9120
9121 if (IfCond)
9122 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9123 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9124 }
9125 if (IfCond)
9126 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9127 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9128 }();
9129
9130 if (Err)
9131 return Err;
9132
9133 return Builder.saveIP();
9134}
9135
9138 bool IsGPUDistribute) {
9139 assert((IVSize == 32 || IVSize == 64) &&
9140 "IV size is not compatible with the omp runtime");
9141 RuntimeFunction Name;
9142 if (IsGPUDistribute)
9143 Name = IVSize == 32
9144 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9145 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9146 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9147 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9148 else
9149 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9150 : omp::OMPRTL___kmpc_for_static_init_4u)
9151 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9152 : omp::OMPRTL___kmpc_for_static_init_8u);
9153
9154 return getOrCreateRuntimeFunction(M, Name);
9155}
9156
9158 bool IVSigned) {
9159 assert((IVSize == 32 || IVSize == 64) &&
9160 "IV size is not compatible with the omp runtime");
9161 RuntimeFunction Name = IVSize == 32
9162 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9163 : omp::OMPRTL___kmpc_dispatch_init_4u)
9164 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9165 : omp::OMPRTL___kmpc_dispatch_init_8u);
9166
9167 return getOrCreateRuntimeFunction(M, Name);
9168}
9169
9171 bool IVSigned) {
9172 assert((IVSize == 32 || IVSize == 64) &&
9173 "IV size is not compatible with the omp runtime");
9174 RuntimeFunction Name = IVSize == 32
9175 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9176 : omp::OMPRTL___kmpc_dispatch_next_4u)
9177 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9178 : omp::OMPRTL___kmpc_dispatch_next_8u);
9179
9180 return getOrCreateRuntimeFunction(M, Name);
9181}
9182
9184 bool IVSigned) {
9185 assert((IVSize == 32 || IVSize == 64) &&
9186 "IV size is not compatible with the omp runtime");
9187 RuntimeFunction Name = IVSize == 32
9188 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9189 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9190 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9191 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9192
9193 return getOrCreateRuntimeFunction(M, Name);
9194}
9195
9197 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9198}
9199
9201 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9202 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9203
9204 DISubprogram *NewSP = Func->getSubprogram();
9205 if (!NewSP)
9206 return;
9207
9209
9210 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9211 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9212 // Only use cached variable if the arg number matches. This is important
9213 // so that DIVariable created for privatized variables are not discarded.
9214 if (NewVar && (arg == NewVar->getArg()))
9215 return NewVar;
9216
9218 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9219 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9220 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9221 return NewVar;
9222 };
9223
9224 auto UpdateDebugRecord = [&](auto *DR) {
9225 DILocalVariable *OldVar = DR->getVariable();
9226 unsigned ArgNo = 0;
9227 for (auto Loc : DR->location_ops()) {
9228 auto Iter = ValueReplacementMap.find(Loc);
9229 if (Iter != ValueReplacementMap.end()) {
9230 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9231 ArgNo = std::get<1>(Iter->second) + 1;
9232 }
9233 }
9234 if (ArgNo != 0)
9235 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9236 };
9237
9239 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9240 if (DVR->getNumVariableLocationOps() != 1u) {
9241 DVR->setKillLocation();
9242 return;
9243 }
9244 Value *Loc = DVR->getVariableLocationOp(0u);
9245 BasicBlock *CurBB = DVR->getParent();
9246 BasicBlock *RequiredBB = nullptr;
9247
9248 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9249 RequiredBB = LocInst->getParent();
9250 else if (isa<llvm::Argument>(Loc))
9251 RequiredBB = &DVR->getFunction()->getEntryBlock();
9252
9253 if (RequiredBB && RequiredBB != CurBB) {
9254 assert(!RequiredBB->empty());
9255 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9256 RequiredBB->back().getIterator());
9257 DVRsToDelete.push_back(DVR);
9258 }
9259 };
9260
9261 // The location and scope of variable records still point to
9262 // the parent function of the target region. Update them.
9263 for (Instruction &I : instructions(Func)) {
9264 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9265 UpdateDebugRecord(&DVR);
9266 MoveDebugRecordToCorrectBlock(&DVR);
9267 }
9268 }
9269 for (auto *DVR : DVRsToDelete)
9270 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9271 // An extra argument is passed to the device. Create the debug data for it.
9272 if (OMPBuilder.Config.isTargetDevice()) {
9273 DICompileUnit *CU = NewSP->getUnit();
9274 Module *M = Func->getParent();
9275 DIBuilder DB(*M, true, CU);
9276 DIType *VoidPtrTy =
9277 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9278 unsigned ArgNo = Func->arg_size();
9279 DILocalVariable *Var = DB.createParameterVariable(
9280 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9281 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9282 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9283 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9284 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9285 &(*Func->begin()));
9286 }
9287}
9288
9290 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9291 return cast<Operator>(V)->getOperand(0);
9292 return V;
9293}
9294
9296 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9298 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9301 DebugLoc OutlinedFnLoc) {
9302 SmallVector<Type *> ParameterTypes;
9303 if (OMPBuilder.Config.isTargetDevice()) {
9304 // All parameters to target devices are passed as pointers
9305 // or i64. This assumes 64-bit address spaces/pointers.
9306 for (auto &Arg : Inputs)
9307 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9308 ? Arg->getType()
9309 : Type::getInt64Ty(Builder.getContext()));
9310 } else {
9311 for (auto &Arg : Inputs)
9312 ParameterTypes.push_back(Arg->getType());
9313 }
9314
9315 // The implicit dyn_ptr argument is always the last parameter on both host
9316 // and device so the argument counts match without runtime manipulation.
9317 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9318 ParameterTypes.push_back(PtrTy);
9319
9320 auto BB = Builder.GetInsertBlock();
9321 auto M = BB->getModule();
9322 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9323 /*isVarArg*/ false);
9324 auto Func =
9325 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9326
9327 // Forward target-cpu and target-features function attributes from the
9328 // original function to the new outlined function.
9329 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9330
9331 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9332 if (TargetCpuAttr.isStringAttribute())
9333 Func->addFnAttr(TargetCpuAttr);
9334
9335 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9336 if (TargetFeaturesAttr.isStringAttribute())
9337 Func->addFnAttr(TargetFeaturesAttr);
9338
9339 if (OMPBuilder.Config.isTargetDevice()) {
9340 Value *ExecMode =
9341 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9342 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9343 }
9344
9345 // Save insert point.
9346 IRBuilder<>::InsertPointGuard IPG(Builder);
9347 // We will generate the entries in the outlined function but the debug
9348 // location is still pointing to the parent function, which is the wrong
9349 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9350 // position scoped to the subprogram that will be attached to the outlined
9351 // function, so it is what everything emitted below needs.
9352 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9353
9354 // Generate the region into the function.
9355 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9356 Builder.SetInsertPoint(EntryBB);
9357
9358 // Insert target init call in the device compilation pass. On the host
9359 // (e.g. a non-GPU offload target), there is no runtime init/deinit
9360 // sequence, but the runtime still needs a '<kernel>_kernel_environment'
9361 // global to know how the kernel was configured, so emit it directly.
9362 if (OMPBuilder.Config.isTargetDevice())
9363 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9364 else
9365 OMPBuilder.emitKernelEnvironment(Builder, DefaultAttrs);
9366
9367 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9368
9369 // As we embed the user code in the middle of our target region after we
9370 // generate entry code, we must move what allocas we can into the entry
9371 // block to avoid possible breaking optimisations for device
9372 if (OMPBuilder.Config.isTargetDevice())
9374
9375 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9376 BasicBlock *OutlinedBodyBB =
9377 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9379 CBFunc(Builder.saveIP(), OutlinedBodyBB->begin(), ExitBB);
9380 if (!AfterIP)
9381 return AfterIP.takeError();
9382 Builder.SetInsertPoint(ExitBB);
9383 // The body callback builds the body with its own IRBuilder and cannot reach
9384 // this one directly. But a body holding another OpenMP construct, a nested
9385 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9386 // this Builder pointing at the wrong debug location, or at none at all. The
9387 // epilogue below belongs to the target construct rather than to whatever the
9388 // body emitted last, so re-establish the location the prologue was emitted
9389 // with.
9390 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9391
9392 // Insert target deinit call in the device compilation pass.
9393 if (OMPBuilder.Config.isTargetDevice())
9394 OMPBuilder.createTargetDeinit(Builder);
9395
9396 // Insert return instruction.
9397 Builder.CreateRetVoid();
9398
9399 // New Alloca IP at entry point of created device function.
9400 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9401 auto AllocaIP = Builder.saveIP();
9402
9403 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9404
9405 // Do not include the artificial dyn_ptr argument.
9406 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9407
9409
9410 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9411 // Things like GEP's can come in the form of Constants. Constants and
9412 // ConstantExpr's do not have access to the knowledge of what they're
9413 // contained in, so we must dig a little to find an instruction so we
9414 // can tell if they're used inside of the function we're outlining. We
9415 // also replace the original constant expression with a new instruction
9416 // equivalent; an instruction as it allows easy modification in the
9417 // following loop, as we can now know the constant (instruction) is
9418 // owned by our target function and replaceUsesOfWith can now be invoked
9419 // on it (cannot do this with constants it seems). A brand new one also
9420 // allows us to be cautious as it is perhaps possible the old expression
9421 // was used inside of the function but exists and is used externally
9422 // (unlikely by the nature of a Constant, but still).
9423 // NOTE: We cannot remove dead constants that have been rewritten to
9424 // instructions at this stage, we run the risk of breaking later lowering
9425 // by doing so as we could still be in the process of lowering the module
9426 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9427 // constants we have created rewritten versions of.
9428 if (auto *Const = dyn_cast<Constant>(Input))
9429 convertUsersOfConstantsToInstructions(Const, Func, false);
9430
9431 // Collect users before iterating over them to avoid invalidating the
9432 // iteration in case a user uses Input more than once (e.g. a call
9433 // instruction).
9434 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9435 // Collect all the instructions
9437 if (auto *Instr = dyn_cast<Instruction>(User))
9438 if (Instr->getFunction() == Func)
9439 Instr->replaceUsesOfWith(Input, InputCopy);
9440 };
9441
9442 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9443
9444 // Rewrite uses of input valus to parameters.
9445 for (auto InArg : zip(Inputs, ArgRange)) {
9446 Value *Input = std::get<0>(InArg);
9447 Argument &Arg = std::get<1>(InArg);
9448 Value *InputCopy = nullptr;
9449
9450 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9451 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(), ExitBB->begin());
9452 if (!AfterIP)
9453 return AfterIP.takeError();
9454 Builder.restoreIP(*AfterIP);
9455 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9456
9457 // In certain cases a Global may be set up for replacement, however, this
9458 // Global may be used in multiple arguments to the kernel, just segmented
9459 // apart, for example, if we have a global array, that is sectioned into
9460 // multiple mappings (technically not legal in OpenMP, but there is a case
9461 // in Fortran for Common Blocks where this is neccesary), we will end up
9462 // with GEP's into this array inside the kernel, that refer to the Global
9463 // but are technically separate arguments to the kernel for all intents and
9464 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9465 // index, it will fold into an referal to the Global, if we then encounter
9466 // this folded GEP during replacement all of the references to the
9467 // Global in the kernel will be replaced with the argument we have generated
9468 // that corresponds to it, including any other GEP's that refer to the
9469 // Global that may be other arguments. This will invalidate all of the other
9470 // preceding mapped arguments that refer to the same global that may be
9471 // separate segments. To prevent this, we defer global processing until all
9472 // other processing has been performed.
9475 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9476 continue;
9477 }
9478
9480 continue;
9481
9482 ReplaceValue(Input, InputCopy, Func);
9483 }
9484
9485 // Replace all of our deferred Input values, currently just Globals.
9486 for (auto Deferred : DeferredReplacement)
9487 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9488
9489 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9490 ValueReplacementMap);
9491 return Func;
9492}
9493/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9494/// of pointers containing shared data between the parent task and the created
9495/// task.
9497 IRBuilderBase &Builder,
9498 Value *TaskWithPrivates,
9499 Type *TaskWithPrivatesTy) {
9500
9501 Type *TaskTy = OMPIRBuilder.Task;
9502 LLVMContext &Ctx = Builder.getContext();
9503 Value *TaskT =
9504 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9505 Value *Shareds = TaskT;
9506 // TaskWithPrivatesTy can be one of the following
9507 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9508 // %struct.privates }
9509 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9510 //
9511 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9512 // its first member has to be the task descriptor. TaskTy is the type of the
9513 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9514 // first member of TaskT, gives us the pointer to shared data.
9515 if (TaskWithPrivatesTy != TaskTy)
9516 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9517 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9518}
9519/// Create an entry point for a target task with the following.
9520/// It'll have the following signature
9521/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9522/// This function is called from emitTargetTask once the
9523/// code to launch the target kernel has been outlined already.
9524/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9525/// into the task structure so that the deferred target task can access this
9526/// data even after the stack frame of the generating task has been rolled
9527/// back. Offloading arrays contain base pointers, pointers, sizes etc
9528/// of the data that the target kernel will access. These in effect are the
9529/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9531 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9532 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9533 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9534
9535 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9536 // This is because PrivatesTy is the type of the structure in which
9537 // we pass the offloading arrays to the deferred target task.
9538 assert((!NumOffloadingArrays || PrivatesTy) &&
9539 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9540 "to privatize");
9541
9542 Module &M = OMPBuilder.M;
9543 // KernelLaunchFunction is the target launch function, i.e.
9544 // the function that sets up kernel arguments and calls
9545 // __tgt_target_kernel to launch the kernel on the device.
9546 //
9547 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9548
9549 // StaleCI is the CallInst which is the call to the outlined
9550 // target kernel launch function. If there are local live-in values
9551 // that the outlined function uses then these are aggregated into a structure
9552 // which is passed as the second argument. If there are no local live-in
9553 // values or if all values used by the outlined kernel are global variables,
9554 // then there's only one argument, the threadID. So, StaleCI can be
9555 //
9556 // %structArg = alloca { ptr, ptr }, align 8
9557 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9558 // store ptr %20, ptr %gep_, align 8
9559 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9560 // store ptr %21, ptr %gep_8, align 8
9561 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9562 //
9563 // OR
9564 //
9565 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9566 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9567
9568 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9569 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9570 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9571
9572 auto ProxyFnTy =
9573 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9574 /* isVarArg */ false);
9575 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9576 ".omp_target_task_proxy_func", M);
9577 Value *ThreadId = ProxyFn->getArg(0);
9578 Value *TaskWithPrivates = ProxyFn->getArg(1);
9579 ThreadId->setName("thread.id");
9580 TaskWithPrivates->setName("task");
9581
9582 bool HasShareds = SharedArgsOperandNo > 0;
9583 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9584 IRBuilder<>::InsertPointGuard IPG(Builder);
9585 BasicBlock *EntryBB =
9586 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9587 Builder.SetInsertPoint(EntryBB);
9588 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9589
9590 SmallVector<Value *> KernelLaunchArgs;
9591 KernelLaunchArgs.reserve(StaleCI->arg_size());
9592 KernelLaunchArgs.push_back(ThreadId);
9593
9594 if (HasOffloadingArrays) {
9595 assert(TaskTy != TaskWithPrivatesTy &&
9596 "If there are offloading arrays to pass to the target"
9597 "TaskTy cannot be the same as TaskWithPrivatesTy");
9598 (void)TaskTy;
9599 Value *Privates =
9600 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9601 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9602 KernelLaunchArgs.push_back(
9603 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9604 }
9605
9606 if (HasShareds) {
9607 auto *ArgStructAlloca =
9608 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9609 assert(ArgStructAlloca &&
9610 "Unable to find the alloca instruction corresponding to arguments "
9611 "for extracted function");
9612 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9613 std::optional<TypeSize> ArgAllocSize =
9614 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9615 assert(ArgStructType && ArgAllocSize &&
9616 "Unable to determine size of arguments for extracted function");
9617 uint64_t StructSize = ArgAllocSize->getFixedValue();
9618
9619 AllocaInst *NewArgStructAlloca =
9620 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9621
9622 Value *SharedsSize = Builder.getInt64(StructSize);
9623
9625 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9626
9627 Builder.CreateMemCpy(
9628 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9629 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9630 KernelLaunchArgs.push_back(NewArgStructAlloca);
9631 }
9632 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9633 Builder.CreateRetVoid();
9634 return ProxyFn;
9635}
9637
9638 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9639 return GEP->getSourceElementType();
9640 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9641 return Alloca->getAllocatedType();
9642
9643 llvm_unreachable("Unhandled Instruction type");
9644 return nullptr;
9645}
9646// This function returns a struct that has at most two members.
9647// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9648// descriptor. The second member, if needed, is a struct containing arrays
9649// that need to be passed to the offloaded target kernel. For example,
9650// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9651// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9652// respectively, then the types created by this function are
9653//
9654// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9655// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9656// %struct.privates }
9657// %struct.task_with_privates is returned by this function.
9658// If there aren't any offloading arrays to pass to the target kernel,
9659// %struct.kmp_task_ompbuilder_t is returned.
9660static StructType *
9662 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9663
9664 if (OffloadingArraysToPrivatize.empty())
9665 return OMPIRBuilder.Task;
9666
9667 SmallVector<Type *, 4> StructFieldTypes;
9668 for (Value *V : OffloadingArraysToPrivatize) {
9669 assert(V->getType()->isPointerTy() &&
9670 "Expected pointer to array to privatize. Got a non-pointer value "
9671 "instead");
9672 Type *ArrayTy = getOffloadingArrayType(V);
9673 assert(ArrayTy && "ArrayType cannot be nullptr");
9674 StructFieldTypes.push_back(ArrayTy);
9675 }
9676 StructType *PrivatesStructTy =
9677 StructType::create(StructFieldTypes, "struct.privates");
9678 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9679 "struct.task_with_privates");
9680}
9682 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9683 TargetRegionEntryInfo &EntryInfo,
9685 Function *&OutlinedFn, Constant *&OutlinedFnID,
9689 DebugLoc OutlinedFnLoc) {
9690
9691 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9692 [&](StringRef EntryFnName) {
9693 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9694 EntryFnName, Inputs, CBFunc,
9695 ArgAccessorFuncCB, OutlinedFnLoc);
9696 };
9697
9698 return OMPBuilder.emitTargetRegionFunction(
9699 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9700 OutlinedFnID);
9701}
9702
9704 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9706 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9707 bool HasNoWait) {
9708
9709 // The following explains the code-gen scenario for the `target` directive. A
9710 // similar scneario is followed for other device-related directives (e.g.
9711 // `target enter data`) but in similar fashion since we only need to emit task
9712 // that encapsulates the proper runtime call.
9713 //
9714 // When we arrive at this function, the target region itself has been
9715 // outlined into the function OutlinedFn.
9716 // So at ths point, for
9717 // --------------------------------------------------------------
9718 // void user_code_that_offloads(...) {
9719 // omp target depend(..) map(from:a) map(to:b) private(i)
9720 // do i = 1, 10
9721 // a(i) = b(i) + n
9722 // }
9723 //
9724 // --------------------------------------------------------------
9725 //
9726 // we have
9727 //
9728 // --------------------------------------------------------------
9729 //
9730 // void user_code_that_offloads(...) {
9731 // %.offload_baseptrs = alloca [2 x ptr], align 8
9732 // %.offload_ptrs = alloca [2 x ptr], align 8
9733 // %.offload_mappers = alloca [2 x ptr], align 8
9734 // ;; target region has been outlined and now we need to
9735 // ;; offload to it via a target task.
9736 // }
9737 // void outlined_device_function(ptr a, ptr b, ptr n) {
9738 // n = *n_ptr;
9739 // do i = 1, 10
9740 // a(i) = b(i) + n
9741 // }
9742 //
9743 // We have to now do the following
9744 // (i) Make an offloading call to outlined_device_function using the OpenMP
9745 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9746 // emitted by emitKernelLaunch
9747 // (ii) Create a task entry point function that calls kernel_launch_function
9748 // and is the entry point for the target task. See
9749 // '@.omp_target_task_proxy_func in the pseudocode below.
9750 // (iii) Create a task with the task entry point created in (ii)
9751 //
9752 // That is we create the following
9753 // struct task_with_privates {
9754 // struct kmp_task_ompbuilder_t task_struct;
9755 // struct privates {
9756 // [2 x ptr] ; baseptrs
9757 // [2 x ptr] ; ptrs
9758 // [2 x i64] ; sizes
9759 // }
9760 // }
9761 // void user_code_that_offloads(...) {
9762 // %.offload_baseptrs = alloca [2 x ptr], align 8
9763 // %.offload_ptrs = alloca [2 x ptr], align 8
9764 // %.offload_sizes = alloca [2 x i64], align 8
9765 //
9766 // %structArg = alloca { ptr, ptr, ptr }, align 8
9767 // %strucArg[0] = a
9768 // %strucArg[1] = b
9769 // %strucArg[2] = &n
9770 //
9771 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9772 // sizeof(kmp_task_ompbuilder_t),
9773 // sizeof(structArg),
9774 // @.omp_target_task_proxy_func,
9775 // ...)
9776 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9777 // sizeof(structArg))
9778 // memcpy(target_task_with_privates->privates->baseptrs,
9779 // offload_baseptrs, sizeof(offload_baseptrs)
9780 // memcpy(target_task_with_privates->privates->ptrs,
9781 // offload_ptrs, sizeof(offload_ptrs)
9782 // memcpy(target_task_with_privates->privates->sizes,
9783 // offload_sizes, sizeof(offload_sizes)
9784 // dependencies_array = ...
9785 // ;; if nowait not present
9786 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9787 // call @__kmpc_omp_task_begin_if0(...)
9788 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9789 // %target_task_with_privates)
9790 // call @__kmpc_omp_task_complete_if0(...)
9791 // }
9792 //
9793 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9794 // ptr %task) {
9795 // %structArg = alloca {ptr, ptr, ptr}
9796 // %task_ptr = getelementptr(%task, 0, 0)
9797 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9798 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9799 //
9800 // %offloading_arrays = getelementptr(%task, 0, 1)
9801 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9802 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9803 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9804 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9805 // %offload_sizes, %structArg)
9806 // }
9807 //
9808 // We need the proxy function because the signature of the task entry point
9809 // expected by kmpc_omp_task is always the same and will be different from
9810 // that of the kernel_launch function.
9811 //
9812 // kernel_launch_function is generated by emitKernelLaunch and has the
9813 // always_inline attribute. For this example, it'll look like so:
9814 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9815 // %offload_sizes, %structArg) alwaysinline {
9816 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9817 // ; load aggregated data from %structArg
9818 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9819 // ; offload_sizes
9820 // call i32 @__tgt_target_kernel(...,
9821 // outlined_device_function,
9822 // ptr %kernel_args)
9823 // }
9824 // void outlined_device_function(ptr a, ptr b, ptr n) {
9825 // n = *n_ptr;
9826 // do i = 1, 10
9827 // a(i) = b(i) + n
9828 // }
9829 //
9830 BasicBlock *TargetTaskBodyBB =
9831 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9832 BasicBlock *TargetTaskAllocaBB =
9833 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9834
9835 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB->begin());
9836 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB->begin());
9837
9838 auto OI = std::make_unique<OutlineInfo>();
9839 OI->EntryBB = TargetTaskAllocaBB;
9840 OI->OuterAllocBB = AllocaIP.getNodeParent();
9841
9842 // Add the thread ID argument.
9844 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9845 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9846
9847 // Generate the task body which will subsequently be outlined.
9848 Builder.restoreIP(TargetTaskBodyIP);
9849 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9850 return Err;
9851
9852 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9853 // it is given. These blocks are enumerated by
9854 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9855 // to be outside the region. In other words, OI.ExitBlock is expected to be
9856 // the start of the region after the outlining. We used to set OI.ExitBlock
9857 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9858 // except when the task body is a single basic block. In that case,
9859 // OI.ExitBlock is set to the single task body block and will get left out of
9860 // the outlining process. So, simply create a new empty block to which we
9861 // uncoditionally branch from where TaskBodyCB left off
9862 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9863 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9864 /*IsFinished=*/true);
9865
9866 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9867 bool NeedsTargetTask = HasNoWait && DeviceID;
9868 if (NeedsTargetTask) {
9869 for (auto *V :
9870 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9871 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9872 RTArgs.SizesArray}) {
9874 OffloadingArraysToPrivatize.push_back(V);
9875 OI->ExcludeArgsFromAggregate.push_back(V);
9876 }
9877 }
9878 }
9879 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9880 DeviceID, OffloadingArraysToPrivatize](
9881 Function &OutlinedFn) mutable {
9882 assert(OutlinedFn.hasOneUse() &&
9883 "there must be a single user for the outlined function");
9884
9885 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9886
9887 // The first argument of StaleCI is always the thread id.
9888 // The next few arguments are the pointers to offloading arrays
9889 // if any. (see OffloadingArraysToPrivatize)
9890 // Finally, all other local values that are live-in into the outlined region
9891 // end up in a structure whose pointer is passed as the last argument. This
9892 // piece of data is passed in the "shared" field of the task structure. So,
9893 // we know we have to pass shareds to the task if the number of arguments is
9894 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9895 // thread id. Further, for safety, we assert that the number of arguments of
9896 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9897 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9898 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9899 assert((!HasShareds ||
9900 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9901 "Wrong number of arguments for StaleCI when shareds are present");
9902 int SharedArgOperandNo =
9903 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9904
9905 StructType *TaskWithPrivatesTy =
9906 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9907 StructType *PrivatesTy = nullptr;
9908
9909 if (!OffloadingArraysToPrivatize.empty())
9910 PrivatesTy =
9911 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9912
9914 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9915 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9916
9917 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9918 << "\n");
9919
9920 Builder.SetInsertPoint(StaleCI);
9921
9922 // Gather the arguments for emitting the runtime call.
9923 uint32_t SrcLocStrSize;
9924 Constant *SrcLocStr =
9926 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9927
9928 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9929 //
9930 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9931 // the DeviceID to the deferred task and also since
9932 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9933 Function *TaskAllocFn =
9934 !NeedsTargetTask
9935 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9937 OMPRTL___kmpc_omp_target_task_alloc);
9938
9939 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9940 // call.
9941 Value *ThreadID = getOrCreateThreadID(Ident);
9942
9943 // Argument - `sizeof_kmp_task_t` (TaskSize)
9944 // Tasksize refers to the size in bytes of kmp_task_t data structure
9945 // plus any other data to be passed to the target task, if any, which
9946 // is packed into a struct. kmp_task_t and the struct so created are
9947 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9948 Value *TaskSize = Builder.getInt64(
9949 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9950
9951 // Argument - `sizeof_shareds` (SharedsSize)
9952 // SharedsSize refers to the shareds array size in the kmp_task_t data
9953 // structure.
9954 Value *SharedsSize = Builder.getInt64(0);
9955 if (HasShareds) {
9956 auto *ArgStructAlloca =
9957 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9958 assert(ArgStructAlloca &&
9959 "Unable to find the alloca instruction corresponding to arguments "
9960 "for extracted function");
9961 std::optional<TypeSize> ArgAllocSize =
9962 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9963 assert(ArgAllocSize &&
9964 "Unable to determine size of arguments for extracted function");
9965 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9966 }
9967
9968 // Argument - `flags`
9969 // Task is tied iff (Flags & 1) == 1.
9970 // Task is untied iff (Flags & 1) == 0.
9971 // Task is final iff (Flags & 2) == 2.
9972 // Task is not final iff (Flags & 2) == 0.
9973 // A target task is not final and is untied.
9974 Value *Flags = Builder.getInt32(0);
9975
9976 // Emit the @__kmpc_omp_task_alloc runtime call
9977 // The runtime call returns a pointer to an area where the task captured
9978 // variables must be copied before the task is run (TaskData)
9979 CallInst *TaskData = nullptr;
9980
9981 SmallVector<llvm::Value *> TaskAllocArgs = {
9982 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9983 /*flags=*/Flags,
9984 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9985 /*task_func=*/ProxyFn};
9986
9987 if (NeedsTargetTask) {
9988 assert(DeviceID && "Expected non-empty device ID.");
9989 TaskAllocArgs.push_back(DeviceID);
9990 }
9991
9992 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9993
9994 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9995 if (HasShareds) {
9996 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9998 *this, Builder, TaskData, TaskWithPrivatesTy);
9999 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
10000 SharedsSize);
10001 }
10002 if (!OffloadingArraysToPrivatize.empty()) {
10003 Value *Privates =
10004 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
10005 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
10006 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
10007 [[maybe_unused]] Type *ArrayType =
10008 getOffloadingArrayType(PtrToPrivatize);
10009 assert(ArrayType && "ArrayType cannot be nullptr");
10010
10011 Type *ElementType = PrivatesTy->getElementType(i);
10012 assert(ElementType == ArrayType &&
10013 "ElementType should match ArrayType");
10014 (void)ArrayType;
10015
10016 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
10017 Builder.CreateMemCpy(
10018 Dst, Alignment, PtrToPrivatize, Alignment,
10019 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
10020 }
10021 }
10022
10023 Value *DepArray = nullptr;
10024 Value *NumDeps = nullptr;
10025 if (Dependencies.DepArray) {
10026 DepArray = Dependencies.DepArray;
10027 NumDeps = Dependencies.NumDeps;
10028 } else if (!Dependencies.Deps.empty()) {
10029 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
10030 NumDeps = Builder.getInt32(Dependencies.Deps.size());
10031 }
10032
10033 // ---------------------------------------------------------------
10034 // V5.2 13.8 target construct
10035 // If the nowait clause is present, execution of the target task
10036 // may be deferred. If the nowait clause is not present, the target task is
10037 // an included task.
10038 // ---------------------------------------------------------------
10039 // The above means that the lack of a nowait on the target construct
10040 // translates to '#pragma omp task if(0)'
10041 if (!NeedsTargetTask) {
10042 if (DepArray) {
10043 Function *TaskWaitFn =
10044 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10046 TaskWaitFn,
10047 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10048 /*ndeps=*/NumDeps,
10049 /*dep_list=*/DepArray,
10050 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10051 /*noalias_dep_list=*/
10053 }
10054 // Included task.
10055 Function *TaskBeginFn =
10056 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10057 Function *TaskCompleteFn =
10058 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10059 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10060 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10061 CI->setDebugLoc(StaleCI->getDebugLoc());
10062 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10063 } else if (DepArray) {
10064 // HasNoWait - meaning the task may be deferred. Call
10065 // __kmpc_omp_task_with_deps if there are dependencies,
10066 // else call __kmpc_omp_task
10067 Function *TaskFn =
10068 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10070 TaskFn,
10071 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10072 ConstantInt::get(Builder.getInt32Ty(), 0),
10074 } else {
10075 // Emit the @__kmpc_omp_task runtime call to spawn the task
10076 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10077 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10078 }
10079
10080 Builder.ClearInsertionPoint();
10081 StaleCI->eraseFromParent();
10082 for (Instruction *I : llvm::reverse(ToBeDeleted))
10083 I->eraseFromParent();
10084 };
10085 addOutlineInfo(std::move(OI));
10086
10087 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10088 << *(Builder.GetInsertBlock()) << "\n");
10089 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10090 << *(Builder.getModule()) << "\n");
10091 return Builder.saveIP();
10092}
10093
10095 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10096 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10097 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10098 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10099 if (Error Err =
10100 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10101 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10102 return Err;
10103 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10104 return Error::success();
10105}
10106
10107static void emitTargetCall(
10108 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride,
10113 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10117 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10118 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10119 // Generate a function call to the host fallback implementation of the target
10120 // region. This is called by the host when no offload entry was generated for
10121 // the target region and when the offloading call fails at runtime.
10122 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10124 Builder.restoreIP(IP);
10125 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10126 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10127 FallbackArgs.push_back(
10128 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10129 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10130 return Builder.saveIP();
10131 };
10132
10133 bool HasDependencies = !Dependencies.empty();
10134 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10135
10137
10138 auto TaskBodyCB =
10139 [&](Value *DeviceID, Value *RTLoc,
10140 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10141 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10142 // produce any.
10144 // emitKernelLaunch makes the necessary runtime call to offload the
10145 // kernel. We then outline all that code into a separate function
10146 // ('kernel_launch_function' in the pseudo code above). This function is
10147 // then called by the target task proxy function (see
10148 // '@.omp_target_task_proxy_func' in the pseudo code above)
10149 // "@.omp_target_task_proxy_func' is generated by
10150 // emitTargetTaskProxyFunction.
10151 if (OutlinedFnID && DeviceID)
10152 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10153 EmitTargetCallFallbackCB, KArgs,
10154 DeviceID, RTLoc, TargetTaskAllocaIP);
10155
10156 // We only need to do the outlining if `DeviceID` is set to avoid calling
10157 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10158 // generating the `else` branch of an `if` clause.
10159 //
10160 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10161 // In this case, we execute the host implementation directly.
10162 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10163 }());
10164
10165 OMPBuilder.Builder.restoreIP(AfterIP);
10166 return Error::success();
10167 };
10168
10169 auto &&EmitTargetCallElse =
10170 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10172 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10173 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10174 // produce any.
10176 if (RequiresOuterTargetTask) {
10177 // Arguments that are intended to be directly forwarded to an
10178 // emitKernelLaunch call are pased as nullptr, since
10179 // OutlinedFnID=nullptr results in that call not being done.
10181 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10182 /*RTLoc=*/nullptr, AllocaIP,
10183 Dependencies, EmptyRTArgs, HasNoWait);
10184 }
10185 return EmitTargetCallFallbackCB(Builder.saveIP());
10186 }());
10187
10188 Builder.restoreIP(AfterIP);
10189 return Error::success();
10190 };
10191
10192 auto &&EmitTargetCallThen =
10193 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10195 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10196 Info.HasNoWait = HasNoWait;
10197 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10198
10200 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10201 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10202 /*IsNonContiguous=*/true,
10203 /*ForEndCall=*/false))
10204 return Err;
10205
10206 SmallVector<Value *, 3> NumTeamsC;
10207 for (auto [DefaultVal, RuntimeVal] :
10208 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10209 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10210 : Builder.getInt32(DefaultVal));
10211
10212 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10213 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10214 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10215 if (Clause)
10216 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10217 /*isSigned=*/false);
10218 return Clause;
10219 };
10220 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10221 if (Clause)
10222 Result =
10223 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10224 Result, Clause)
10225 : Clause;
10226 };
10227
10228 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10229 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10230 SmallVector<Value *, 3> NumThreadsC;
10231 Value *MaxThreadsClause =
10232 RuntimeAttrs.TeamsThreadLimit.size() == 1
10233 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10234 : nullptr;
10235
10236 for (auto [TeamsVal, TargetVal] : zip_equal(
10237 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10238 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10239 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10240
10241 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10242 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10243
10244 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10245 }
10246
10247 unsigned NumTargetItems = Info.NumberOfPtrs;
10248 Value *RTLoc = RTLocOverride;
10249 if (!RTLoc) {
10250 uint32_t SrcLocStrSize;
10251 Constant *SrcLocStr =
10252 OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10253 RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10254 llvm::omp::IdentFlag(0), 0);
10255 }
10256
10257 Value *TripCount = RuntimeAttrs.LoopTripCount
10258 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10259 Builder.getInt64Ty(),
10260 /*isSigned=*/false)
10261 : Builder.getInt64(0);
10262
10263 // Request zero groupprivate bytes by default.
10264 if (!DynCGroupMem)
10265 DynCGroupMem = Builder.getInt32(0);
10266
10268 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10269 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10270 DynCGroupMemFallback);
10271
10272 // Assume no error was returned because TaskBodyCB and
10273 // EmitTargetCallFallbackCB don't produce any.
10275 // The presence of certain clauses on the target directive require the
10276 // explicit generation of the target task.
10277 if (RequiresOuterTargetTask)
10278 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10279 RTLoc, AllocaIP, Dependencies,
10280 KArgs.RTArgs, Info.HasNoWait);
10281
10282 return OMPBuilder.emitKernelLaunch(
10283 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10284 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10285 }());
10286
10287 Builder.restoreIP(AfterIP);
10288 return Error::success();
10289 };
10290
10291 // If we don't have an ID for the target region, it means an offload entry
10292 // wasn't created. In this case we just run the host fallback directly and
10293 // ignore any potential 'if' clauses.
10294 if (!OutlinedFnID) {
10295 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10296 return;
10297 }
10298
10299 // If there's no 'if' clause, only generate the kernel launch code path.
10300 if (!IfCond) {
10301 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10302 return;
10303 }
10304
10305 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10306 EmitTargetCallElse, AllocaIP));
10307}
10308
10310 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10311 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10312 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10313 const TargetKernelDefaultAttrs &DefaultAttrs,
10314 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10315 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10318 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10319 bool HasNowait, Value *DynCGroupMem,
10320 OMPDynGroupprivateFallbackType DynCGroupMemFallback, DebugLoc OutlinedFnLoc,
10321 Value *RTLocOverride) {
10322
10323 if (!updateToLocation(Loc))
10324 return InsertPointTy();
10325
10326 Builder.restoreIP(CodeGenIP);
10327
10328 Function *OutlinedFn;
10329 Constant *OutlinedFnID = nullptr;
10330 // The target region is outlined into its own function. The LLVM IR for
10331 // the target region itself is generated using the callbacks CBFunc
10332 // and ArgAccessorFuncCB
10334 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10335 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10336 return Err;
10337
10338 // If we are not on the target device, then we need to generate code
10339 // to make a remote call (offload) to the previously outlined function
10340 // that represents the target region. Do that now.
10341 if (!Config.isTargetDevice())
10342 emitTargetCall(*this, Builder, RTLocOverride, AllocaIP, DeallocBlocks, Info,
10343 DefaultAttrs, RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID,
10344 Inputs, GenMapInfoCB, CustomMapperCB, Dependencies,
10345 HasNowait, DynCGroupMem, DynCGroupMemFallback);
10346 return Builder.saveIP();
10347}
10348
10349std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10350 StringRef FirstSeparator,
10351 StringRef Separator) {
10352 SmallString<128> Buffer;
10353 llvm::raw_svector_ostream OS(Buffer);
10354 StringRef Sep = FirstSeparator;
10355 for (StringRef Part : Parts) {
10356 OS << Sep << Part;
10357 Sep = Separator;
10358 }
10359 return OS.str().str();
10360}
10361
10362std::string
10364 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10365 Config.separator());
10366}
10367
10369 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10370 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10371 if (Elem.second) {
10372 assert(Elem.second->getValueType() == Ty &&
10373 "OMP internal variable has different type than requested");
10374 } else {
10375 // TODO: investigate the appropriate linkage type used for the global
10376 // variable for possibly changing that to internal or private, or maybe
10377 // create different versions of the function for different OMP internal
10378 // variables.
10379 const DataLayout &DL = M.getDataLayout();
10380 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10381 // default global AS is 1.
10382 // See double-target-call-with-declare-target.f90 and
10383 // declare-target-vars-in-target-region.f90 libomptarget
10384 // tests.
10385 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10386 : M.getTargetTriple().isAMDGPU()
10387 ? 0
10388 : DL.getDefaultGlobalsAddressSpace();
10389 auto Linkage = this->M.getTargetTriple().isWasm()
10392 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10393 Constant::getNullValue(Ty), Elem.first(),
10394 /*InsertBefore=*/nullptr,
10395 GlobalValue::NotThreadLocal, AddressSpaceVal);
10396 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10397 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10398 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10399 Elem.second = GV;
10400 }
10401
10402 return Elem.second;
10403}
10404
10405Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10406 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10407 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10408 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10409}
10410
10412 LLVMContext &Ctx = Builder.getContext();
10413 Value *Null =
10414 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10415 Value *SizeGep =
10416 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10417 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10418 return SizePtrToInt;
10419}
10420
10423 std::string VarName) {
10424 llvm::Constant *MaptypesArrayInit =
10425 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10426 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10427 M, MaptypesArrayInit->getType(),
10428 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10429 VarName);
10430 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10431 return MaptypesArrayGlobal;
10432}
10433
10435 InsertPointTy AllocaIP,
10436 unsigned NumOperands,
10437 struct MapperAllocas &MapperAllocas) {
10438 if (!updateToLocation(Loc))
10439 return;
10440
10441 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10442 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10443 Builder.restoreIP(AllocaIP);
10444 AllocaInst *ArgsBase = Builder.CreateAlloca(
10445 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10446 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10447 ".offload_ptrs");
10448 AllocaInst *ArgSizes = Builder.CreateAlloca(
10449 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10451 MapperAllocas.ArgsBase = ArgsBase;
10452 MapperAllocas.Args = Args;
10453 MapperAllocas.ArgSizes = ArgSizes;
10454}
10455
10457 Function *MapperFunc, Value *SrcLocInfo,
10458 Value *MaptypesArg, Value *MapnamesArg,
10460 int64_t DeviceID, unsigned NumOperands) {
10461 if (!updateToLocation(Loc))
10462 return;
10463
10464 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10465 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10466 Value *ArgsBaseGEP =
10467 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10468 {Builder.getInt32(0), Builder.getInt32(0)});
10469 Value *ArgsGEP =
10470 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10471 {Builder.getInt32(0), Builder.getInt32(0)});
10472 Value *ArgSizesGEP =
10473 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10474 {Builder.getInt32(0), Builder.getInt32(0)});
10475 Value *NullPtr =
10476 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10477 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10478 Builder.getInt32(NumOperands),
10479 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10480 MaptypesArg, MapnamesArg, NullPtr});
10481}
10482
10484 TargetDataRTArgs &RTArgs,
10485 TargetDataInfo &Info,
10486 bool ForEndCall) {
10487 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10488 "expected region end call to runtime only when end call is separate");
10489 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10490 auto VoidPtrTy = UnqualPtrTy;
10491 auto VoidPtrPtrTy = UnqualPtrTy;
10492 auto Int64Ty = Type::getInt64Ty(M.getContext());
10493 auto Int64PtrTy = UnqualPtrTy;
10494
10495 if (!Info.NumberOfPtrs) {
10496 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10497 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10498 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10499 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10500 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10501 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10502 return;
10503 }
10504
10505 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10506 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10507 Info.RTArgs.BasePointersArray,
10508 /*Idx0=*/0, /*Idx1=*/0);
10509 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10510 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10511 /*Idx0=*/0,
10512 /*Idx1=*/0);
10513 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10514 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10515 /*Idx0=*/0, /*Idx1=*/0);
10516 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10517 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10518 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10519 : Info.RTArgs.MapTypesArray,
10520 /*Idx0=*/0,
10521 /*Idx1=*/0);
10522
10523 // Only emit the mapper information arrays if debug information is
10524 // requested.
10525 if (!Info.EmitDebug)
10526 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10527 else
10528 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10529 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10530 /*Idx0=*/0,
10531 /*Idx1=*/0);
10532 // If there is no user-defined mapper, set the mapper array to nullptr to
10533 // avoid an unnecessary data privatization
10534 if (!Info.HasMapper)
10535 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10536 else
10537 RTArgs.MappersArray =
10538 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10539}
10540
10542 InsertPointTy CodeGenIP,
10543 MapInfosTy &CombinedInfo,
10544 TargetDataInfo &Info) {
10546 CombinedInfo.NonContigInfo;
10547
10548 // Build an array of struct descriptor_dim and then assign it to
10549 // offload_args.
10550 //
10551 // struct descriptor_dim {
10552 // uint64_t offset;
10553 // uint64_t count;
10554 // uint64_t stride
10555 // };
10556 Type *Int64Ty = Builder.getInt64Ty();
10558 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10559 "struct.descriptor_dim");
10560
10561 enum { OffsetFD = 0, CountFD, StrideFD };
10562 // We need two index variable here since the size of "Dims" is the same as
10563 // the size of Components, however, the size of offset, count, and stride is
10564 // equal to the size of base declaration that is non-contiguous.
10565 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10566 // Skip emitting ir if dimension size is 1 since it cannot be
10567 // non-contiguous.
10568 if (NonContigInfo.Dims[I] == 1)
10569 continue;
10570 Builder.restoreIP(AllocaIP);
10571 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10572 AllocaInst *DimsAddr =
10573 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10574 Builder.restoreIP(CodeGenIP);
10575 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10576 unsigned RevIdx = EE - II - 1;
10577 Value *DimsLVal = Builder.CreateInBoundsGEP(
10578 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10579 // Offset
10580 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10581 Builder.CreateAlignedStore(
10582 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10583 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10584 // Count
10585 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10586 Builder.CreateAlignedStore(
10587 NonContigInfo.Counts[L][RevIdx], CountLVal,
10588 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10589 // Stride
10590 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10591 Builder.CreateAlignedStore(
10592 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10593 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10594 }
10595 // args[I] = &dims
10596 Builder.restoreIP(CodeGenIP);
10597 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10598 DimsAddr, Builder.getPtrTy());
10599 Value *P = Builder.CreateConstInBoundsGEP2_32(
10600 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10601 Info.RTArgs.PointersArray, 0, I);
10602 Builder.CreateAlignedStore(
10603 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10604 ++L;
10605 }
10606}
10607
10608void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10609 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10610 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10611 BasicBlock *ExitBB, bool IsInit) {
10612 StringRef Prefix = IsInit ? ".init" : ".del";
10613
10614 // Evaluate if this is an array section.
10616 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10617 Value *IsArray =
10618 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10619 Value *DeleteBit = Builder.CreateAnd(
10620 MapType,
10621 Builder.getInt64(
10622 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10623 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10624 Value *DeleteCond;
10625 Value *Cond;
10626 if (IsInit) {
10627 // base != begin?
10628 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10629 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10630 DeleteCond = Builder.CreateIsNull(
10631 DeleteBit,
10632 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10633 } else {
10634 Cond = IsArray;
10635 DeleteCond = Builder.CreateIsNotNull(
10636 DeleteBit,
10637 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10638 }
10639 Cond = Builder.CreateAnd(Cond, DeleteCond);
10640 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10641
10642 emitBlock(BodyBB, MapperFn);
10643 // Get the array size by multiplying element size and element number (i.e., \p
10644 // Size).
10645 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10646 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10647 // memory allocation/deletion purpose only.
10648 Value *MapTypeArg = Builder.CreateAnd(
10649 MapType,
10650 Builder.getInt64(
10651 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10652 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10653 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10654 MapTypeArg = Builder.CreateOr(
10655 MapTypeArg,
10656 Builder.getInt64(
10657 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10658 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10659
10660 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10661 // data structure.
10662 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10663 ArraySize, MapTypeArg, MapName};
10665 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10666 OffloadingArgs);
10667}
10668
10671 llvm::Value *BeginArg)>
10672 GenMapInfoCB,
10673 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10674 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10675 SmallVector<Type *> Params;
10676 Params.emplace_back(Builder.getPtrTy());
10677 Params.emplace_back(Builder.getPtrTy());
10678 Params.emplace_back(Builder.getPtrTy());
10679 Params.emplace_back(Builder.getInt64Ty());
10680 Params.emplace_back(Builder.getInt64Ty());
10681 Params.emplace_back(Builder.getPtrTy());
10682
10683 auto *FnTy =
10684 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10685
10686 SmallString<64> TyStr;
10687 raw_svector_ostream Out(TyStr);
10688 Function *MapperFn =
10690 MapperFn->addFnAttr(Attribute::NoInline);
10691 MapperFn->addFnAttr(Attribute::NoUnwind);
10692 MapperFn->addParamAttr(0, Attribute::NoUndef);
10693 MapperFn->addParamAttr(1, Attribute::NoUndef);
10694 MapperFn->addParamAttr(2, Attribute::NoUndef);
10695 MapperFn->addParamAttr(3, Attribute::NoUndef);
10696 MapperFn->addParamAttr(4, Attribute::NoUndef);
10697 MapperFn->addParamAttr(5, Attribute::NoUndef);
10698
10699 // Start the mapper function code generation.
10700 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10702 Builder.SetInsertPoint(EntryBB);
10703 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10704
10705 Value *MapperHandle = MapperFn->getArg(0);
10706 Value *BaseIn = MapperFn->getArg(1);
10707 Value *BeginIn = MapperFn->getArg(2);
10708 Value *Size = MapperFn->getArg(3);
10709 Value *MapType = MapperFn->getArg(4);
10710 Value *MapName = MapperFn->getArg(5);
10711
10712 // Compute the starting and end addresses of array elements.
10713 // Prepare common arguments for array initiation and deletion.
10714 // Convert the size in bytes into the number of array elements.
10715 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10716 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10717 Value *PtrBegin = BeginIn;
10718 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10719
10720 // Emit array initiation if this is an array section and \p MapType indicates
10721 // that memory allocation is required.
10722 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10723 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10724 MapType, MapName, ElementSize, HeadBB,
10725 /*IsInit=*/true);
10726
10727 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10728
10729 // Emit the loop header block.
10730 emitBlock(HeadBB, MapperFn);
10731 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10732 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10733 // Evaluate whether the initial condition is satisfied.
10734 Value *IsEmpty =
10735 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10736 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10737
10738 // Emit the loop body block.
10739 emitBlock(BodyBB, MapperFn);
10740 BasicBlock *LastBB = BodyBB;
10741 PHINode *PtrPHI =
10742 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10743 PtrPHI->addIncoming(PtrBegin, HeadBB);
10744
10745 // Get map clause information. Fill up the arrays with all mapped variables.
10746 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10747 if (!Info)
10748 return Info.takeError();
10749
10750 // Call the runtime API __tgt_mapper_num_components to get the number of
10751 // pre-existing components.
10752 Value *OffloadingArgs[] = {MapperHandle};
10753 Value *PreviousSize = createRuntimeFunctionCall(
10754 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10755 OffloadingArgs);
10756 Value *ShiftedPreviousSize =
10757 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10758
10759 // Fill up the runtime mapper handle for all components.
10760 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10761 Value *CurBaseArg = Info->BasePointers[I];
10762 Value *CurBeginArg = Info->Pointers[I];
10763 Value *CurSizeArg = Info->Sizes[I];
10764 Value *CurNameArg = Info->Names.size()
10765 ? Info->Names[I]
10766 : Constant::getNullValue(Builder.getPtrTy());
10767
10768 Value *OriMapType = Builder.getInt64(
10769 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10770 Info->Types[I]));
10771 auto RawType =
10772 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10773 Info->Types[I]);
10774 constexpr uint64_t MemberOfMask =
10775 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10776 constexpr uint64_t AttachBit =
10777 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10778 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10779
10780 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10781 // current array element (N = __tgt_mapper_num_components() at loop body
10782 // start).
10783 //
10784 // Example 1:
10785 // struct S { int x; int *p; };
10786 //
10787 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10788 // use: S arr[2]; ... map(arr)
10789 // entries per element:
10790 //
10791 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10792 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10793 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10794 //
10795 // Example 2:
10796 // struct S1 { int x; int y; };
10797 // struct S2 { int z; S1 *s1p; };
10798 //
10799 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10800 // s2.s1p->y)
10801 // use: S2 arr[2]; ... map(arr)
10802 // entries per element:
10803 //
10804 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10805 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10806 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10807 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10808 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10809 //
10810 // x/y carry inner MEMBER_OF(2)
10811 // which is shifted by N to become MEMBER_OF(N+2).
10812 //
10813 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10814 // the combined ALLOC entry for the s1p->x..y block, and the individual
10815 // x/y entries that are MEMBER_OF that block, all describe storage
10816 // reached through the attach ptr arr[i].s1p.
10817 //
10818 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10819 // linking them to the parent struct:
10820 //
10821 // * (*) Entries with HasAttachPtr: they represent pointee data that
10822 // occupies a different storage block than the struct being mapped, so
10823 // they are not a member of it. They may still be MEMBER_OF an entry
10824 // within that pointee block, in which case those pre-existing bits are
10825 // shifted -- see (***).
10826 // * (**) ATTACH entries: they are not a member of anything — they just
10827 // link a ptr to its ptee.
10828 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10829 // its pre-shaped entries already carry their final MEMBER_OF bits.
10830 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10831 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10832 // it.
10833 //
10834 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10835 // s1p->x/y entries above), those bits are still shifted by N.
10836 Value *MemberMapType;
10837 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10838 Info->HasAttachPtr[I]) {
10839 if (RawType & MemberOfMask)
10840 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10841 else
10842 MemberMapType = OriMapType;
10843 } else {
10844 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10845 }
10846
10847 // Combine the map type inherited from user-defined mapper with that
10848 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10849 // bits of the \a MapType, which is the input argument of the mapper
10850 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10851 // bits of MemberMapType.
10852 // [OpenMP 5.0], 1.2.6. map-type decay.
10853 // | alloc | to | from | tofrom | release | delete
10854 // ----------------------------------------------------------
10855 // alloc | alloc | alloc | alloc | alloc | release | delete
10856 // to | alloc | to | alloc | to | release | delete
10857 // from | alloc | alloc | from | from | release | delete
10858 // tofrom | alloc | to | from | tofrom | release | delete
10859 Value *LeftToFrom = Builder.CreateAnd(
10860 MapType,
10861 Builder.getInt64(
10862 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10863 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10864 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10865 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10866 BasicBlock *AllocElseBB =
10867 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10868 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10869 BasicBlock *ToElseBB =
10870 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10871 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10872 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10873 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10874 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10875 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10876 emitBlock(AllocBB, MapperFn);
10877 Value *AllocMapType = Builder.CreateAnd(
10878 MemberMapType,
10879 Builder.getInt64(
10880 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10881 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10882 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10883 Builder.CreateBr(EndBB);
10884 emitBlock(AllocElseBB, MapperFn);
10885 Value *IsTo = Builder.CreateICmpEQ(
10886 LeftToFrom,
10887 Builder.getInt64(
10888 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10889 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10890 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10891 // In case of to, clear OMP_MAP_FROM.
10892 emitBlock(ToBB, MapperFn);
10893 Value *ToMapType = Builder.CreateAnd(
10894 MemberMapType,
10895 Builder.getInt64(
10896 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10897 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10898 Builder.CreateBr(EndBB);
10899 emitBlock(ToElseBB, MapperFn);
10900 Value *IsFrom = Builder.CreateICmpEQ(
10901 LeftToFrom,
10902 Builder.getInt64(
10903 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10904 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10905 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10906 // In case of from, clear OMP_MAP_TO.
10907 emitBlock(FromBB, MapperFn);
10908 Value *FromMapType = Builder.CreateAnd(
10909 MemberMapType,
10910 Builder.getInt64(
10911 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10912 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10913 // In case of tofrom, do nothing.
10914 emitBlock(EndBB, MapperFn);
10915 LastBB = EndBB;
10916 PHINode *CurMapType =
10917 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10918 CurMapType->addIncoming(AllocMapType, AllocBB);
10919 CurMapType->addIncoming(ToMapType, ToBB);
10920 CurMapType->addIncoming(FromMapType, FromBB);
10921 CurMapType->addIncoming(MemberMapType, ToElseBB);
10922
10923 // Propagate map-type-modifying bits from the outer map clause to each map
10924 // inserted by the mapper.
10925 //
10926 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10927 // list item from the map clause and to apply the clauses specified in the
10928 // declared mapper to the construct on which the map clause appears...
10929 // If any modifier with the map-type-modifying property appears in the map
10930 // clause then the effect is as if that modifier appears in each map clause
10931 // specified in the declared mapper.
10932 //
10933 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10934 //
10935 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10936 //
10937 // PRESENT is propagated only to entries that have an attach ptr
10938 // (HasAttachPtr): the pointee data, which occupies a different storage
10939 // block than the struct being mapped and so is not covered by the
10940 // present-check on the struct's own storage. A present modifier on the
10941 // outer clause must still require that pointee to be present on the device.
10942 //
10943 // This is gated on \p PropagatePresentToPointee (set by callers only for
10944 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10945 // applying to the pointee: the spec committee confirmed the divergence
10946 // between the present "motion" modifier (to/from) and the present map-type
10947 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10948 // so for 5.2 present is ignored for the pointee for both map and to/from.
10949 //
10950 // TODO: PRESENT should also be propagated to the struct's own members
10951 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10952 // member triggers the present-check. We cannot do that yet: while pointer
10953 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10954 // the whole struct (including the pointer's storage), so propagating
10955 // PRESENT to it would wrongly require the pointer's pointee to be present.
10956 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10957 // attach-style maps throughout.
10958 uint64_t ModifierBits =
10959 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10960 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10961 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10962 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10963 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10964 ModifierBits |=
10965 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10966 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10967 Value *ImportedModifierBits =
10968 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10969 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10970 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10971
10972 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10973 // reserved for the attach(always) map-type modifier, and other modifier
10974 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10975 Value *FinalMapType =
10976 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10977
10978 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10979 CurSizeArg, FinalMapType, CurNameArg};
10980
10981 auto ChildMapperFn = CustomMapperCB(I);
10982 if (!ChildMapperFn)
10983 return ChildMapperFn.takeError();
10984 if (*ChildMapperFn) {
10985 // Call the corresponding mapper function.
10986 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10987 ->setDoesNotThrow();
10988 } else {
10989 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10990 // data structure.
10992 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10993 OffloadingArgs);
10994 }
10995 }
10996
10997 // Update the pointer to point to the next element that needs to be mapped,
10998 // and check whether we have mapped all elements.
10999 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
11000 "omp.arraymap.next");
11001 PtrPHI->addIncoming(PtrNext, LastBB);
11002 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
11003 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
11004 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
11005
11006 emitBlock(ExitBB, MapperFn);
11007 // Emit array deletion if this is an array section and \p MapType indicates
11008 // that deletion is required.
11009 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
11010 MapType, MapName, ElementSize, DoneBB,
11011 /*IsInit=*/false);
11012
11013 // Emit the function exit block.
11014 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
11015
11016 Builder.CreateRetVoid();
11017 return MapperFn;
11018}
11019
11021 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
11022 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
11023 bool IsNonContiguous,
11024 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
11025
11026 // Reset the array information.
11027 Info.clearArrayInfo();
11028 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
11029
11030 if (Info.NumberOfPtrs == 0)
11031 return Error::success();
11032
11033 Builder.restoreIP(AllocaIP);
11034 // Detect if we have any capture size requiring runtime evaluation of the
11035 // size so that a constant array could be eventually used.
11036 ArrayType *PointerArrayType =
11037 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
11038
11039 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
11040 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
11041
11042 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11043 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11044 AllocaInst *MappersArray = Builder.CreateAlloca(
11045 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11046 Info.RTArgs.MappersArray = MappersArray;
11047
11048 // If we don't have any VLA types or other types that require runtime
11049 // evaluation, we can use a constant array for the map sizes, otherwise we
11050 // need to fill up the arrays as we do for the pointers.
11051 Type *Int64Ty = Builder.getInt64Ty();
11052 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11053 ConstantInt::get(Int64Ty, 0));
11054 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11055 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11056 bool IsNonContigEntry =
11057 IsNonContiguous &&
11058 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11059 CombinedInfo.Types[I] &
11060 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11061 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11062 // descriptor_dim records), not the byte size.
11063 if (IsNonContigEntry) {
11064 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11065 "Index must be in-bounds for NON_CONTIG Dims array");
11066 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11067 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11068 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11069 continue;
11070 }
11071 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11072 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11073 ConstSizes[I] = CI;
11074 continue;
11075 }
11076 }
11077 RuntimeSizes.set(I);
11078 }
11079
11080 if (RuntimeSizes.all()) {
11081 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11082 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11083 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11084 restoreIPandDebugLoc(Builder, CodeGenIP);
11085 } else {
11086 auto *SizesArrayInit = ConstantArray::get(
11087 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11088 std::string Name = createPlatformSpecificName({"offload_sizes"});
11089 auto *SizesArrayGbl =
11090 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11091 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11092 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11093
11094 if (!RuntimeSizes.any()) {
11095 Info.RTArgs.SizesArray = SizesArrayGbl;
11096 } else {
11097 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11098 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11099 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11100 AllocaInst *Buffer = Builder.CreateAlloca(
11101 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11102 Buffer->setAlignment(OffloadSizeAlign);
11103 restoreIPandDebugLoc(Builder, CodeGenIP);
11104 Builder.CreateMemCpy(
11105 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11106 SizesArrayGbl, OffloadSizeAlign,
11107 Builder.getIntN(
11108 IndexSize,
11109 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11110
11111 Info.RTArgs.SizesArray = Buffer;
11112 }
11113 restoreIPandDebugLoc(Builder, CodeGenIP);
11114 }
11115
11116 // The map types are always constant so we don't need to generate code to
11117 // fill arrays. Instead, we create an array constant.
11119 for (auto mapFlag : CombinedInfo.Types)
11120 Mapping.push_back(
11121 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11122 mapFlag));
11123 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11124 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11125 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11126
11127 // The information types are only built if provided.
11128 if (!CombinedInfo.Names.empty()) {
11129 auto *MapNamesArrayGbl = createOffloadMapnames(
11130 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11131 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11132 Info.EmitDebug = true;
11133 } else {
11134 Info.RTArgs.MapNamesArray =
11136 Info.EmitDebug = false;
11137 }
11138
11139 // If there's a present map type modifier, it must not be applied to the end
11140 // of a region, so generate a separate map type array in that case.
11141 if (Info.separateBeginEndCalls()) {
11142 bool EndMapTypesDiffer = false;
11143 for (uint64_t &Type : Mapping) {
11144 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11145 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11146 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11147 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11148 EndMapTypesDiffer = true;
11149 }
11150 }
11151 if (EndMapTypesDiffer) {
11152 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11153 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11154 }
11155 }
11156
11157 PointerType *PtrTy = Builder.getPtrTy();
11158 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11159 Value *BPVal = CombinedInfo.BasePointers[I];
11160 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11161 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11162 0, I);
11163 Builder.CreateAlignedStore(BPVal, BP,
11164 M.getDataLayout().getPrefTypeAlign(PtrTy));
11165
11166 if (Info.requiresDevicePointerInfo()) {
11167 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11168 CodeGenIP = Builder.saveIP();
11169 Builder.restoreIP(AllocaIP);
11170 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11171 restoreIPandDebugLoc(Builder, CodeGenIP);
11172 if (DeviceAddrCB)
11173 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11174 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11175 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11176 if (DeviceAddrCB)
11177 DeviceAddrCB(I, BP);
11178 }
11179 }
11180
11181 Value *PVal = CombinedInfo.Pointers[I];
11182 Value *P = Builder.CreateConstInBoundsGEP2_32(
11183 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11184 I);
11185 // TODO: Check alignment correct.
11186 Builder.CreateAlignedStore(PVal, P,
11187 M.getDataLayout().getPrefTypeAlign(PtrTy));
11188
11189 if (RuntimeSizes.test(I)) {
11190 Value *S = Builder.CreateConstInBoundsGEP2_32(
11191 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11192 /*Idx0=*/0,
11193 /*Idx1=*/I);
11194 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11195 Int64Ty,
11196 /*isSigned=*/true),
11197 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11198 }
11199 // Fill up the mapper array.
11200 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11201 Value *MFunc = ConstantPointerNull::get(PtrTy);
11202
11203 auto CustomMFunc = CustomMapperCB(I);
11204 if (!CustomMFunc)
11205 return CustomMFunc.takeError();
11206 if (*CustomMFunc)
11207 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11208
11209 Value *MAddr = Builder.CreateInBoundsGEP(
11210 PointerArrayType, MappersArray,
11211 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11212 Builder.CreateAlignedStore(
11213 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11214 }
11215
11216 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11217 Info.NumberOfPtrs == 0)
11218 return Error::success();
11219 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11220 return Error::success();
11221}
11222
11224 BasicBlock *CurBB = Builder.GetInsertBlock();
11225
11226 if (!CurBB || CurBB->hasTerminator()) {
11227 // If there is no insert point or the previous block is already
11228 // terminated, don't touch it.
11229 } else {
11230 // Otherwise, create a fall-through branch.
11231 Builder.CreateBr(Target);
11232 }
11233
11234 Builder.ClearInsertionPoint();
11235}
11236
11238 bool IsFinished) {
11239 BasicBlock *CurBB = Builder.GetInsertBlock();
11240
11241 // Fall out of the current block (if necessary).
11242 emitBranch(BB);
11243
11244 if (IsFinished && BB->use_empty()) {
11245 BB->eraseFromParent();
11246 return;
11247 }
11248
11249 // Place the block after the current block, if possible, or else at
11250 // the end of the function.
11251 if (CurBB && CurBB->getParent())
11252 CurFn->insert(std::next(CurBB->getIterator()), BB);
11253 else
11254 CurFn->insert(CurFn->end(), BB);
11255 Builder.SetInsertPoint(BB);
11256}
11257
11259 BodyGenCallbackTy ElseGen,
11260 InsertPointTy AllocaIP,
11261 ArrayRef<BasicBlock *> DeallocBlocks) {
11262 // If the condition constant folds and can be elided, try to avoid emitting
11263 // the condition and the dead arm of the if/else.
11264 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11265 auto CondConstant = CI->getSExtValue();
11266 if (CondConstant)
11267 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11268
11269 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11270 }
11271
11272 Function *CurFn = Builder.GetInsertBlock()->getParent();
11273
11274 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11275 // emit the conditional branch.
11276 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11277 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11278 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11279 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11280 // Emit the 'then' code.
11281 emitBlock(ThenBlock, CurFn);
11282 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11283 return Err;
11284 emitBranch(ContBlock);
11285 // Emit the 'else' code if present.
11286 // There is no need to emit line number for unconditional branch.
11287 emitBlock(ElseBlock, CurFn);
11288 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11289 return Err;
11290 // There is no need to emit line number for unconditional branch.
11291 emitBranch(ContBlock);
11292 // Emit the continuation block for code after the if.
11293 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11294 return Error::success();
11295}
11296
11297bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11298 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11301 "Unexpected Atomic Ordering.");
11302
11303 bool Flush = false;
11305
11306 switch (AK) {
11307 case Read:
11310 FlushAO = AtomicOrdering::Acquire;
11311 Flush = true;
11312 }
11313 break;
11314 case Write:
11315 case Compare:
11316 case Update:
11319 FlushAO = AtomicOrdering::Release;
11320 Flush = true;
11321 }
11322 break;
11323 case Capture:
11324 switch (AO) {
11326 FlushAO = AtomicOrdering::Acquire;
11327 Flush = true;
11328 break;
11330 FlushAO = AtomicOrdering::Release;
11331 Flush = true;
11332 break;
11336 Flush = true;
11337 break;
11338 default:
11339 // do nothing - leave silently.
11340 break;
11341 }
11342 }
11343
11344 if (Flush) {
11345 // Currently Flush RT call still doesn't take memory_ordering, so for when
11346 // that happens, this tries to do the resolution of which atomic ordering
11347 // to use with but issue the flush call
11348 // TODO: pass `FlushAO` after memory ordering support is added
11349 (void)FlushAO;
11350 emitFlush(Loc);
11351 }
11352
11353 // for AO == AtomicOrdering::Monotonic and all other case combinations
11354 // do nothing
11355 return Flush;
11356}
11357
11361 AtomicOrdering AO, InsertPointTy AllocaIP) {
11362 if (!updateToLocation(Loc))
11363 return Loc.IP;
11364
11365 assert(X.Var->getType()->isPointerTy() &&
11366 "OMP Atomic expects a pointer to target memory");
11367 Type *XElemTy = X.ElemTy;
11368 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11369 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11370 "OMP atomic read expected a scalar type");
11371
11372 Value *XRead = nullptr;
11373
11374 if (XElemTy->isIntegerTy()) {
11375 LoadInst *XLD =
11376 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11377 XLD->setAtomic(AO);
11378 XRead = cast<Value>(XLD);
11379 } else if (XElemTy->isStructTy()) {
11380 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11381 // target does not support `atomicrmw` of the size of the struct
11382 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11383 OldVal->setAtomic(AO);
11384 const DataLayout &DL = OldVal->getDataLayout();
11385 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11386 OpenMPIRBuilder::AtomicInfo atomicInfo(
11387 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11388 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11389 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11390 XRead = AtomicLoadRes.first;
11391 OldVal->eraseFromParent();
11392 } else {
11393 // We need to perform atomic op as integer
11394 IntegerType *IntCastTy =
11395 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11396 LoadInst *XLoad =
11397 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11398 XLoad->setAtomic(AO);
11399 if (XElemTy->isFloatingPointTy()) {
11400 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11401 } else {
11402 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11403 }
11404 }
11405 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11406 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11407 return Builder.saveIP();
11408}
11409
11412 AtomicOpValue &X, Value *Expr,
11413 AtomicOrdering AO, InsertPointTy AllocaIP) {
11414 if (!updateToLocation(Loc))
11415 return Loc.IP;
11416
11417 assert(X.Var->getType()->isPointerTy() &&
11418 "OMP Atomic expects a pointer to target memory");
11419 Type *XElemTy = X.ElemTy;
11420 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11421 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11422 "OMP atomic write expected a scalar type");
11423
11424 if (XElemTy->isIntegerTy()) {
11425 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11426 XSt->setAtomic(AO);
11427 } else if (XElemTy->isStructTy()) {
11428 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11429 const DataLayout &DL = OldVal->getDataLayout();
11430 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11431 OpenMPIRBuilder::AtomicInfo atomicInfo(
11432 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11433 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11434 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11435 OldVal->eraseFromParent();
11436 } else {
11437 // We need to bitcast and perform atomic op as integers
11438 IntegerType *IntCastTy =
11439 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11440 Value *ExprCast =
11441 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11442 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11443 XSt->setAtomic(AO);
11444 }
11445
11446 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11447 return Builder.saveIP();
11448}
11449
11452 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11453 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11454 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11455 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11456 if (!updateToLocation(Loc))
11457 return Loc.IP;
11458
11459 LLVM_DEBUG({
11460 Type *XTy = X.Var->getType();
11461 assert(XTy->isPointerTy() &&
11462 "OMP Atomic expects a pointer to target memory");
11463 Type *XElemTy = X.ElemTy;
11464 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11465 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11466 "OMP atomic update expected a scalar or struct type");
11467 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11468 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11469 "OpenMP atomic does not support LT or GT operations");
11470 });
11471
11472 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11473 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11474 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11475 if (!AtomicResult)
11476 return AtomicResult.takeError();
11477 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11478 return Builder.saveIP();
11479}
11480
11481// FIXME: Duplicating AtomicExpand
11482Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11483 AtomicRMWInst::BinOp RMWOp) {
11484 switch (RMWOp) {
11485 case AtomicRMWInst::Add:
11486 return Builder.CreateAdd(Src1, Src2);
11487 case AtomicRMWInst::Sub:
11488 return Builder.CreateSub(Src1, Src2);
11489 case AtomicRMWInst::And:
11490 return Builder.CreateAnd(Src1, Src2);
11492 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11493 case AtomicRMWInst::Or:
11494 return Builder.CreateOr(Src1, Src2);
11495 case AtomicRMWInst::Xor:
11496 return Builder.CreateXor(Src1, Src2);
11501 case AtomicRMWInst::Max:
11502 case AtomicRMWInst::Min:
11515 llvm_unreachable("Unsupported atomic update operation");
11516 }
11517 llvm_unreachable("Unsupported atomic update operation");
11518}
11519
11521 // Loads cannot use Release or AcquireRelease ordering. This load is
11522 // just the initial value for the cmpxchg loop; the cmpxchg itself
11523 // retains the original ordering.
11524 AtomicOrdering LoadAO = AO;
11525
11526 if (AO == AtomicOrdering::Release) {
11528 } else if (AO == AtomicOrdering::AcquireRelease) {
11529 LoadAO = AtomicOrdering::Acquire;
11530 }
11531
11532 return LoadAO;
11533}
11534
11535Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11536 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11538 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11539 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11540 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11541 bool emitRMWOp = false;
11542 switch (RMWOp) {
11543 case AtomicRMWInst::Add:
11544 case AtomicRMWInst::And:
11546 case AtomicRMWInst::Or:
11547 case AtomicRMWInst::Xor:
11549 emitRMWOp = XElemTy;
11550 break;
11551 case AtomicRMWInst::Sub:
11552 emitRMWOp = (IsXBinopExpr && XElemTy);
11553 break;
11554 default:
11555 emitRMWOp = false;
11556 }
11557 emitRMWOp &= XElemTy->isIntegerTy();
11558
11559 std::pair<Value *, Value *> Res;
11560 if (emitRMWOp) {
11561 AtomicRMWInst *RMWInst =
11562 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11563 if (IsIgnoreDenormalMode)
11564 RMWInst->setMetadata(llvm::LLVMContext::MD_atomic_ignore_denormal_mode,
11565 llvm::MDNode::get(Builder.getContext(), {}));
11566 if (T.isAMDGPU()) {
11567 if (!IsFineGrainedMemory)
11568 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11569 llvm::MDNode::get(Builder.getContext(), {}));
11570 if (!IsRemoteMemory)
11571 RMWInst->setMetadata("amdgpu.no.remote.memory",
11572 llvm::MDNode::get(Builder.getContext(), {}));
11573 }
11574 Res.first = RMWInst;
11575 // not needed except in case of postfix captures. Generate anyway for
11576 // consistency with the else part. Will be removed with any DCE pass.
11577 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11578 if (RMWOp == AtomicRMWInst::Xchg)
11579 Res.second = Res.first;
11580 else
11581 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11582 } else if (XElemTy->isStructTy()) {
11583 LoadInst *OldVal =
11584 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11586 OldVal->setAtomic(LoadAO);
11587 const DataLayout &LoadDL = OldVal->getDataLayout();
11588 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11589
11590 OpenMPIRBuilder::AtomicInfo atomicInfo(
11591 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11592 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11593 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11594 BasicBlock *CurBB = Builder.GetInsertBlock();
11595 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11596 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11597 BasicBlock *ExitBB =
11598 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11599 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11600 X->getName() + ".atomic.cont");
11601 ContBB->getTerminator()->eraseFromParent();
11602 Builder.restoreIP(AllocaIP);
11603 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11604 NewAtomicAddr->setName(X->getName() + "x.new.val");
11605 Builder.SetInsertPoint(ContBB);
11606 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11607 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11608 Value *OldExprVal = PHI;
11609 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11610 if (!CBResult)
11611 return CBResult.takeError();
11612 Value *Upd = *CBResult;
11613 Builder.CreateStore(Upd, NewAtomicAddr);
11616 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11617 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11618 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11619 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11620 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11621 OldVal->eraseFromParent();
11622 Res.first = OldExprVal;
11623 Res.second = Upd;
11624
11625 if (UnreachableInst *ExitTI =
11627 CurBBTI->eraseFromParent();
11628 Builder.SetInsertPoint(ExitBB);
11629 } else {
11630 Builder.SetInsertPoint(ExitTI);
11631 }
11632 } else {
11633 IntegerType *IntCastTy =
11634 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11635 LoadInst *OldVal =
11636 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11638 OldVal->setAtomic(LoadAO);
11639 // CurBB
11640 // | /---\
11641 // ContBB |
11642 // | \---/
11643 // ExitBB
11644 BasicBlock *CurBB = Builder.GetInsertBlock();
11645 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11646 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11647 BasicBlock *ExitBB =
11648 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11649 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11650 X->getName() + ".atomic.cont");
11651 ContBB->getTerminator()->eraseFromParent();
11652 Builder.restoreIP(AllocaIP);
11653 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11654 NewAtomicAddr->setName(X->getName() + "x.new.val");
11655 Builder.SetInsertPoint(ContBB);
11656 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11657 PHI->addIncoming(OldVal, CurBB);
11658 bool IsIntTy = XElemTy->isIntegerTy();
11659 Value *OldExprVal = PHI;
11660 if (!IsIntTy) {
11661 if (XElemTy->isFloatingPointTy()) {
11662 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11663 X->getName() + ".atomic.fltCast");
11664 } else {
11665 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11666 X->getName() + ".atomic.ptrCast");
11667 }
11668 }
11669
11670 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11671 if (!CBResult)
11672 return CBResult.takeError();
11673 Value *Upd = *CBResult;
11674 Builder.CreateStore(Upd, NewAtomicAddr);
11675 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11678 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11679 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11680 Result->setVolatile(VolatileX);
11681 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11682 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11683 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11684 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11685
11686 Res.first = OldExprVal;
11687 Res.second = Upd;
11688
11689 // set Insertion point in exit block
11690 if (UnreachableInst *ExitTI =
11692 CurBBTI->eraseFromParent();
11693 Builder.SetInsertPoint(ExitBB);
11694 } else {
11695 Builder.SetInsertPoint(ExitTI);
11696 }
11697 }
11698
11699 return Res;
11700}
11701
11704 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11705 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11706 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11707 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11708 if (!updateToLocation(Loc))
11709 return Loc.IP;
11710
11711 LLVM_DEBUG({
11712 Type *XTy = X.Var->getType();
11713 assert(XTy->isPointerTy() &&
11714 "OMP Atomic expects a pointer to target memory");
11715 Type *XElemTy = X.ElemTy;
11716 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11717 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11718 "OMP atomic capture expected a scalar or struct type");
11719 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11720 "OpenMP atomic does not support LT or GT operations");
11721 });
11722
11723 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11724 // 'x' is simply atomically rewritten with 'expr'.
11725 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11726 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11727 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11728 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11729 if (!AtomicResult)
11730 return AtomicResult.takeError();
11731 Value *CapturedVal =
11732 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11733 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11734
11735 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11736 return Builder.saveIP();
11737}
11738
11742 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11743 bool IsFailOnly, bool IsWeak) {
11744
11746 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11747 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11748}
11749
11753 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11754 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11755
11756 if (!updateToLocation(Loc))
11757 return Loc.IP;
11758
11759 assert(X.Var->getType()->isPointerTy() &&
11760 "OMP atomic expects a pointer to target memory");
11761 // compare capture
11762 if (V.Var) {
11763 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11764 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11765 }
11766
11767 bool IsInteger = E->getType()->isIntegerTy();
11768
11769 if (Op == OMPAtomicCompareOp::EQ) {
11770 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11771 // R.Var handling.
11772 Value *OldValue = nullptr;
11773 Value *SuccessOrFail = nullptr;
11774
11775 if (!IsInteger && HandleFPNegZero) {
11776 // IEEE 754 special cases for cmpxchg (which is bitwise):
11777 // 1. -0.0 == +0.0 but they have different bit patterns.
11778 // 2. NaN != NaN but identical NaN bit patterns would match.
11779 //
11780 // CurBB:
11781 // %e_int = bitcast E to intN
11782 // %d_int = bitcast D to intN
11783 // %x_curr = load atomic intN, X
11784 // %x_fp = bitcast %x_curr to FP
11785 // %e_is_nan = fcmp uno E, E
11786 // %x_is_nan = fcmp uno %x_fp, %x_fp
11787 // %either_nan = or %e_is_nan, %x_is_nan
11788 // br %either_nan, NaNBB, NotNaNBB
11789 // NaNBB: ; NaN == anything is always false
11790 // br ExitBB
11791 // NotNaNBB:
11792 // %x_is_zero = fcmp oeq %x_fp, 0.0
11793 // %e_is_zero = fcmp oeq E, 0.0
11794 // %both_zero = and %x_is_zero, %e_is_zero
11795 // br %both_zero, ZeroBB, NormalBB
11796 // ZeroBB: ; both ±0.0 → x = d
11797 // cmpxchg X, %x_curr, %d_int
11798 // br ExitBB
11799 // NormalBB: ; original path
11800 // cmpxchg X, %e_int, %d_int
11801 // br ExitBB
11802 // ExitBB:
11803 // phi merge
11804 IntegerType *IntCastTy =
11805 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11806 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11807 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11808
11809 // Load X atomically.
11810 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11811 X.Var->getName() + ".atomic.load");
11813 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11814
11815 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11816 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11817 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11818 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11819 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11820
11821 BasicBlock *CurBB = Builder.GetInsertBlock();
11822 Function *F = CurBB->getParent();
11823 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11824 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11825 BasicBlock *ExitBB =
11826 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11828 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11829 BasicBlock *NotNaNBB = BasicBlock::Create(
11830 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11832 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11833 BasicBlock *NormalBB = BasicBlock::Create(
11834 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11835
11836 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11837 CurBB->getTerminator()->eraseFromParent();
11838 Builder.SetInsertPoint(CurBB);
11839 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11840
11841 // NaNBB: NaN == anything is always false; skip cmpxchg.
11842 Builder.SetInsertPoint(NaNBB);
11843 Builder.CreateBr(ExitBB);
11844
11845 // NotNaNBB: check both X and E for ±0.0.
11846 Builder.SetInsertPoint(NotNaNBB);
11847 Value *XIsZero =
11848 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11849 X.Var->getName() + ".atomic.xiszero");
11850 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11851 "atomic.e.iszero");
11852 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11853 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11854
11855 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11856 Builder.SetInsertPoint(ZeroBB);
11857 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11858 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11859 ResZero->setWeak(IsWeak);
11860 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11861 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11862 Builder.CreateBr(ExitBB);
11863
11864 // NormalBB: original bitwise cmpxchg.
11865 Builder.SetInsertPoint(NormalBB);
11866 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11867 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11868 ResNormal->setWeak(IsWeak);
11869 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11870 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11871 Builder.CreateBr(ExitBB);
11872
11873 // ExitBB: merge results from NaN, Zero, and Normal paths.
11874 Builder.SetInsertPoint(ExitBB->begin());
11875 PHINode *OldIntPHI =
11876 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11877 OldIntPHI->addIncoming(XCurr, NaNBB);
11878 OldIntPHI->addIncoming(OldZero, ZeroBB);
11879 OldIntPHI->addIncoming(OldNormal, NormalBB);
11880 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11881 X.Var->getName() + ".atomic.ok");
11882 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11883 SuccessPHI->addIncoming(OkZero, ZeroBB);
11884 SuccessPHI->addIncoming(OkNormal, NormalBB);
11885
11886 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11887 CurBBTI->eraseFromParent();
11888 Builder.SetInsertPoint(ExitBB);
11889 } else {
11890 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11891 }
11892
11893 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11894 X.Var->getName() + ".atomic.old.fp");
11895 SuccessOrFail = SuccessPHI;
11896 } else {
11897 AtomicCmpXchgInst *Result = nullptr;
11898 if (!IsInteger) {
11899 IntegerType *IntCastTy =
11900 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11901 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11902 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11903 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11904 MaybeAlign(), AO, Failure);
11905 } else {
11906 Result =
11907 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11908 }
11909 Result->setWeak(IsWeak);
11910
11911 if (V.Var) {
11912 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11913 if (!IsInteger)
11914 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11915 assert(OldValue->getType() == V.ElemTy &&
11916 "OldValue and V must be of same type");
11917 if (IsPostfixUpdate) {
11918 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11919 } else {
11920 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11921 if (IsFailOnly) {
11922 BasicBlock *CurBB = Builder.GetInsertBlock();
11923 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11924 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11925 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11926 CurBBTI, X.Var->getName() + ".atomic.exit");
11927 BasicBlock *ContBB = CurBB->splitBasicBlock(
11928 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11929 ContBB->getTerminator()->eraseFromParent();
11930 CurBB->getTerminator()->eraseFromParent();
11931
11932 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11933
11934 Builder.SetInsertPoint(ContBB);
11935 Builder.CreateStore(OldValue, V.Var);
11936 Builder.CreateBr(ExitBB);
11937
11938 if (UnreachableInst *ExitTI =
11940 CurBBTI->eraseFromParent();
11941 Builder.SetInsertPoint(ExitBB);
11942 } else {
11943 Builder.SetInsertPoint(ExitTI);
11944 }
11945 } else {
11946 Value *CapturedValue =
11947 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11948 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11949 }
11950 }
11951 }
11952 // The comparison result has to be stored.
11953 if (R.Var) {
11954 assert(R.Var->getType()->isPointerTy() &&
11955 "r.var must be of pointer type");
11956 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11957
11958 Value *SuccessFailureVal =
11959 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11960 Value *ResultCast =
11961 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11962 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11963 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11964 }
11965 }
11966
11967 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11968 // pre-computed OldValue and SuccessOrFail.
11969 if (HandleFPNegZero && !IsInteger) {
11970 if (V.Var) {
11971 assert(OldValue->getType() == V.ElemTy &&
11972 "OldValue and V must be of same type");
11973 if (IsPostfixUpdate) {
11974 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11975 } else {
11976 if (IsFailOnly) {
11977 BasicBlock *CurBB = Builder.GetInsertBlock();
11978 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11979 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11980 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11981 CurBBTI, X.Var->getName() + ".atomic.exit");
11982 BasicBlock *ContBB = CurBB->splitBasicBlock(
11983 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11984 ContBB->getTerminator()->eraseFromParent();
11985 CurBB->getTerminator()->eraseFromParent();
11986
11987 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11988
11989 Builder.SetInsertPoint(ContBB);
11990 Builder.CreateStore(OldValue, V.Var);
11991 Builder.CreateBr(ExitBB);
11992
11993 if (UnreachableInst *ExitTI =
11995 CurBBTI->eraseFromParent();
11996 Builder.SetInsertPoint(ExitBB);
11997 } else {
11998 Builder.SetInsertPoint(ExitTI);
11999 }
12000 } else {
12001 Value *CapturedValue =
12002 Builder.CreateSelect(SuccessOrFail, E, OldValue);
12003 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12004 }
12005 }
12006 }
12007 // The comparison result has to be stored.
12008 if (R.Var) {
12009 assert(R.Var->getType()->isPointerTy() &&
12010 "r.var must be of pointer type");
12011 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12012
12013 Value *ResultCast = R.IsSigned
12014 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
12015 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
12016 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12017 }
12018 }
12019 } else {
12020 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
12021 "Op should be either max or min at this point");
12022 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
12023
12024 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
12025 // Let's take max as example.
12026 // OpenMP form:
12027 // x = x > expr ? expr : x;
12028 // LLVM form:
12029 // *ptr = *ptr > val ? *ptr : val;
12030 // We need to transform to LLVM form.
12031 // x = x <= expr ? x : expr;
12033 if (IsXBinopExpr) {
12034 if (IsInteger) {
12035 if (X.IsSigned)
12036 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
12038 else
12039 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
12041 } else {
12042 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12044 }
12045 } else {
12046 if (IsInteger) {
12047 if (X.IsSigned)
12048 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12050 else
12051 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12053 } else {
12054 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12056 }
12057 }
12058
12059 AtomicRMWInst *OldValue =
12060 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12061 if (V.Var) {
12062 Value *CapturedValue = nullptr;
12063 if (IsPostfixUpdate) {
12064 CapturedValue = OldValue;
12065 } else {
12066 CmpInst::Predicate Pred;
12067 switch (NewOp) {
12068 case AtomicRMWInst::Max:
12069 Pred = CmpInst::ICMP_SGT;
12070 break;
12072 Pred = CmpInst::ICMP_UGT;
12073 break;
12075 Pred = CmpInst::FCMP_OGT;
12076 break;
12077 case AtomicRMWInst::Min:
12078 Pred = CmpInst::ICMP_SLT;
12079 break;
12081 Pred = CmpInst::ICMP_ULT;
12082 break;
12084 Pred = CmpInst::FCMP_OLT;
12085 break;
12086 default:
12087 llvm_unreachable("unexpected comparison op");
12088 }
12089 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12090 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12091 }
12092 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12093 }
12094 }
12095
12096 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12097
12098 return Builder.saveIP();
12099}
12100
12103 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12104 Value *NumTeamsUpper, Value *ThreadLimit,
12105 Value *IfExpr) {
12106 if (!updateToLocation(Loc))
12107 return InsertPointTy();
12108
12109 uint32_t SrcLocStrSize;
12110 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12111 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12112 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12113
12114 // Outer allocation basicblock is the entry block of the current function.
12115 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12116 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12117 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12118 Builder.SetInsertPoint(BodyBB->begin());
12119 }
12120
12121 // The current basic block is split into four basic blocks. After outlining,
12122 // they will be mapped as follows:
12123 // ```
12124 // def current_fn() {
12125 // current_basic_block:
12126 // br label %teams.exit
12127 // teams.exit:
12128 // ; instructions after teams
12129 // }
12130 //
12131 // def outlined_fn() {
12132 // teams.alloca:
12133 // br label %teams.body
12134 // teams.body:
12135 // ; instructions within teams body
12136 // }
12137 // ```
12138 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12139 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12140 BasicBlock *AllocaBB =
12141 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12142
12143 bool SubClausesPresent =
12144 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12145 // Push num_teams
12146 if (!Config.isTargetDevice() && SubClausesPresent) {
12147 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12148 "if lowerbound is non-null, then upperbound must also be non-null "
12149 "for bounds on num_teams");
12150
12151 if (NumTeamsUpper == nullptr)
12152 NumTeamsUpper = Builder.getInt32(0);
12153
12154 if (NumTeamsLower == nullptr)
12155 NumTeamsLower = NumTeamsUpper;
12156
12157 if (IfExpr) {
12158 assert(IfExpr->getType()->isIntegerTy() &&
12159 "argument to if clause must be an integer value");
12160
12161 // upper = ifexpr ? upper : 1
12162 if (IfExpr->getType() != Int1)
12163 IfExpr = Builder.CreateICmpNE(IfExpr,
12164 ConstantInt::get(IfExpr->getType(), 0));
12165 NumTeamsUpper = Builder.CreateSelect(
12166 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12167
12168 // lower = ifexpr ? lower : 1
12169 NumTeamsLower = Builder.CreateSelect(
12170 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12171 }
12172
12173 if (ThreadLimit == nullptr)
12174 ThreadLimit = Builder.getInt32(0);
12175
12176 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12177 // truncate or sign extend the passed values to match the int32 parameters.
12178 Value *NumTeamsLowerInt32 =
12179 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12180 Value *NumTeamsUpperInt32 =
12181 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12182 Value *ThreadLimitInt32 =
12183 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12184
12185 Value *ThreadNum = getOrCreateThreadID(Ident);
12186
12188 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12189 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12190 ThreadLimitInt32});
12191 }
12192 // Generate the body of teams.
12193 InsertPointTy AllocaIP(AllocaBB->begin());
12194 InsertPointTy CodeGenIP(BodyBB->begin());
12195 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12196 return Err;
12197
12198 auto OI = std::make_unique<OutlineInfo>();
12199 OI->EntryBB = AllocaBB;
12200 OI->ExitBB = ExitBB;
12201 OI->OuterAllocBB = &OuterAllocaBB;
12202
12203 // Insert fake values for global tid and bound tid.
12205 InsertPointTy OuterAllocaIP(OuterAllocaBB.begin());
12206 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12207 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12208 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12209 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12210
12211 auto HostPostOutlineCB = [this, Ident,
12212 ToBeDeleted](Function &OutlinedFn) mutable {
12213 // The stale call instruction will be replaced with a new call instruction
12214 // for runtime call with the outlined function.
12215
12216 assert(OutlinedFn.hasOneUse() &&
12217 "there must be a single user for the outlined function");
12218 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12219 ToBeDeleted.push_back(StaleCI);
12220
12221 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12222 "Outlined function must have two or three arguments only");
12223
12224 bool HasShared = OutlinedFn.arg_size() == 3;
12225
12226 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12227 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12228 if (HasShared)
12229 OutlinedFn.getArg(2)->setName("data");
12230
12231 // Call to the runtime function for teams in the current function.
12232 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12233 "outlined function.");
12234 Builder.SetInsertPoint(StaleCI);
12235 SmallVector<Value *> Args = {
12236 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12237 if (HasShared)
12238 Args.push_back(StaleCI->getArgOperand(2));
12241 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12242 Args);
12243
12244 Builder.ClearInsertionPoint();
12245 for (Instruction *I : llvm::reverse(ToBeDeleted))
12246 I->eraseFromParent();
12247 };
12248
12249 if (!Config.isTargetDevice())
12250 OI->PostOutlineCB = HostPostOutlineCB;
12251
12252 addOutlineInfo(std::move(OI));
12253
12254 Builder.SetInsertPoint(ExitBB);
12255
12256 return Builder.saveIP();
12257}
12258
12260 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12261 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12262 if (!updateToLocation(Loc))
12263 return InsertPointTy();
12264
12265 BasicBlock *OuterAllocaBB = OuterAllocIP.getNodeParent();
12266
12267 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12268 BasicBlock *BodyBB =
12269 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12270 Builder.SetInsertPoint(BodyBB->begin());
12271 }
12272 BasicBlock *ExitBB =
12273 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12274 BasicBlock *BodyBB =
12275 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12276 BasicBlock *AllocaBB =
12277 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12278
12279 // Generate the body of distribute clause
12280 InsertPointTy AllocaIP(AllocaBB->begin());
12281 InsertPointTy CodeGenIP(BodyBB->begin());
12282 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12283 return Err;
12284
12285 // When using target we use different runtime functions which require a
12286 // callback.
12287 if (Config.isTargetDevice()) {
12288 auto OI = std::make_unique<OutlineInfo>();
12289 OI->OuterAllocBB = OuterAllocIP.getNodeParent();
12290 OI->EntryBB = AllocaBB;
12291 OI->ExitBB = ExitBB;
12292 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12293 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12294
12295 addOutlineInfo(std::move(OI));
12296 }
12297 Builder.SetInsertPoint(ExitBB);
12298
12299 return Builder.saveIP();
12300}
12301
12304 std::string VarName) {
12305 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12307 Names.size()),
12308 Names);
12309 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12310 M, MapNamesArrayInit->getType(),
12311 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12312 VarName);
12313 return MapNamesArrayGlobal;
12314}
12315
12316// Create all simple and struct types exposed by the runtime and remember
12317// the llvm::PointerTypes of them for easy access later.
12318void OpenMPIRBuilder::initializeTypes(Module &M) {
12319 LLVMContext &Ctx = M.getContext();
12320 StructType *T;
12321 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12322 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12323#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12324#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12325 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12326 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12327#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12328 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12329 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12330#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12331 T = StructType::getTypeByName(Ctx, StructName); \
12332 if (!T) \
12333 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12334 VarName = T; \
12335 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12336#include "llvm/Frontend/OpenMP/OMPKinds.def"
12337}
12338
12341 SmallVectorImpl<BasicBlock *> &BlockVector) {
12343 BlockSet.insert(EntryBB);
12344 BlockSet.insert(ExitBB);
12345
12346 Worklist.push_back(EntryBB);
12347 while (!Worklist.empty()) {
12348 BasicBlock *BB = Worklist.pop_back_val();
12349 BlockVector.push_back(BB);
12350 for (BasicBlock *SuccBB : successors(BB))
12351 if (BlockSet.insert(SuccBB).second)
12352 Worklist.push_back(SuccBB);
12353 }
12354}
12355
12356std::unique_ptr<CodeExtractor>
12358 bool ArgsInZeroAddressSpace,
12359 Twine Suffix) {
12360 return std::make_unique<CodeExtractor>(
12361 Blocks, /* DominatorTree */ nullptr,
12362 /* AggregateArgs */ true,
12363 /* BlockFrequencyInfo */ nullptr,
12364 /* BranchProbabilityInfo */ nullptr,
12365 /* AssumptionCache */ nullptr,
12366 /* AllowVarArgs */ true,
12367 /* AllowAlloca */ true,
12368 /* AllocationBlock*/ OuterAllocBB,
12369 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12370 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12371}
12372
12373std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12374 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12375 return std::make_unique<DeviceSharedMemCodeExtractor>(
12376 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12377 /* AggregateArgs */ true,
12378 /* BlockFrequencyInfo */ nullptr,
12379 /* BranchProbabilityInfo */ nullptr,
12380 /* AssumptionCache */ nullptr,
12381 /* AllowVarArgs */ true,
12382 /* AllowAlloca */ true,
12383 /* AllocationBlock*/ OuterAllocBB,
12384 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12386 : OuterDeallocBBs,
12387 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12388}
12389
12391 uint64_t Size, int32_t Flags,
12393 StringRef Name) {
12394 if (!Config.isGPU()) {
12397 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12398 return;
12399 }
12400 // TODO: Add support for global variables on the device after declare target
12401 // support.
12402 Function *Fn = dyn_cast<Function>(Addr);
12403 if (!Fn)
12404 return;
12405
12406 // Add a function attribute for the kernel.
12407 Fn->addFnAttr("kernel");
12408 if (T.isAMDGCN())
12409 Fn->addFnAttr("uniform-work-group-size");
12410 Fn->addFnAttr(Attribute::MustProgress);
12411}
12412
12413// We only generate metadata for function that contain target regions.
12416
12417 // If there are no entries, we don't need to do anything.
12418 if (OffloadInfoManager.empty())
12419 return;
12420
12421 LLVMContext &C = M.getContext();
12424 16>
12425 OrderedEntries(OffloadInfoManager.size());
12426
12427 // Auxiliary methods to create metadata values and strings.
12428 auto &&GetMDInt = [this](unsigned V) {
12429 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12430 };
12431
12432 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12433
12434 // Create the offloading info metadata node.
12435 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12436 auto &&TargetRegionMetadataEmitter =
12437 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12438 const TargetRegionEntryInfo &EntryInfo,
12440 // Generate metadata for target regions. Each entry of this metadata
12441 // contains:
12442 // - Entry 0 -> Kind of this type of metadata (0).
12443 // - Entry 1 -> Device ID of the file where the entry was identified.
12444 // - Entry 2 -> File ID of the file where the entry was identified.
12445 // - Entry 3 -> Mangled name of the function where the entry was
12446 // identified.
12447 // - Entry 4 -> Line in the file where the entry was identified.
12448 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12449 // - Entry 6 -> Order the entry was created.
12450 // The first element of the metadata node is the kind.
12451 Metadata *Ops[] = {
12452 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12453 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12454 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12455 GetMDInt(E.getOrder())};
12456
12457 // Save this entry in the right position of the ordered entries array.
12458 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12459
12460 // Add metadata to the named metadata node.
12461 MD->addOperand(MDNode::get(C, Ops));
12462 };
12463
12464 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12465
12466 // Create function that emits metadata for each device global variable entry;
12467 auto &&DeviceGlobalVarMetadataEmitter =
12468 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12469 StringRef MangledName,
12471 // Generate metadata for global variables. Each entry of this metadata
12472 // contains:
12473 // - Entry 0 -> Kind of this type of metadata (1).
12474 // - Entry 1 -> Mangled name of the variable.
12475 // - Entry 2 -> Declare target kind.
12476 // - Entry 3 -> Order the entry was created.
12477 // The first element of the metadata node is the kind.
12478 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12479 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12480
12481 // Save this entry in the right position of the ordered entries array.
12482 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12483 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12484
12485 // Add metadata to the named metadata node.
12486 MD->addOperand(MDNode::get(C, Ops));
12487 };
12488
12489 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12490 DeviceGlobalVarMetadataEmitter);
12491
12492 for (const auto &E : OrderedEntries) {
12493 assert(E.first && "All ordered entries must exist!");
12494 if (const auto *CE =
12496 E.first)) {
12497 if (!CE->getID() || !CE->getAddress()) {
12498 // Do not blame the entry if the parent funtion is not emitted.
12499 TargetRegionEntryInfo EntryInfo = E.second;
12500 StringRef FnName = EntryInfo.ParentName;
12501 if (!M.getNamedValue(FnName))
12502 continue;
12503 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12504 continue;
12505 }
12506 createOffloadEntry(CE->getID(), CE->getAddress(),
12507 /*Size=*/0, CE->getFlags(),
12509 } else if (const auto *CE = dyn_cast<
12511 E.first)) {
12514 CE->getFlags());
12515 switch (Flags) {
12518 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12519 continue;
12520 if (!CE->getAddress()) {
12521 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12522 continue;
12523 }
12524 // The vaiable has no definition - no need to add the entry.
12525 if (CE->getVarSize() == 0)
12526 continue;
12527 break;
12529 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12530 (!Config.isTargetDevice() && CE->getAddress())) &&
12531 "Declaret target link address is set.");
12532 if (Config.isTargetDevice())
12533 continue;
12534 if (!CE->getAddress()) {
12536 continue;
12537 }
12538 break;
12541 if (!CE->getAddress()) {
12542 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12543 continue;
12544 }
12545 break;
12546 default:
12547 break;
12548 }
12549
12550 // Hidden or internal symbols on the device are not externally visible.
12551 // We should not attempt to register them by creating an offloading
12552 // entry. Indirect variables are handled separately on the device.
12553 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12554 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12555 (Flags !=
12557 Flags != OffloadEntriesInfoManager::
12558 OMPTargetGlobalVarEntryIndirectVTable))
12559 continue;
12560
12561 // Indirect globals need to use a special name that doesn't match the name
12562 // of the associated host global.
12564 Flags ==
12566 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12567 Flags, CE->getLinkage(), CE->getVarName());
12568 else
12569 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12570 Flags, CE->getLinkage());
12571
12572 } else {
12573 llvm_unreachable("Unsupported entry kind.");
12574 }
12575 }
12576
12577 // Emit requires directive globals to a special entry so the runtime can
12578 // register them when the device image is loaded.
12579 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12580 // entries should be redesigned to better suit this use-case.
12581 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12585 ".requires", /*Size=*/0,
12587 Config.getRequiresFlags());
12588}
12589
12592 unsigned FileID, unsigned Line, unsigned Count) {
12593 raw_svector_ostream OS(Name);
12594 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12595 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12596 if (Count)
12597 OS << "_" << Count;
12598}
12599
12601 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12602 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12604 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12605 EntryInfo.Line, NewCount);
12606}
12607
12610 vfs::FileSystem &VFS,
12611 StringRef ParentName) {
12612 sys::fs::UniqueID ID(0xdeadf17e, 0);
12613 auto FileIDInfo = CallBack();
12614 uint64_t FileID = 0;
12615 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12616 ID = Status->getUniqueID();
12617 FileID = Status->getUniqueID().getFile();
12618 } else {
12619 // If the inode ID could not be determined, create a hash value
12620 // the current file name and use that as an ID.
12621 FileID = hash_value(std::get<0>(FileIDInfo));
12622 }
12623
12624 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12625 std::get<1>(FileIDInfo));
12626}
12627
12629 unsigned Offset = 0;
12630 for (uint64_t Remain =
12631 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12633 !(Remain & 1); Remain = Remain >> 1)
12634 Offset++;
12635 return Offset;
12636}
12637
12640 // Rotate by getFlagMemberOffset() bits.
12641 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12642 << getFlagMemberOffset());
12643}
12644
12647 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12648 // If the entry is PTR_AND_OBJ but has not been marked with the special
12649 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12650 // marked as MEMBER_OF.
12651 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12653 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12656 return;
12657
12658 // Entries with ATTACH are not members-of anything. They are handled
12659 // separately by the runtime after other maps have been handled.
12660 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12662 return;
12663
12664 // Reset the placeholder value to prepare the flag for the assignment of the
12665 // proper MEMBER_OF value.
12666 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12667 Flags |= MemberOfFlag;
12668}
12669
12673 bool IsDeclaration, bool IsExternallyVisible,
12674 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12675 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12676 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12677 std::function<Constant *()> GlobalInitializer,
12678 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12679 // TODO: convert this to utilise the IRBuilder Config rather than
12680 // a passed down argument.
12681 if (OpenMPSIMD)
12682 return nullptr;
12683
12686 CaptureClause ==
12688 Config.hasRequiresUnifiedSharedMemory())) {
12689 SmallString<64> PtrName;
12690 {
12691 raw_svector_ostream OS(PtrName);
12692 OS << MangledName;
12693 if (!IsExternallyVisible)
12694 OS << format("_%x", EntryInfo.FileID);
12695 OS << "_decl_tgt_ref_ptr";
12696 }
12697
12698 Value *Ptr = M.getNamedValue(PtrName);
12699
12700 if (!Ptr) {
12701 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12702 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12703
12704 auto *GV = cast<GlobalVariable>(Ptr);
12705 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12706
12707 if (!Config.isTargetDevice()) {
12708 if (GlobalInitializer)
12709 GV->setInitializer(GlobalInitializer());
12710 else
12711 GV->setInitializer(GlobalValue);
12712 }
12713
12715 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12716 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12717 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12718 }
12719
12720 return cast<Constant>(Ptr);
12721 }
12722
12723 return nullptr;
12724}
12725
12729 bool IsDeclaration, bool IsExternallyVisible,
12730 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12731 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12732 std::vector<Triple> TargetTriple,
12733 std::function<Constant *()> GlobalInitializer,
12734 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12735 Constant *Addr) {
12737 (TargetTriple.empty() && !Config.isTargetDevice()))
12738 return;
12739
12741 StringRef VarName;
12742 int64_t VarSize;
12744
12746 CaptureClause ==
12748 !Config.hasRequiresUnifiedSharedMemory()) {
12750 VarName = MangledName;
12751 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12752
12753 if (!IsDeclaration)
12754 VarSize = divideCeil(
12755 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12756 else
12757 VarSize = 0;
12758 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12759
12760 // This is a workaround carried over from Clang which prevents undesired
12761 // optimisation of internal variables.
12762 if (Config.isTargetDevice() &&
12763 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12764 // Do not create a "ref-variable" if the original is not also available
12765 // on the host.
12766 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12767 return;
12768
12769 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12770
12771 if (!M.getNamedValue(RefName)) {
12772 Constant *AddrRef =
12773 getOrCreateInternalVariable(Addr->getType(), RefName);
12774 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12775 GvAddrRef->setConstant(true);
12776 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12777 GvAddrRef->setInitializer(Addr);
12778 GeneratedRefs.push_back(GvAddrRef);
12779 }
12780 }
12781 } else {
12784 else
12786
12787 if (Config.isTargetDevice()) {
12788 VarName = (Addr) ? Addr->getName() : "";
12789 Addr = nullptr;
12790 } else {
12792 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12793 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12794 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12795 VarName = (Addr) ? Addr->getName() : "";
12796 }
12797 VarSize = M.getDataLayout().getPointerSize();
12799 }
12800
12801 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12802 Flags, Linkage);
12803}
12804
12805/// Loads all the offload entries information from the host IR
12806/// metadata.
12808 // If we are in target mode, load the metadata from the host IR. This code has
12809 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12810
12811 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12812 if (!MD)
12813 return;
12814
12815 for (MDNode *MN : MD->operands()) {
12816 auto &&GetMDInt = [MN](unsigned Idx) {
12817 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12818 return cast<ConstantInt>(V->getValue())->getZExtValue();
12819 };
12820
12821 auto &&GetMDString = [MN](unsigned Idx) {
12822 auto *V = cast<MDString>(MN->getOperand(Idx));
12823 return V->getString();
12824 };
12825
12826 switch (GetMDInt(0)) {
12827 default:
12828 llvm_unreachable("Unexpected metadata!");
12829 break;
12830 case OffloadEntriesInfoManager::OffloadEntryInfo::
12831 OffloadingEntryInfoTargetRegion: {
12832 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12833 /*DeviceID=*/GetMDInt(1),
12834 /*FileID=*/GetMDInt(2),
12835 /*Line=*/GetMDInt(4),
12836 /*Count=*/GetMDInt(5));
12837 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12838 /*Order=*/GetMDInt(6));
12839 break;
12840 }
12841 case OffloadEntriesInfoManager::OffloadEntryInfo::
12842 OffloadingEntryInfoDeviceGlobalVar:
12843 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12844 /*MangledName=*/GetMDString(1),
12846 /*Flags=*/GetMDInt(2)),
12847 /*Order=*/GetMDInt(3));
12848 break;
12849 }
12850 }
12851}
12852
12854 StringRef HostFilePath) {
12855 if (HostFilePath.empty())
12856 return;
12857
12858 auto Buf = VFS.getBufferForFile(HostFilePath);
12859 if (std::error_code Err = Buf.getError()) {
12860 report_fatal_error(("error opening host file from host file path inside of "
12861 "OpenMPIRBuilder: " +
12862 Err.message())
12863 .c_str());
12864 }
12865
12866 LLVMContext Ctx;
12868 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12869 if (std::error_code Err = M.getError()) {
12871 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12872 .c_str());
12873 }
12874
12875 loadOffloadInfoMetadata(*M.get());
12876}
12877
12880 llvm::StringRef Name) {
12881 Builder.restoreIP(Loc.IP);
12882
12883 BasicBlock *CurBB = Builder.GetInsertBlock();
12884 assert(CurBB &&
12885 "expected a valid insertion block for creating an iterator loop");
12886 Function *F = CurBB->getParent();
12887
12888 InsertPointTy SplitIP = Builder.saveIP();
12889 if (SplitIP == CurBB->end())
12890 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12891 SplitIP = Terminator->getIterator();
12892
12893 BasicBlock *ContBB =
12894 splitBB(SplitIP, /*CreateBranch=*/false,
12895 Builder.getCurrentDebugLocation(), "omp.it.cont");
12896
12897 CanonicalLoopInfo *CLI =
12898 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12899 /*PreInsertBefore=*/ContBB,
12900 /*PostInsertBefore=*/ContBB, Name);
12901
12902 // Enter loop from original block.
12903 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12904
12905 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12906 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12907 T->eraseFromParent();
12908
12909 InsertPointTy BodyIP = CLI->getBodyIP();
12910 // Blocks that exist before BodyGen, other than the body and latch, are
12911 // outside the loop body.
12912 SmallPtrSet<BasicBlock *, 32> ExistingBlocks;
12913 for (BasicBlock &Block : *F)
12914 ExistingBlocks.insert(&Block);
12915 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12916 return Err;
12917
12918 // The body may span several blocks. Branch its single unterminated block to
12919 // the latch; otherwise some block must already branch there.
12920 BasicBlock *Latch = CLI->getLatch();
12921 BasicBlock *OpenBB = nullptr;
12922 bool ReachesLatch = false;
12923 SmallVector<BasicBlock *> Worklist{CLI->getBody()};
12924 SmallPtrSet<BasicBlock *, 8> Visited{CLI->getBody()};
12925 while (!Worklist.empty()) {
12926 BasicBlock *BB = Worklist.pop_back_val();
12927 if (!BB->hasTerminator()) {
12928 if (OpenBB)
12930 "iterator bodygen must leave at most one unterminated block",
12932 OpenBB = BB;
12933 continue;
12934 }
12935 for (BasicBlock *Succ : successors(BB)) {
12936 if (Succ == Latch) {
12937 ReachesLatch = true;
12938 continue;
12939 }
12940 if (Succ != CLI->getBody() && ExistingBlocks.contains(Succ))
12942 "iterator bodygen must not branch out of the loop body",
12944 if (Visited.insert(Succ).second)
12945 Worklist.push_back(Succ);
12946 }
12947 }
12948
12949 if (OpenBB) {
12950 Builder.SetInsertPoint(OpenBB);
12951 Builder.CreateBr(Latch);
12952 } else if (!ReachesLatch) {
12953 return make_error<StringError>("iterator bodygen must reach the loop latch",
12955 }
12956
12957 // Link After -> ContBB
12958 Builder.SetInsertPoint(CLI->getAfter()->begin());
12959 if (!CLI->getAfter()->hasTerminator())
12960 Builder.CreateBr(ContBB);
12961
12962 return ContBB->begin();
12963}
12964
12965/// Mangle the parameter part of the vector function name according to
12966/// their OpenMP classification. The mangling function is defined in
12967/// section 4.5 of the AAVFABI(2021Q1).
12968static std::string mangleVectorParameters(
12970 SmallString<256> Buffer;
12971 llvm::raw_svector_ostream Out(Buffer);
12972 for (const auto &ParamAttr : ParamAttrs) {
12973 switch (ParamAttr.Kind) {
12975 Out << 'l';
12976 break;
12978 Out << 'R';
12979 break;
12981 Out << 'U';
12982 break;
12984 Out << 'L';
12985 break;
12987 Out << 'u';
12988 break;
12990 Out << 'v';
12991 break;
12992 }
12993 if (ParamAttr.HasVarStride)
12994 Out << "s" << ParamAttr.StrideOrArg;
12995 else if (ParamAttr.Kind ==
12997 ParamAttr.Kind ==
12999 ParamAttr.Kind ==
13001 ParamAttr.Kind ==
13003 // Don't print the step value if it is not present or if it is
13004 // equal to 1.
13005 if (ParamAttr.StrideOrArg < 0)
13006 Out << 'n' << -ParamAttr.StrideOrArg;
13007 else if (ParamAttr.StrideOrArg != 1)
13008 Out << ParamAttr.StrideOrArg;
13009 }
13010
13011 if (!!ParamAttr.Alignment)
13012 Out << 'a' << ParamAttr.Alignment;
13013 }
13014
13015 return std::string(Out.str());
13016}
13017
13019 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
13021 struct ISADataTy {
13022 char ISA;
13023 unsigned VecRegSize;
13024 };
13025 ISADataTy ISAData[] = {
13026 {'b', 128}, // SSE
13027 {'c', 256}, // AVX
13028 {'d', 256}, // AVX2
13029 {'e', 512}, // AVX512
13030 };
13032 switch (Branch) {
13034 Masked.push_back('N');
13035 Masked.push_back('M');
13036 break;
13038 Masked.push_back('N');
13039 break;
13041 Masked.push_back('M');
13042 break;
13043 }
13044 for (char Mask : Masked) {
13045 for (const ISADataTy &Data : ISAData) {
13047 llvm::raw_svector_ostream Out(Buffer);
13048 Out << "_ZGV" << Data.ISA << Mask;
13049 if (!VLENVal) {
13050 assert(NumElts && "Non-zero simdlen/cdtsize expected");
13051 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
13052 } else {
13053 Out << VLENVal;
13054 }
13055 Out << mangleVectorParameters(ParamAttrs);
13056 Out << '_' << Fn->getName();
13057 Fn->addFnAttr(Out.str());
13058 }
13059 }
13060}
13061
13062// Function used to add the attribute. The parameter `VLEN` is templated to
13063// allow the use of `x` when targeting scalable functions for SVE.
13064template <typename T>
13065static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
13066 char ISA, StringRef ParSeq,
13067 StringRef MangledName, bool OutputBecomesInput,
13068 llvm::Function *Fn) {
13069 SmallString<256> Buffer;
13070 llvm::raw_svector_ostream Out(Buffer);
13071 Out << Prefix << ISA << LMask << VLEN;
13072 if (OutputBecomesInput)
13073 Out << 'v';
13074 Out << ParSeq << '_' << MangledName;
13075 Fn->addFnAttr(Out.str());
13076}
13077
13078// Helper function to generate the Advanced SIMD names depending on the value
13079// of the NDS when simdlen is not present.
13080static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13081 StringRef Prefix, char ISA,
13082 StringRef ParSeq, StringRef MangledName,
13083 bool OutputBecomesInput,
13084 llvm::Function *Fn) {
13085 switch (NDS) {
13086 case 8:
13087 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13088 OutputBecomesInput, Fn);
13089 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13090 OutputBecomesInput, Fn);
13091 break;
13092 case 16:
13093 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13094 OutputBecomesInput, Fn);
13095 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13096 OutputBecomesInput, Fn);
13097 break;
13098 case 32:
13099 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13100 OutputBecomesInput, Fn);
13101 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13102 OutputBecomesInput, Fn);
13103 break;
13104 case 64:
13105 case 128:
13106 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13107 OutputBecomesInput, Fn);
13108 break;
13109 default:
13110 llvm_unreachable("Scalar type is too wide.");
13111 }
13112}
13113
13114/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13116 llvm::Function *Fn, unsigned UserVLEN,
13118 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13119 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13120
13121 // Sort out parameter sequence.
13122 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13123 StringRef Prefix = "_ZGV";
13124 StringRef MangledName = Fn->getName();
13125
13126 // Generate simdlen from user input (if any).
13127 if (UserVLEN) {
13128 if (ISA == 's') {
13129 // SVE generates only a masked function.
13130 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13131 OutputBecomesInput, Fn);
13132 return;
13133 }
13134
13135 switch (Branch) {
13137 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13138 OutputBecomesInput, Fn);
13139 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13140 OutputBecomesInput, Fn);
13141 break;
13143 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13144 OutputBecomesInput, Fn);
13145 break;
13147 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13148 OutputBecomesInput, Fn);
13149 break;
13150 }
13151 return;
13152 }
13153
13154 if (ISA == 's') {
13155 // SVE, section 3.4.1, item 1.
13156 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13157 OutputBecomesInput, Fn);
13158 return;
13159 }
13160
13161 switch (Branch) {
13163 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13164 MangledName, OutputBecomesInput, Fn);
13165 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13166 MangledName, OutputBecomesInput, Fn);
13167 break;
13169 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13170 MangledName, OutputBecomesInput, Fn);
13171 break;
13173 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13174 MangledName, OutputBecomesInput, Fn);
13175 break;
13176 }
13177}
13178
13179//===----------------------------------------------------------------------===//
13180// OffloadEntriesInfoManager
13181//===----------------------------------------------------------------------===//
13182
13184 return OffloadEntriesTargetRegion.empty() &&
13185 OffloadEntriesDeviceGlobalVar.empty();
13186}
13187
13188unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13189 const TargetRegionEntryInfo &EntryInfo) const {
13190 auto It = OffloadEntriesTargetRegionCount.find(
13191 getTargetRegionEntryCountKey(EntryInfo));
13192 if (It == OffloadEntriesTargetRegionCount.end())
13193 return 0;
13194 return It->second;
13195}
13196
13197void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13198 const TargetRegionEntryInfo &EntryInfo) {
13199 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13200 EntryInfo.Count + 1;
13201}
13202
13203/// Initialize target region entry.
13205 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13206 OffloadEntriesTargetRegion[EntryInfo] =
13207 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13209 ++OffloadingEntriesNum;
13210}
13211
13213 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13215 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13216
13217 // Update the EntryInfo with the next available count for this location.
13218 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13219
13220 // If we are emitting code for a target, the entry is already initialized,
13221 // only has to be registered.
13222 if (OMPBuilder->Config.isTargetDevice()) {
13223 // This could happen if the device compilation is invoked standalone.
13224 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13225 return;
13226 }
13227 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13228 Entry.setAddress(Addr);
13229 Entry.setID(ID);
13230 Entry.setFlags(Flags);
13231 } else {
13233 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13234 return;
13235 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13236 "Target region entry already registered!");
13237 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13238 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13239 ++OffloadingEntriesNum;
13240 }
13241 incrementTargetRegionEntryInfoCount(EntryInfo);
13242}
13243
13245 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13246
13247 // Update the EntryInfo with the next available count for this location.
13248 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13249
13250 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13251 if (It == OffloadEntriesTargetRegion.end()) {
13252 return false;
13253 }
13254 // Fail if this entry is already registered.
13255 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13256 return false;
13257 return true;
13258}
13259
13261 const OffloadTargetRegionEntryInfoActTy &Action) {
13262 // Scan all target region entries and perform the provided action.
13263 for (const auto &It : OffloadEntriesTargetRegion) {
13264 Action(It.first, It.second);
13265 }
13266}
13267
13269 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13270 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13271 ++OffloadingEntriesNum;
13272}
13273
13275 StringRef VarName, Constant *Addr, int64_t VarSize,
13277 if (OMPBuilder->Config.isTargetDevice()) {
13278 // This could happen if the device compilation is invoked standalone.
13279 if (!hasDeviceGlobalVarEntryInfo(VarName))
13280 return;
13281 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13282 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13283 if (Entry.getVarSize() == 0) {
13284 Entry.setVarSize(VarSize);
13285 Entry.setLinkage(Linkage);
13286 }
13287 return;
13288 }
13289 Entry.setVarSize(VarSize);
13290 Entry.setLinkage(Linkage);
13291 Entry.setAddress(Addr);
13292 } else {
13293 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13294 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13295 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13296 "Entry not initialized!");
13297 if (Entry.getVarSize() == 0) {
13298 Entry.setVarSize(VarSize);
13299 Entry.setLinkage(Linkage);
13300 }
13301 return;
13302 }
13304 Flags ==
13306 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13307 Addr, VarSize, Flags, Linkage,
13308 VarName.str());
13309 else
13310 OffloadEntriesDeviceGlobalVar.try_emplace(
13311 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13312 ++OffloadingEntriesNum;
13313 }
13314}
13315
13318 // Scan all target region entries and perform the provided action.
13319 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13320 Action(E.getKey(), E.getValue());
13321}
13322
13323//===----------------------------------------------------------------------===//
13324// CanonicalLoopInfo
13325//===----------------------------------------------------------------------===//
13326
13327void CanonicalLoopInfo::collectControlBlocks(
13329 // We only count those BBs as control block for which we do not need to
13330 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13331 // flow. For consistency, this also means we do not add the Body block, which
13332 // is just the entry to the body code.
13333 BBs.reserve(BBs.size() + 6);
13334 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13335}
13336
13338 assert(isValid() && "Requires a valid canonical loop");
13339 for (BasicBlock *Pred : predecessors(Header)) {
13340 if (Pred != Latch)
13341 return Pred;
13342 }
13343 llvm_unreachable("Missing preheader");
13344}
13345
13346void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13347 assert(isValid() && "Requires a valid canonical loop");
13348
13349 Instruction *CmpI = &getCond()->front();
13350 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13351 CmpI->setOperand(1, TripCount);
13352
13353#ifndef NDEBUG
13354 assertOK();
13355#endif
13356}
13357
13358void CanonicalLoopInfo::mapIndVar(
13359 llvm::function_ref<Value *(Instruction *)> Updater) {
13360 assert(isValid() && "Requires a valid canonical loop");
13361
13362 Instruction *OldIV = getIndVar();
13363
13364 // Record all uses excluding those introduced by the updater. Uses by the
13365 // CanonicalLoopInfo itself to keep track of the number of iterations are
13366 // excluded.
13367 SmallVector<Use *> ReplacableUses;
13368 for (Use &U : OldIV->uses()) {
13369 auto *User = dyn_cast<Instruction>(U.getUser());
13370 if (!User)
13371 continue;
13372 if (User->getParent() == getCond())
13373 continue;
13374 if (User->getParent() == getLatch())
13375 continue;
13376 ReplacableUses.push_back(&U);
13377 }
13378
13379 // Run the updater that may introduce new uses
13380 Value *NewIV = Updater(OldIV);
13381
13382 // Replace the old uses with the value returned by the updater.
13383 for (Use *U : ReplacableUses)
13384 U->set(NewIV);
13385
13386#ifndef NDEBUG
13387 assertOK();
13388#endif
13389}
13390
13392#ifndef NDEBUG
13393 // No constraints if this object currently does not describe a loop.
13394 if (!isValid())
13395 return;
13396
13397 BasicBlock *Preheader = getPreheader();
13398 BasicBlock *Body = getBody();
13399 BasicBlock *After = getAfter();
13400
13401 // Verify standard control-flow we use for OpenMP loops.
13402 assert(Preheader);
13403 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13404 "Preheader must terminate with unconditional branch");
13405 assert(Preheader->getSingleSuccessor() == Header &&
13406 "Preheader must jump to header");
13407
13408 assert(Header);
13409 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13410 "Header must terminate with unconditional branch");
13411 assert(Header->getSingleSuccessor() == Cond &&
13412 "Header must jump to exiting block");
13413
13414 assert(Cond);
13415 assert(Cond->getSinglePredecessor() == Header &&
13416 "Exiting block only reachable from header");
13417
13418 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13419 "Exiting block must terminate with conditional branch");
13420 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13421 "Exiting block's first successor jump to the body");
13422 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13423 "Exiting block's second successor must exit the loop");
13424
13425 assert(Body);
13426 assert(Body->getSinglePredecessor() == Cond &&
13427 "Body only reachable from exiting block");
13428 assert(!isa<PHINode>(Body->front()));
13429
13430 assert(Latch);
13431 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13432 "Latch must terminate with unconditional branch");
13433 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13434 // TODO: To support simple redirecting of the end of the body code that has
13435 // multiple; introduce another auxiliary basic block like preheader and after.
13436 assert(Latch->getSinglePredecessor() != nullptr);
13437 assert(!isa<PHINode>(Latch->front()));
13438
13439 assert(Exit);
13440 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13441 "Exit block must terminate with unconditional branch");
13442 assert(Exit->getSingleSuccessor() == After &&
13443 "Exit block must jump to after block");
13444
13445 assert(After);
13446 assert(After->getSinglePredecessor() == Exit &&
13447 "After block only reachable from exit block");
13448 assert(After->empty() || !isa<PHINode>(After->front()));
13449
13450 Instruction *IndVar = getIndVar();
13451 assert(IndVar && "Canonical induction variable not found?");
13452 assert(isa<IntegerType>(IndVar->getType()) &&
13453 "Induction variable must be an integer");
13454 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13455 "Induction variable must be a PHI in the loop header");
13456 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13457 assert(
13458 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13459 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13460
13461 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13462 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13463 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13464 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13465 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13466 ->isOne());
13467
13468 Value *TripCount = getTripCount();
13469 assert(TripCount && "Loop trip count not found?");
13470 assert(IndVar->getType() == TripCount->getType() &&
13471 "Trip count and induction variable must have the same type");
13472
13473 auto *CmpI = cast<CmpInst>(&Cond->front());
13474 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13475 "Exit condition must be a signed less-than comparison");
13476 assert(CmpI->getOperand(0) == IndVar &&
13477 "Exit condition must compare the induction variable");
13478 assert(CmpI->getOperand(1) == TripCount &&
13479 "Exit condition must compare with the trip count");
13480#endif
13481}
13482
13484 Header = nullptr;
13485 Cond = nullptr;
13486 Latch = nullptr;
13487 Exit = nullptr;
13488}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
Rewrite undef for PHI
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static cl::opt< ITMode > IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT), cl::values(clEnumValN(DefaultIT, "arm-default-it", "Generate any type of IT block"), clEnumValN(RestrictedIT, "arm-restrict-it", "Disallow complex IT blocks")))
Expand Atomic instructions
@ ParamAttr
This file contains the simple types necessary to represent the attributes associated with functions a...
static const Function * getParent(const Value *V)
#define X(NUM, ENUM, NAME)
Definition ELF.h:857
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Hexagon Common GEP
Hexagon Hardware Loops
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
This header defines various interfaces for pass management in LLVM.
iv Induction Variable Users
Definition IVUsers.cpp:48
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
static LVOptions Options
Definition LVOptions.cpp:25
static bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
Definition Lint.cpp:540
#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 void emitTargetCall(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride, 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 * removeASCastIfPresent(Value *V)
static void createTargetLoopWorkshareCall(OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType, BasicBlock *InsertBlock, Value *Ident, Value *LoopBodyArg, Value *TripCount, Function &LoopBodyFn, bool NoLoop)
Value * createFakeIntVal(IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy OuterAllocaIP, llvm::SmallVectorImpl< Instruction * > &ToBeDeleted, OpenMPIRBuilder::InsertPointTy InnerAllocaIP, const Twine &Name="", bool AsPtr=true, bool Is64Bit=false)
static Function * createTargetParallelWrapper(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn)
Create wrapper function used to gather the outlined function's argument structure from a shared buffe...
static void redirectTo(BasicBlock *Source, BasicBlock *Target, DebugLoc DL)
Make Source branch to Target.
static FunctionCallee getKmpcDistForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void applyParallelAccessesMetadata(CanonicalLoopInfo *CLI, LLVMContext &Ctx, Loop *Loop, LoopInfo &LoopInfo, SmallVector< Metadata * > &LoopMDList)
static Expected< Function * > createOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, StringRef FuncName, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB, DebugLoc OutlinedFnLoc)
static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static FunctionCallee getKmpcForDynamicFiniForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for finalizing the dynamic loop using depending on type.
static void FixupDebugInfoForOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func, DenseMap< Value *, std::tuple< Value *, unsigned > > &ValueReplacementMap)
static OMPScheduleType getOpenMPOrderingScheduleType(OMPScheduleType BaseScheduleType, bool HasOrderedClause)
Adds ordering modifier flags to schedule type.
static OMPScheduleType getOpenMPMonotonicityScheduleType(OMPScheduleType ScheduleType, bool HasSimdModifier, bool HasMonotonic, bool HasNonmonotonic, bool HasOrderedClause)
Adds monotonicity modifier flags to schedule type.
static std::string mangleVectorParameters(ArrayRef< llvm::OpenMPIRBuilder::DeclareSimdAttrTy > ParamAttrs)
Mangle the parameter part of the vector function name according to their OpenMP classification.
static bool isGenericKernel(Function &Fn)
static void workshareLoopTargetCallback(OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident, Function &OutlinedFn, const SmallVector< Instruction *, 4 > &ToBeDeleted, WorksharingLoopType LoopType, bool NoLoop)
static bool isValidWorkshareLoopScheduleType(OMPScheduleType SchedType)
static bool isAtomicableReductionSet(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos)
static llvm::CallInst * emitNoUnwindRuntimeCall(IRBuilder<> &Builder, llvm::FunctionCallee Callee, ArrayRef< llvm::Value * > Args, const llvm::Twine &Name)
static Error populateReductionFunction(Function *ReductionFunc, ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, IRBuilder<> &Builder, ArrayRef< bool > IsByRef, bool IsGPU)
static Function * getFreshReductionFunc(Module &M)
static void raiseUserConstantDataAllocasToEntryBlock(IRBuilderBase &Builder, Function *Function)
static FunctionCallee getKmpcForDynamicNextForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for updating the next loop using OpenMP dynamic scheduling depending...
static bool isConflictIP(IRBuilder<>::InsertPoint IP1, IRBuilder<>::InsertPoint IP2)
Return whether IP1 and IP2 are ambiguous, i.e.
static void checkReductionInfos(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, bool IsGPU)
static Type * getOffloadingArrayType(Value *V)
static OMPScheduleType getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasDistScheduleChunks)
Determine which scheduling algorithm to use, determined from schedule clause arguments.
static OMPScheduleType computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasMonotonicModifier, bool HasNonmonotonicModifier, bool HasOrderedClause, bool HasDistScheduleChunks)
Determine the schedule type using schedule and ordering clause arguments.
static FunctionCallee getKmpcForDynamicInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for initializing loop bounds using OpenMP dynamic scheduling dependi...
static std::optional< omp::OMPTgtExecModeFlags > getTargetKernelExecMode(Function &Kernel)
Given a function, if it represents the entry point of a target kernel, this returns the execution mod...
static StructType * createTaskWithPrivatesTy(OpenMPIRBuilder &OMPIRBuilder, ArrayRef< Value * > OffloadingArraysToPrivatize)
static cl::opt< double > UnrollThresholdFactor("openmp-ir-builder-unroll-threshold-factor", cl::Hidden, cl::desc("Factor for the unroll threshold to account for code " "simplifications still taking place"), cl::init(1.5))
static cl::opt< bool > UseDefaultMaxThreads("openmp-ir-builder-use-default-max-threads", cl::Hidden, cl::desc("Use a default max threads if none is provided."), cl::init(true))
static int32_t computeHeuristicUnrollFactor(CanonicalLoopInfo *CLI)
Heuristically determine the best-performant unroll factor for CLI.
static Error emitTargetOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry, TargetRegionEntryInfo &EntryInfo, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, Function *&OutlinedFn, Constant *&OutlinedFnID, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB, DebugLoc OutlinedFnLoc)
static Value * emitTaskDependencies(OpenMPIRBuilder &OMPBuilder, const SmallVectorImpl< OpenMPIRBuilder::DependData > &Dependencies)
static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value, bool Min)
static OpenMPIRBuilder::InsertPointTy getInsertPointAfterInstr(Instruction *I)
static void redirectAllPredecessorsTo(BasicBlock *OldTarget, BasicBlock *NewTarget, DebugLoc DL)
Redirect all edges that branch to OldTarget to NewTarget.
static void hoistNonEntryAllocasToEntryBlock(llvm::BasicBlock &Block)
static std::unique_ptr< TargetMachine > createTargetMachine(Function *F, CodeGenOptLevel OptLevel)
Create the TargetMachine object to query the backend for optimization preferences.
static FunctionCallee getKmpcForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup, LoopInfo &LI)
Attach llvm.access.group metadata to the memref instructions of Block.
static void addBasicBlockMetadata(BasicBlock *BB, ArrayRef< Metadata * > Properties)
Attach metadata Properties to the basic block described by BB.
static void restoreIPandDebugLoc(llvm::IRBuilderBase &Builder, llvm::IRBuilderBase::InsertPoint IP)
This is a wrapper over IRBuilderBase::restoreIP that also restores a current debug location when the ...
static LoadInst * loadSharedDataFromTaskDescriptor(OpenMPIRBuilder &OMPIRBuilder, IRBuilderBase &Builder, Value *TaskWithPrivates, Type *TaskWithPrivatesTy)
Given a task descriptor, TaskWithPrivates, return the pointer to the block of pointers containing sha...
static cl::opt< bool > OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden, cl::desc("Use optimistic attributes describing " "'as-if' properties of runtime calls."), cl::init(false))
static bool hasGridValue(const Triple &T)
static FunctionCallee getKmpcForStaticLoopForType(Type *Ty, OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType)
static const omp::GV & getGridValue(const Triple &T, Function *Kernel)
static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static Function * emitTargetTaskProxyFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI, StructType *PrivatesTy, StructType *TaskWithPrivatesTy, const size_t NumOffloadingArrays, const int SharedArgsOperandNo)
Create an entry point for a target task with the following.
static void addLoopMetadata(CanonicalLoopInfo *Loop, ArrayRef< Metadata * > Properties)
Attach loop metadata Properties to the loop described by Loop.
static AtomicOrdering TransformReleaseAcquireRelease(AtomicOrdering AO)
static void removeUnusedBlocksFromParent(ArrayRef< BasicBlock * > BBs)
static void targetParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition, Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr, Value *ThreadID, const SmallVector< Instruction *, 4 > &ToBeDeleted)
static void hostParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, Value *Ident, Value *IfCondition, Instruction *PrivTID, AllocaInst *PrivTIDAddr, const SmallVector< Instruction *, 4 > &ToBeDeleted)
#define P(N)
FunctionAnalysisManager FAM
Function * Fun
This file defines the Pass Instrumentation classes that provide instrumentation points into the pass ...
const SmallVectorImpl< MachineOperand > & Cond
Remove Loads Into Fake Uses
static bool isValid(const char C)
Returns true if C is a valid mangled character: <0-9a-zA-Z_>.
SmallPtrSet< BasicBlock *, 0 > BlockSet
This file implements the SmallBitVector class.
This file defines the SmallSet class.
This file defines less commonly used SmallVector utilities.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static SymbolRef::Type getType(const Symbol *Sym)
Definition TapiFile.cpp:39
Defines the virtual file system interface vfs::FileSystem.
Value * RHS
Value * LHS
static cl::opt< unsigned > MaxThreads("xcore-max-threads", cl::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:205
An arbitrary precision integer that knows its signedness.
Definition APSInt.h:24
static APSInt getUnsigned(uint64_t X)
Definition APSInt.h:349
This class represents a conversion between pointers from one address space to another.
an instruction to allocate memory on the stack
Align getAlign() const
Return the alignment of the memory that is being allocated by the instruction.
PointerType * getType() const
Overload to return most specific pointer type.
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
unsigned getAddressSpace() const
Return the address space for the allocation.
LLVM_ABI std::optional< TypeSize > getAllocationSize(const DataLayout &DL) const
Get allocation size in bytes.
LLVM_ABI bool isArrayAllocation() const
Return true if there is an allocation size parameter to the allocation instruction that is not 1.
void setAlignment(Align Align)
const Value * getArraySize() const
Get the number of elements allocated.
bool registerPass(PassBuilderT &&PassBuilder)
Register an analysis pass with the manager.
This class represents an incoming formal argument to a Function.
Definition Argument.h:32
unsigned getArgNo() const
Return the index of this formal argument in its containing function.
Definition Argument.h:50
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
iterator end() const
Definition ArrayRef.h:130
size_t size() const
Get the array size.
Definition ArrayRef.h:141
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
Class to represent array types.
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
A function analysis which provides an AssumptionCache.
LLVM_ABI AssumptionCache run(Function &F, FunctionAnalysisManager &)
A cache of @llvm.assume calls within a function.
An instruction that atomically checks whether a specified value is in a memory location,...
void setWeak(bool IsWeak)
static AtomicOrdering getStrongestFailureOrdering(AtomicOrdering SuccessOrdering)
Returns the strongest permitted ordering on failure, given the desired ordering on success.
LLVM_ABI std::pair< LoadInst *, AllocaInst * > EmitAtomicLoadLibcall(AtomicOrdering AO)
Definition Atomic.cpp:109
LLVM_ABI void EmitAtomicStoreLibcall(AtomicOrdering AO, Value *Source)
Definition Atomic.cpp:150
an instruction that atomically reads a memory location, combines it with another value,...
BinOp
This enumeration lists the possible modifications atomicrmw can make.
@ Add
*p = old + v
@ FAdd
*p = old + v
@ USubCond
Subtract only if no unsigned overflow.
@ FMinimum
*p = minimum(old, v) minimum matches the behavior of llvm.minimum.
@ Min
*p = old <signed v ? old : v
@ Sub
*p = old - v
@ And
*p = old & v
@ Xor
*p = old ^ v
@ USubSat
*p = usub.sat(old, v) usub.sat matches the behavior of llvm.usub.sat.
@ FMaximum
*p = maximum(old, v) maximum matches the behavior of llvm.maximum.
@ FSub
*p = old - v
@ UIncWrap
Increment one up to a maximum value.
@ Max
*p = old >signed v ? old : v
@ UMin
*p = old <unsigned v ? old : v
@ FMin
*p = minnum(old, v) minnum matches the behavior of llvm.minnum.
@ UMax
*p = old >unsigned v ? old : v
@ FMaximumNum
*p = maximumnum(old, v) maximumnum matches the behavior of llvm.maximumnum.
@ FMax
*p = maxnum(old, v) maxnum matches the behavior of llvm.maxnum.
@ UDecWrap
Decrement one until a minimum value or zero.
@ FMinimumNum
*p = minimumnum(old, v) minimumnum matches the behavior of llvm.minimumnum.
@ Nand
*p = ~(old & v)
This class holds the attributes for a particular argument, parameter, function, or return value.
Definition Attributes.h:410
LLVM_ABI AttributeSet addAttributes(LLVMContext &C, AttributeSet AS) const
Add attributes to the attribute set.
LLVM_ABI AttributeSet addAttribute(LLVMContext &C, Attribute::AttrKind Kind) const
Add an argument attribute.
static LLVM_ABI Attribute getWithAlignment(LLVMContext &Context, Align Alignment)
Return a uniquified Attribute object that has the specific alignment set.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
LLVM_ABI void replaceSuccessorsPhiUsesWith(BasicBlock *Old, BasicBlock *New)
Update all phi nodes in this basic block's successors to refer to basic block New instead of basic bl...
iterator end()
Definition BasicBlock.h:459
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:446
LLVM_ABI const_iterator getFirstInsertionPt() const
Returns an iterator to the first instruction in this block that is suitable for inserting a non-PHI i...
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction.
const Function * getParent() const
Return the enclosing method, or null if none.
Definition BasicBlock.h:213
reverse_iterator rbegin()
Definition BasicBlock.h:462
bool hasTerminator() const LLVM_READONLY
Returns whether the block has a terminator.
Definition BasicBlock.h:232
bool empty() const
Definition BasicBlock.h:468
const Instruction & back() const
Definition BasicBlock.h:471
LLVM_ABI BasicBlock * splitBasicBlockBefore(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction and insert the new basic blo...
LLVM_ABI InstListType::const_iterator getFirstNonPHIIt() const
Returns an iterator to the first instruction in this block that is not a PHINode instruction.
LLVM_ABI void insertDbgRecordBefore(DbgRecord *DR, InstListType::iterator Here)
Insert a DbgRecord into a block at the position given by Here.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
LLVM_ABI InstListType::const_iterator getFirstNonPHIOrDbg(bool SkipPseudoOp=true) const
Returns a pointer to the first instruction in this block that is not a PHINode or a debug intrinsic,...
LLVM_ABI const BasicBlock * getUniqueSuccessor() const
Return the successor of this block if it has a unique successor.
LLVM_ABI const BasicBlock * getSinglePredecessor() const
Return the predecessor of this block if it has a single predecessor block.
const Instruction & front() const
Definition BasicBlock.h:469
InstListType::reverse_iterator reverse_iterator
Definition BasicBlock.h:172
LLVM_ABI const BasicBlock * getUniquePredecessor() const
Return the predecessor of this block if it has a unique predecessor block.
const Instruction * getTerminatorOrNull() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
Definition BasicBlock.h:248
LLVM_ABI const BasicBlock * getSingleSuccessor() const
Return the successor of this block if it has a single successor.
LLVM_ABI SymbolTableList< BasicBlock >::iterator eraseFromParent()
Unlink 'this' from the containing function and delete it.
reverse_iterator rend()
Definition BasicBlock.h:464
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
LLVM_ABI LLVMContext & getContext() const
Get the context in which this basic block lives.
void moveBefore(BasicBlock *MovePos)
Unlink this basic block from its current function and insert it into the function that MovePos lives ...
Definition BasicBlock.h:373
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
void splice(BasicBlock::iterator ToIt, BasicBlock *FromBB)
Transfer all instructions from FromBB to this basic block at ToIt.
Definition BasicBlock.h:644
LLVM_ABI void removePredecessor(BasicBlock *Pred, bool KeepOneInputPHIs=false)
Update PHI nodes in this BasicBlock before removal of predecessor Pred.
void setDoesNotThrow()
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Value * getArgOperand(unsigned i) const
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
unsigned arg_size() const
This class represents a function call, abstracting a target machine's calling convention.
Class to represented the control flow structure of an OpenMP canonical loop.
Value * getTripCount() const
Returns the llvm::Value containing the number of loop iterations.
BasicBlock * getHeader() const
The header is the entry for each iteration.
LLVM_ABI void assertOK() const
Consistency self-check.
Type * getIndVarType() const
Return the type of the induction variable (and the trip count).
BasicBlock * getBody() const
The body block is the single entry for a loop iteration and not controlled by CanonicalLoopInfo.
bool isValid() const
Returns whether this object currently represents the IR of a loop.
void setLastIter(Value *IterVar)
Sets the last iteration variable for this loop.
OpenMPIRBuilder::InsertPointTy getAfterIP() const
Return the insertion point for user code after the loop.
OpenMPIRBuilder::InsertPointTy getBodyIP() const
Return the insertion point for user code in the body.
BasicBlock * getAfter() const
The after block is intended for clean-up code such as lifetime end markers.
Function * getFunction() const
LLVM_ABI void invalidate()
Invalidate this loop.
BasicBlock * getLatch() const
Reaching the latch indicates the end of the loop body code.
OpenMPIRBuilder::InsertPointTy getPreheaderIP() const
Return the insertion point for user code before the loop.
BasicBlock * getCond() const
The condition block computes whether there is another loop iteration.
BasicBlock * getExit() const
Reaching the exit indicates no more iterations are being executed.
LLVM_ABI BasicBlock * getPreheader() const
The preheader ensures that there is only a single edge entering the loop.
Instruction * getIndVar() const
Returns the instruction representing the current logical induction variable.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ ICMP_SLT
signed less than
Definition InstrTypes.h:769
@ ICMP_SLE
signed less or equal
Definition InstrTypes.h:770
@ FCMP_OLT
0 1 0 0 True if ordered and less than
Definition InstrTypes.h:746
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
Definition InstrTypes.h:744
@ ICMP_UGT
unsigned greater than
Definition InstrTypes.h:763
@ ICMP_SGT
signed greater than
Definition InstrTypes.h:767
@ ICMP_ULT
unsigned less than
Definition InstrTypes.h:765
@ ICMP_NE
not equal
Definition InstrTypes.h:762
@ ICMP_ULE
unsigned less or equal
Definition InstrTypes.h:766
A cache for the CodeExtractor analysis.
Utility class for extracting code into a new function.
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:548
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 * getPointerBitCastOrAddrSpaceCast(Constant *C, Type *Ty)
Create a BitCast or AddrSpaceCast for a pointer type depending on the address space.
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:644
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:169
const BasicBlock & getEntryBlock() const
Definition Function.h:794
Argument * arg_iterator
Definition Function.h:73
bool empty() const
Definition Function.h:844
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:212
void removeFromParent()
removeFromParent - This method unlinks 'this' from the containing module, but does not delete it.
Definition Function.cpp:451
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:769
DISubprogram * getSubprogram() const
Get the attached subprogram.
AttributeList getAttributes() const
Return the attribute list for this Function.
Definition Function.h:329
const Function & getFunction() const
Definition Function.h:167
iterator begin()
Definition Function.h:838
arg_iterator arg_begin()
Definition Function.h:853
void setAttributes(AttributeList Attrs)
Set the attribute list for this Function.
Definition Function.h:332
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:356
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
adds the attribute to the list of attributes for the given arg.
Definition Function.cpp:672
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition Function.h:740
size_t arg_size() const
Definition Function.h:886
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:217
iterator end()
Definition Function.h:840
void setCallingConv(CallingConv::ID CC)
Definition Function.h:277
Argument * getArg(unsigned i) const
Definition Function.h:871
bool hasMetadata() const
Return true if this GlobalObject has any metadata attached to it.
LLVM_ABI void addMetadata(unsigned KindID, MDNode &MD)
Add a metadata attachment.
LinkageTypes getLinkage() const
void setLinkage(LinkageTypes LT)
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
PointerType * getType() const
Global values are always pointers.
@ HiddenVisibility
The GV is hidden.
Definition GlobalValue.h:69
@ ProtectedVisibility
The GV is protected.
Definition GlobalValue.h:70
void setVisibility(VisibilityTypes V)
LinkageTypes
An enumeration for the kinds of linkage for global values.
Definition GlobalValue.h:52
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
@ CommonLinkage
Tentative definitions.
Definition GlobalValue.h:63
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ WeakODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:58
@ WeakAnyLinkage
Keep one copy of named function when linking (weak)
Definition GlobalValue.h:57
@ AppendingLinkage
Special purpose, only applies to global arrays.
Definition GlobalValue.h:59
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:56
Type * getValueType() const
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
Common base class shared among various IRBuilders.
Definition IRBuilder.h:111
InsertPoint saveIP() const
Returns the current insert point.
Definition IRBuilder.h:266
BasicBlock::iterator InsertPoint
InsertPoint - A saved insertion point.
Definition IRBuilder.h:263
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:276
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2918
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 void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
user_iterator user_begin()
user_iterator user_end()
LLVM_ABI void moveBeforePreserving(InstListType::iterator MovePos)
Perform a moveBefore operation, while signalling that the caller intends to preserve the original ord...
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
LLVM_ABI void insertAfter(Instruction *InsertPos)
Insert an unlinked instruction into a basic block immediately after the specified instruction.
LLVM_ABI const DataLayout & getDataLayout() const
Get the data layout of the module this instruction belongs to.
Class to represent integer types.
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:338
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:71
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:594
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h:1081
LLVM_ABI void replaceOperandWith(unsigned I, Metadata *New)
Replace a specific operand.
static MDTuple * getDistinct(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1587
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1435
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:597
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
size_type size() const
Definition MapVector.h:58
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:332
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:325
A tuple of MDNodes.
Definition Metadata.h:1797
iterator_range< op_iterator > operands()
Definition Metadata.h:1893
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 createTarget(const LocationDescription &Loc, bool IsOffloadEntry, OpenMPIRBuilder::InsertPointTy AllocaIP, OpenMPIRBuilder::InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo, const TargetKernelDefaultAttrs &DefaultAttrs, const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, SmallVectorImpl< Value * > &Inputs, GenMapInfoCallbackTy GenMapInfoCB, TargetBodyGenCallbackTy BodyGenCB, TargetGenArgAccessorsCallbackTy ArgAccessorFuncCB, CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies={}, bool HasNowait=false, Value *DynCGroupMem=nullptr, omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback=omp::OMPDynGroupprivateFallbackType::Abort, DebugLoc OutlinedFnLoc={}, Value *RTLocOverride=nullptr)
Generator for 'omp target'.
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 Constant * emitKernelEnvironment(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
The omp target interface.
LLVM_ABI std::string createPlatformSpecificName(ArrayRef< StringRef > Parts) const
Get the create a name using the platform specific separators.
LLVM_ABI FunctionCallee createDispatchNextFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_next_* runtime function for the specified size IVSize and sign IVSigned.
static LLVM_ABI void getKernelArgsVector(TargetKernelArgs &KernelArgs, IRBuilderBase &Builder, SmallVector< Value * > &ArgsVector)
Create the kernel args vector used by emitTargetKernel.
LLVM_ABI void unrollLoopHeuristic(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully or partially unroll a loop.
LLVM_ABI omp::OpenMPOffloadMappingFlags getMemberOfFlag(unsigned Position)
Get OMP_MAP_MEMBER_OF flag with extra bits reserved based on the position given.
LLVM_ABI void addAttributes(omp::RuntimeFunction FnID, Function &Fn)
Add attributes known for FnID to Fn.
Module & M
The underlying LLVM-IR module.
StringMap< Constant * > SrcLocStrMap
Map to remember source location strings.
LLVM_ABI void createMapperAllocas(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumOperands, struct MapperAllocas &MapperAllocas)
Create the allocas instruction used in call to mapper functions.
SmallVector< DeclareTargetGlobalReplacement, 8 > DeclareTargetGlobalReplacements
Collection of declare target globals to rewrite uses of during device module finalizaiton.
LLVM_ABI Constant * getOrCreateSrcLocStr(StringRef LocStr, uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the source location LocStr.
LLVM_ABI Error emitTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry, Function *&OutlinedFn, Constant *&OutlinedFnID)
Create a unique name for the entry function using the source location information of the current targ...
LLVM_ABI InsertPointOrErrorTy createIteratorLoop(LocationDescription Loc, llvm::Value *TripCount, IteratorBodyGenTy BodyGen, llvm::StringRef Name="iterator")
Create a canonical iterator loop at the current insertion point.
LLVM_ABI Expected< SmallVector< llvm::CanonicalLoopInfo * > > createCanonicalScanLoops(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo)
Generator for the control flow structure of an OpenMP canonical loops if the parent directive has an ...
LLVM_ABI FunctionCallee createDispatchFiniFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_fini_* runtime function for the specified size IVSize and sign IVSigned.
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> TargetBodyGenCallbackTy
LLVM_ABI void unrollLoopPartial(DebugLoc DL, CanonicalLoopInfo *Loop, int32_t Factor, CanonicalLoopInfo **UnrolledCLI)
Partially unroll a loop.
function_ref< Error(Value *DeviceID, Value *RTLoc, IRBuilderBase::InsertPoint TargetTaskAllocaIP)> TargetTaskBodyCallbackTy
Callback type for generating the bodies of device directives that require outer target tasks (e....
Expected< MapInfosTy & > MapInfosOrErrorTy
bool HandleFPNegZero
Emit atomic compare for constructs: — Only scalar data types cond-expr-stmt: x = x ordop expr ?
LLVM_ABI void emitTaskyieldImpl(const LocationDescription &Loc)
Generate a taskyield runtime call.
LLVM_ABI void emitMapperCall(const LocationDescription &Loc, Function *MapperFunc, Value *SrcLocInfo, Value *MaptypesArg, Value *MapnamesArg, struct MapperAllocas &MapperAllocas, int64_t DeviceID, unsigned NumOperands)
Create the call for the target mapper function.
LLVM_ABI InsertPointOrErrorTy createDistribute(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for #omp distribute
function_ref< Expected< Function * >(unsigned int)> CustomMapperCallbackTy
LLVM_ABI InsertPointTy createOrderedDepend(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumLoops, ArrayRef< llvm::Value * > StoreValues, const Twine &Name, bool IsDependSource)
Generator for 'omp ordered depend (source | sink)'.
LLVM_ABI InsertPointTy createCopyinClauseBlocks(InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr, llvm::IntegerType *IntPtrTy, bool BranchtoEnd=true)
Generate conditional branch and relevant BasicBlocks through which private threads copy the 'copyin' ...
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Original, Value &Inner, Value *&ReplVal)> PrivatizeCallbackTy
Callback type for variable privatization (think copy & default constructor).
LLVM_ABI bool isFinalized()
Check whether the finalize function has already run.
SmallVector< FinalizationInfo, 8 > FinalizationStack
The finalization stack made up of finalize callbacks currently in-flight, wrapped into FinalizationIn...
LLVM_ABI std::vector< CanonicalLoopInfo * > tileLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, ArrayRef< Value * > TileSizes)
Tile a loop nest.
LLVM_ABI CallInst * createOMPInteropInit(const LocationDescription &Loc, Value *InteropVar, omp::OMPInteropType InteropType, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_init.
LLVM_ABI Error emitIfClause(Value *Cond, BodyGenCallbackTy ThenGen, BodyGenCallbackTy ElseGen, InsertPointTy AllocaIP={}, ArrayRef< BasicBlock * > DeallocBlocks={})
Emits code for OpenMP 'if' clause using specified BodyGenCallbackTy Here is the logic: if (Cond) { Th...
LLVM_ABI void finalize(Function *Fn=nullptr)
Finalize the underlying module, e.g., by outlining regions.
LLVM_ABI Function * getOrCreateRuntimeFunctionPtr(omp::RuntimeFunction FnID)
void addOutlineInfo(std::unique_ptr< OutlineInfo > &&OI)
Add a new region that will be outlined later.
LLVM_ABI InsertPointTy createTargetInit(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
Create a runtime call for kmpc_target_init.
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.
LLVM_ABI InsertPointOrErrorTy createTask(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, bool Tied=true, Value *Final=nullptr, Value *IfCondition=nullptr, const DependenciesInfo &Dependencies={}, const AffinityData &Affinities={}, bool Mergeable=false, Value *EventHandle=nullptr, Value *Priority=nullptr, bool FreeAgent=false)
Generator for #omp taskloop
std::forward_list< ScanInfo > ScanInfos
Collection of owned ScanInfo objects that eventually need to be free'd.
static LLVM_ABI void writeTeamsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI Value * calculateCanonicalLoopTripCount(const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, const Twine &Name="loop")
Calculate the trip count of a canonical loop.
LLVM_ABI InsertPointOrErrorTy createBarrier(const LocationDescription &Loc, omp::Directive Kind, bool ForceSimpleCall=false, bool CheckCancelFlag=true)
Emitter methods for OpenMP directives.
LLVM_ABI void setCorrectMemberOfFlag(omp::OpenMPOffloadMappingFlags &Flags, omp::OpenMPOffloadMappingFlags MemberOfFlag)
Given an initial flag set, this function modifies it to contain the passed in MemberOfFlag generated ...
LLVM_ABI Error emitOffloadingArraysAndArgs(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info, TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, bool ForEndCall=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Allocates memory for and populates the arrays required for offloading (offload_{baseptrs|ptrs|mappers...
LLVM_ABI Constant * getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the default source location.
LLVM_ABI InsertPointOrErrorTy createCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst)
Generator for 'omp critical'.
LLVM_ABI void createError(const LocationDescription &Loc, bool IsFatal, Value *Message)
Generate a call to the runtime to emit the diagnostic of an OpenMP error directive with at(execution)...
LLVM_ABI void createOffloadEntry(Constant *ID, Constant *Addr, uint64_t Size, int32_t Flags, GlobalValue::LinkageTypes, StringRef Name="")
Creates offloading entry for the provided entry ID ID, address Addr, size Size, and flags Flags.
static LLVM_ABI unsigned getOpenMPDefaultSimdAlign(const Triple &TargetTriple, const StringMap< bool > &Features)
Get the default alignment value for given target.
LLVM_ABI unsigned getFlagMemberOffset()
Get the offset of the OMP_MAP_MEMBER_OF field.
LLVM_ABI InsertPointOrErrorTy 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.
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, bool NeedsLastIter=false)
Modifies the canonical loop to be a workshare loop.
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 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...
LLVM_ABI void createTaskwait(const LocationDescription &Loc, DependenciesInfo Dependencies={}, bool IsNowait=false)
Generator for 'omp taskwait'.
origPtr *with the address space normalization required by the runtime entry point *The NULL descriptor makes the runtime walk the enclosing taskgroups to *find the matching task_reduction registration for the item The lookups *are emitted at p Loc
EmitMetadataErrorKind
The kind of errors that can occur when emitting the offload entries and metadata.
unsigned getOpcode() const
Return the opcode for this Instruction or ConstantExpr.
Definition Operator.h:43
The optimization diagnostic interface.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
Pseudo-analysis pass that exposes the PassInstrumentation to pass managers.
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:887
PostDominatorTree Class - Concrete subclass of DominatorTree that is used to compute the post-dominat...
Analysis pass that exposes the ScalarEvolution for a function.
LLVM_ABI ScalarEvolution run(Function &F, FunctionAnalysisManager &AM)
The main scalar evolution driver.
ScanInfo holds the information to assist in lowering of Scan reduction.
llvm::SmallDenseMap< llvm::Value *, llvm::Value * > * ScanBuffPtrs
Maps the private reduction variable to the pointer of the temporary buffer.
llvm::BasicBlock * OMPScanLoopExit
Exit block of loop body.
llvm::Value * IV
Keeps track of value of iteration variable for input/scan loop to be used for Scan directive lowering...
llvm::BasicBlock * OMPAfterScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanInit
Block before loop body where scan initializations are done.
llvm::BasicBlock * OMPBeforeScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanFinish
Block after loop body where scan finalizations are done.
llvm::Value * Span
Stores the span of canonical loop being lowered to be used for temporary buffer allocation or Finaliz...
bool OMPFirstScanLoop
If true, it indicates Input phase is lowered; else it indicates ScanPhase is lowered.
llvm::BasicBlock * OMPScanDispatch
Controls the flow to before or after scan blocks.
A vector that has set insertion semantics.
Definition SetVector.h:57
bool remove_if(UnaryPredicate P)
Remove items from the set vector based on a predicate function.
Definition SetVector.h:236
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallBitVector & set()
bool test(unsigned Idx) const
Returns true if bit Idx is set.
bool all() const
Returns true if all bits are set.
bool any() const
Returns true if any bit is set.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
void append(StringRef RHS)
Append from a StringRef.
Definition SmallString.h:68
StringRef str() const
Explicit conversion to StringRef.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void resize(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
void setAlignment(Align Align)
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this store instruction.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:129
ValueTy lookup(StringRef Key) const
lookup - Return the entry for the specified key, or a default constructed value if no such entry exis...
Definition StringMap.h:250
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
size_t count(char C) const
Return the number of occurrences of C in the string.
Definition StringRef.h:471
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition StringRef.h:270
StringRef drop_back(size_t N=1) const
Return a StringRef equal to 'this' but with the last N elements dropped.
Definition StringRef.h:642
Class to represent struct types.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:467
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:662
Type * getElementType(unsigned N) const
Multiway switch.
LLVM_ABI void addCase(ConstantInt *OnVal, BasicBlock *Dest)
Add an entry to the switch instruction.
Analysis pass providing the TargetTransformInfo.
LLVM_ABI Result run(const Function &F, FunctionAnalysisManager &)
Analysis pass providing the TargetLibraryInfo.
Target - Wrapper for Target specific information.
TargetMachine * createTargetMachine(const Triple &TT, StringRef CPU, StringRef Features, const TargetOptions &Options, std::optional< Reloc::Model > RM, std::optional< CodeModel::Model > CM=std::nullopt, CodeGenOptLevel OL=CodeGenOptLevel::Default, bool JIT=false) const
createTargetMachine - Create a target specific machine implementation for the specified Triple.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isPPC() const
Tests whether the target is PowerPC (32- or 64-bit LE or BE).
Definition Triple.h:1141
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1201
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1215
bool isSystemZ() const
Tests whether the target is SystemZ.
Definition Triple.h:1198
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM_ABI std::string str() const
Return the twine contents as a std::string.
Definition Twine.cpp:17
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
Definition Type.cpp:300
LLVM_ABI unsigned getIntegerBitWidth() const
LLVM_ABI Type * getStructElementType(unsigned N) const
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:277
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
bool isStructTy() const
True if this is an instance of StructType.
Definition Type.h:271
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:222
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:252
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:303
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
Unconditional Branch instruction.
static UncondBrInst * Create(BasicBlock *Target, InsertPosition InsertBefore=nullptr)
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
This function has undefined behavior.
Produce an estimate of the unrolled cost of the specified loop.
Definition UnrollLoop.h:150
LLVM_ABI bool canUnroll(OptimizationRemarkEmitter *ORE=nullptr, const Loop *L=nullptr) const
Whether it is legal to unroll this loop.
uint64_t getRolledLoopSize() const
Definition UnrollLoop.h:174
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
void setOperand(unsigned i, Value *Val)
Definition User.h:212
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:394
bool hasOneUse() const
Return true if there is exactly one use of this value.
Definition Value.h:441
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:428
User * user_back()
Definition Value.h:414
LLVM_ABI Align getPointerAlignment(const DataLayout &DL) const
Returns an alignment of the pointer value.
Definition Value.cpp:1002
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:147
LLVM_ABI User * getUniqueUndroppableUser()
Return true if there is exactly one unique user of this value that cannot be dropped (that user can h...
Definition Value.cpp:185
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:712
bool use_empty() const
Definition Value.h:348
LLVM_ABI bool replaceUsesWithIf(Value *New, llvm::function_ref< bool(Use &U)> ShouldReplace)
Go through the uses list for this definition and make each use point to "V" if the callback ShouldRep...
Definition Value.cpp:561
iterator_range< use_iterator > uses()
Definition Value.h:382
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
An efficient, type-erasing, non-owning reference to a callable.
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
A raw_ostream that writes to an SmallVector or SmallString.
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
CallInst * Call
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ SPIR_KERNEL
Used for SPIR kernel functions.
@ PTX_Kernel
Call to a PTX kernel. Passes all arguments in parameter space.
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:83
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
Flag
These should be considered private to the implementation of the MCInstrDesc class.
constexpr StringLiteral MaxNTID("nvvm.maxntid")
constexpr StringLiteral MaxClusterRank("nvvm.maxclusterrank")
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
LLVM_ABI GlobalVariable * emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr=nullptr)
Definition Utility.cpp:104
OpenMPOffloadMappingFlags
Values for bit flags used to specify the mapping type for offloading.
@ OMP_MAP_PTR_AND_OBJ
The element being mapped is a pointer-pointee pair; both the pointer and the pointee should be mapped...
@ OMP_MAP_MEMBER_OF
The 16 MSBs of the flags indicate whether the entry is member of some struct/class.
IdentFlag
IDs for all omp runtime library ident_t flag encodings (see their defintion in openmp/runtime/src/kmp...
RuntimeFunction
IDs for all omp runtime library (RTL) functions.
constexpr const GV & getAMDGPUGridValues()
static constexpr GV SPIRVGridValues
For generic SPIR-V GPUs.
OMPDynGroupprivateFallbackType
The fallback types for the dyn_groupprivate clause.
static constexpr GV NVPTXGridValues
For Nvidia GPUs.
@ OMP_TGT_EXEC_MODE_SPMD_NO_LOOP
Function * Kernel
Summary of a kernel (=entry point for target offloading).
Definition OpenMPOpt.h:21
WorksharingLoopType
A type of worksharing loop construct.
OMPAtomicCompareOp
Atomic compare operations. Currently OpenMP only supports ==, >, and <.
EnumSet< Property > Properties
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:316
LLVM_ABI BasicBlock * splitBBWithSuffix(IRBuilderBase &Builder, bool CreateBranch, llvm::Twine Suffix=".split")
Like splitBB, but reuses the current block's name for the new name.
@ Offset
Definition DWP.cpp:577
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
Definition STLExtras.h:846
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:1755
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:856
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:2570
unsigned getPointerAddressSpace(const Type *T)
Definition SPIRVUtils.h:393
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:2224
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:649
LLVM_ABI TargetTransformInfo::UnrollingPreferences gatherUnrollingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, llvm::OptimizationRemarkEmitter &ORE, int OptLevel, std::optional< unsigned > UserThreshold, std::optional< bool > UserAllowPartial, std::optional< bool > UserRuntime, std::optional< bool > UserUpperBound, std::optional< unsigned > UserFullUnrollMaxCount)
Gather the various unrolling parameters based on the defaults, compiler flags, TTI overrides and user...
std::string utostr(uint64_t X, bool isNeg=false)
void * PointerTy
ErrorOr< T > expectedToErrorOrAndEmitErrors(LLVMContext &Ctx, Expected< T > Val)
bool isa_and_nonnull(const Y &Val)
Definition Casting.h:676
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
LLVM_ABI bool convertUsersOfConstantsToInstructions(ArrayRef< Constant * > Consts, Function *RestrictToFunc=nullptr, bool RemoveDeadConstants=true, bool IncludeSelf=false)
Replace constant expressions users of the given constants with instructions.
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition MathExtras.h:326
auto reverse(ContainerTy &&C)
Definition STLExtras.h:408
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:1769
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:227
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
AtomicOrdering
Atomic ordering for LLVM's memory model.
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h:389
TargetTransformInfo TTI
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
LLVM_ABI bool MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, MemoryDependenceResults *MemDep=nullptr, bool PredecessorWithTwoSuccessors=false, DominatorTree *DT=nullptr)
Attempts to merge a block into its predecessor, if possible.
@ Mul
Product of integers.
@ Add
Sum of integers.
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
IntPtrTy
Definition InstrProf.h:82
DWARFExpression::Operation Op
LLVM_ABI void remapInstructionsInBlocks(ArrayRef< BasicBlock * > Blocks, ValueToValueMapTy &VMap)
Remaps instructions in Blocks using the mapping in VMap.
ArrayRef(const T &OneElt) -> ArrayRef< T >
OutputIt copy(R &&Range, OutputIt Out)
Definition STLExtras.h:1901
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,...
@ End
Run the machine verifier once, at the end of the pipeline.
Definition CodeGen.h:267
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:111
A struct to pack the relevant information for an OpenMP affinity clause.
a struct to pack relevant information while generating atomic Ops
A struct to pack the relevant information for an OpenMP depend clause.
omp::RTLDependenceKindTy DepKind
A struct to pack static and dynamic dependency information for a task.
LLVM_ABI Error mergeFiniBB(IRBuilderBase &Builder, BasicBlock *ExistingFiniBB)
For cases where there is an unavoidable existing finalization block (e.g.
LLVM_ABI Expected< BasicBlock * > getFiniBB(IRBuilderBase &Builder)
The basic block to which control should be transferred to implement the FiniCB.
Description of a LLVM-IR insertion point (IP) and a debug/source location (filename,...
This structure contains combined information generated for mappable clauses, including base pointers,...
MapDeviceInfoArrayTy DevicePointers
StructNonContiguousInfo NonContigInfo
Helper that contains information about regions we need to outline during finalization.
void collectBlocks(SmallPtrSetImpl< BasicBlock * > &BlockSet, SmallVectorImpl< BasicBlock * > &BlockVector)
Collect all blocks in between EntryBB and ExitBB in both the given vector and set.
virtual std::unique_ptr< CodeExtractor > createCodeExtractor(ArrayRef< BasicBlock * > Blocks, bool ArgsInZeroAddressSpace, Twine Suffix=Twine(""))
Create a CodeExtractor instance based on the information stored in this structure,...
Information about an OpenMP reduction.
EvalKind EvaluationKind
Reduction evaluation kind - scalar, complex or aggregate.
ReductionGenAtomicCBTy AtomicReductionGen
Callback for generating the atomic reduction body, may be null.
ReductionGenCBTy ReductionGen
Callback for generating the reduction body.
Value * Variable
Reduction variable of pointer type.
Value * PrivateVariable
Thread-private partial reduction variable.
ReductionGenClangCBTy ReductionGenClang
Clang callback for generating the reduction body.
Type * ElementType
Reduction element type, must match pointee type of variable.
ReductionGenDataPtrPtrCBTy DataPtrPtrGen
Container for the arguments used to pass data to the runtime library.
Value * SizesArray
The array of sizes passed to the runtime library.
Value * PointersArray
The array of section pointers passed to the runtime library.
Value * MappersArray
The array of user-defined mappers passed to the runtime library.
Value * MapTypesArrayEnd
The array of map types passed to the runtime library for the end of the region, or nullptr if there a...
Value * BasePointersArray
The array of base pointer passed to the runtime library.
Value * MapTypesArray
The array of map types passed to the runtime library for the beginning of the region or for the entir...
Value * MapNamesArray
The array of original declaration names of mapped pointers sent to the runtime library for debugging.
Data structure that contains the needed information to construct the kernel args vector.
bool StrictBlocks
True if the kernel strictly requires the number of blocks and threads above to run.
ArrayRef< Value * > NumThreads
The number of threads.
TargetDataRTArgs RTArgs
Arguments passed to the runtime library.
Value * NumIterations
The number of iterations.
Value * DynCGroupMem
The size of the dynamic shared memory.
unsigned NumTargetItems
Number of arguments passed to the runtime library.
bool HasNoWait
True if the kernel has 'no wait' clause.
ArrayRef< Value * > NumTeams
The number of teams.
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback
The fallback mechanism for the shared memory.
Container to pass the default attributes with which a kernel must be launched, used to set kernel att...
Container to pass LLVM IR runtime values or constants related to the number of teams and threads with...
Value * DeviceID
Device ID value used in the kernel launch.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic kernel.
SmallVector< Value * > MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Data structure to contain the information needed to uniquely identify a target entry.
static LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, StringRef ParentName, unsigned DeviceID, unsigned FileID, unsigned Line, unsigned Count)
static constexpr const char * KernelNamePrefix
The prefix used for kernel names.
static LLVM_ABI const Target * lookupTarget(const Triple &TheTriple, std::string &Error)
lookupTarget - Lookup a target based on a target triple.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
bool Force
Apply loop unroll on any kind of loop (mainly to loops that fail runtime unrolling).
unsigned PartialOptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size, like OptSizeThreshold,...
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
unsigned OptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size (set to UINT_MAX to disable).
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...