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
1901 // CodeExtractor creates an alloca aggregate struct containing pointers to all
1902 // the captured values. The address of this struct is then passed as a
1903 // parameter to the outlined function. This parameter of the outlined function
1904 // can be marked noalias because CodeExractor + this host OpenMP lowering
1905 // maintains the following invariants:
1906 // - The caller initializes the fields before starting any callbacks.
1907 // - Neither the caller, OpenMP runtime, nor callbacks modify the structure
1908 // while callbacks are executing.
1909 // - The callbacks only load captured values from the structure; they do not
1910 // use it to write results back to the caller. This is generated code; the
1911 // user has no way to modify the structure directly.
1912 //
1913 // Multiple callbacks may read the same structure. This is allowed by noalias,
1914 // which permits aliasing accesses to memory that is not modified during the
1915 // call.
1916 //
1917 // The synchronous fork keeps the structure alive until all callbacks return,
1918 // which justifies nofreeobj. Neither attribute extends to the objects pointed
1919 // to by the captured fields.
1920 if (NumCapturedVars) {
1921 assert(NumCapturedVars == 1 && "Expected an aggregate capture argument");
1924 ->getAllocatedType()
1925 ->isStructTy() &&
1926 "Expected a stack-allocated capture structure");
1927
1928 OutlinedFn.addParamAttr(2, Attribute::NoAlias);
1929 OutlinedFn.addParamAttr(2, Attribute::NoFreeObj);
1930 }
1931
1932 CI->getParent()->setName("omp_parallel");
1933 Builder.SetInsertPoint(CI);
1934
1935 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1936 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1937 &OutlinedFn};
1938
1939 SmallVector<Value *, 16> RealArgs;
1940 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1941 if (IfCondition) {
1942 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1943 RealArgs.push_back(Cond);
1944 }
1945 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1946
1947 // __kmpc_fork_call_if always expects a void ptr as the last argument
1948 // If there are no arguments, pass a null pointer.
1949 auto PtrTy = OMPIRBuilder->VoidPtr;
1950 if (IfCondition && NumCapturedVars == 0) {
1951 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1952 RealArgs.push_back(NullPtrValue);
1953 }
1954
1955 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1956
1957 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1958 << *Builder.GetInsertBlock()->getParent() << "\n");
1959
1960 // Initialize the local TID stack location with the argument value.
1961 Builder.SetInsertPoint(PrivTID);
1962 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1963 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1964 PrivTIDAddr);
1965
1966 // Remove redundant call to the outlined function.
1967 CI->eraseFromParent();
1968
1969 for (Instruction *I : ToBeDeleted) {
1970 I->eraseFromParent();
1971 }
1972}
1973
1975 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1976 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1977 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1978 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1979 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1980
1981 if (!updateToLocation(Loc))
1982 return Loc.IP;
1983
1984 uint32_t SrcLocStrSize;
1985 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1986 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1987 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1988 (ProcBind != OMP_PROC_BIND_default);
1989 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1990 // If we generate code for the target device, we need to allocate
1991 // struct for aggregate params in the device default alloca address space.
1992 // OpenMP runtime requires that the params of the extracted functions are
1993 // passed as zero address space pointers. This flag ensures that extracted
1994 // function arguments are declared in zero address space
1995 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1996
1997 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1998 // only if we compile for host side.
1999 if (NumThreads && !Config.isTargetDevice()) {
2000 Value *Args[] = {
2001 Ident, ThreadID,
2002 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
2004 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
2005 }
2006
2007 if (ProcBind != OMP_PROC_BIND_default) {
2008 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
2009 Value *Args[] = {
2010 Ident, ThreadID,
2011 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
2013 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
2014 }
2015
2016 BasicBlock *InsertBB = Builder.GetInsertBlock();
2017 Function *OuterFn = InsertBB->getParent();
2018
2019 // Save the outer alloca block because the insertion iterator may get
2020 // invalidated and we still need this later.
2021 BasicBlock *OuterAllocaBlock = OuterAllocIP.getNodeParent();
2022
2023 // Vector to remember instructions we used only during the modeling but which
2024 // we want to delete at the end.
2026
2027 // Change the location to the outer alloca insertion point to create and
2028 // initialize the allocas we pass into the parallel region.
2029 InsertPointTy NewOuter(OuterAllocaBlock->begin());
2030 Builder.restoreIP(NewOuter);
2031 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
2032 AllocaInst *ZeroAddrAlloca =
2033 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
2034 Instruction *TIDAddr = TIDAddrAlloca;
2035 Instruction *ZeroAddr = ZeroAddrAlloca;
2036 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
2037 // Add additional casts to enforce pointers in zero address space
2038 TIDAddr = new AddrSpaceCastInst(
2039 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2040 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2041 ToBeDeleted.push_back(TIDAddr);
2042 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2043 PointerType ::get(M.getContext(), 0),
2044 "zero.addr.ascast");
2045 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2046 ToBeDeleted.push_back(ZeroAddr);
2047 }
2048
2049 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2050 // associated arguments in the outlined function, so we delete them later.
2051 ToBeDeleted.push_back(TIDAddrAlloca);
2052 ToBeDeleted.push_back(ZeroAddrAlloca);
2053
2054 // Create an artificial insertion point that will also ensure the blocks we
2055 // are about to split are not degenerated.
2056 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2057
2058 BasicBlock *EntryBB = UI->getParent();
2059 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2060 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2061 BasicBlock *PRegPreFiniBB =
2062 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2063 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2064
2065 auto FiniCBWrapper = [&](InsertPointTy IP) {
2066 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2067 // target to the region exit block.
2068 if (IP == IP.getNodeParent()->end()) {
2070 Builder.restoreIP(IP);
2071 Instruction *I = Builder.CreateBr(PRegExitBB);
2072 IP = I->getIterator();
2073 }
2074 assert(IP.getNodeParent()->getTerminator()->getNumSuccessors() == 1 &&
2075 IP.getNodeParent()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2076 "Unexpected insertion point for finalization call!");
2077 return FiniCB(IP);
2078 };
2079
2080 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2081
2082 // Generate the privatization allocas in the block that will become the entry
2083 // of the outlined function.
2084 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2085 InsertPointTy InnerAllocaIP = Builder.saveIP();
2086
2087 AllocaInst *PrivTIDAddr =
2088 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2089 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2090
2091 // Add some fake uses for OpenMP provided arguments.
2092 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2093 Instruction *ZeroAddrUse =
2094 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2095 ToBeDeleted.push_back(ZeroAddrUse);
2096
2097 // EntryBB
2098 // |
2099 // V
2100 // PRegionEntryBB <- Privatization allocas are placed here.
2101 // |
2102 // V
2103 // PRegionBodyBB <- BodeGen is invoked here.
2104 // |
2105 // V
2106 // PRegPreFiniBB <- The block we will start finalization from.
2107 // |
2108 // V
2109 // PRegionExitBB <- A common exit to simplify block collection.
2110 //
2111
2112 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2113
2114 // Let the caller create the body.
2115 assert(BodyGenCB && "Expected body generation callback!");
2116 InsertPointTy CodeGenIP(PRegBodyBB->begin());
2117 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2118 return Err;
2119
2120 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2121
2122 // If OuterFn is a Generic kernel, we need to use device shared memory to
2123 // allocate argument structures. Otherwise, we use stack allocations as usual.
2124 bool UsesDeviceSharedMemory =
2125 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2126 std::unique_ptr<OutlineInfo> OI =
2127 UsesDeviceSharedMemory
2128 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2129 : std::make_unique<OutlineInfo>();
2130
2131 if (Config.isTargetDevice()) {
2132 // Generate OpenMP target specific runtime call
2133 OI->PostOutlineCB = [=, ToBeDeletedVec =
2134 std::move(ToBeDeleted)](Function &OutlinedFn) {
2135 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2136 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2137 ThreadID, ToBeDeletedVec);
2138 };
2139 } else {
2140 // Generate OpenMP host runtime call
2141 OI->PostOutlineCB = [=, ToBeDeletedVec =
2142 std::move(ToBeDeleted)](Function &OutlinedFn) {
2143 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2144 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2145 };
2146 }
2147
2148 OI->FixUpNonEntryAllocas = true;
2149 OI->OuterAllocBB = OuterAllocaBlock;
2150 OI->EntryBB = PRegEntryBB;
2151 OI->ExitBB = PRegExitBB;
2152 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2153 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2154
2155 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2157 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2158
2159 CodeExtractorAnalysisCache CEAC(*OuterFn);
2160 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2161 /* AggregateArgs */ false,
2162 /* BlockFrequencyInfo */ nullptr,
2163 /* BranchProbabilityInfo */ nullptr,
2164 /* AssumptionCache */ nullptr,
2165 /* AllowVarArgs */ true,
2166 /* AllowAlloca */ true,
2167 /* AllocationBlock */ OuterAllocaBlock,
2168 /* DeallocationBlocks */ {},
2169 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2170
2171 // Find inputs to, outputs from the code region.
2172 BasicBlock *CommonExit = nullptr;
2173 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2174 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2175
2176 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2177 /*CollectGlobalInputs=*/true);
2178
2179 Inputs.remove_if([&](Value *I) {
2181 return GV->getValueType() == OpenMPIRBuilder::Ident;
2182
2183 return false;
2184 });
2185
2186 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2187
2188 FunctionCallee TIDRTLFn =
2189 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2190
2191 auto PrivHelper = [&](Value &V) -> Error {
2192 if (&V == TIDAddr || &V == ZeroAddr) {
2193 OI->ExcludeArgsFromAggregate.push_back(&V);
2194 return Error::success();
2195 }
2196
2198 for (Use &U : V.uses())
2199 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2200 if (ParallelRegionBlockSet.count(UserI->getParent()))
2201 Uses.insert(&U);
2202
2203 // __kmpc_fork_call expects extra arguments as pointers. If the input
2204 // already has a pointer type, everything is fine. Otherwise, store the
2205 // value onto stack and load it back inside the to-be-outlined region. This
2206 // will ensure only the pointer will be passed to the function.
2207 // FIXME: if there are more than 15 trailing arguments, they must be
2208 // additionally packed in a struct.
2209 Value *Inner = &V;
2210 if (!V.getType()->isPointerTy()) {
2212 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2213
2214 Builder.restoreIP(OuterAllocIP);
2215 Value *Ptr;
2216 if (UsesDeviceSharedMemory) {
2217 // Use device shared memory instead, if needed.
2218 Ptr = createOMPAllocShared(Builder, V.getType(),
2219 V.getName() + ".reloaded");
2220 for (BasicBlock *DeallocBlock : OuterDeallocBlocks) {
2221 assert(DeallocBlock->getParent() ==
2222 OuterAllocIP.getNodeParent()->getParent() &&
2223 "Dealloc block must be in the allocation's function to reuse "
2224 "its debug location");
2225 createOMPFreeShared({DeallocBlock->getFirstInsertionPt(),
2226 Builder.getCurrentDebugLocation()},
2227 Ptr, V.getType());
2228 }
2229 } else {
2230 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2231 V.getName() + ".reloaded");
2232 }
2233
2234 // Store to stack at end of the block that currently branches to the entry
2235 // block of the to-be-outlined region.
2236 Builder.SetInsertPoint(InsertBB->getTerminator()->getIterator());
2237 Builder.CreateStore(&V, Ptr);
2238
2239 // Load back next to allocations in the to-be-outlined region.
2240 Builder.restoreIP(InnerAllocaIP);
2241 Inner = Builder.CreateLoad(V.getType(), Ptr);
2242 }
2243
2244 Value *ReplacementValue = nullptr;
2245 CallInst *CI = dyn_cast<CallInst>(&V);
2246 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2247 ReplacementValue = PrivTID;
2248 } else {
2249 InsertPointOrErrorTy AfterIP =
2250 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2251 if (!AfterIP)
2252 return AfterIP.takeError();
2253 Builder.restoreIP(*AfterIP);
2254 InnerAllocaIP =
2255 InnerAllocaIP.getNodeParent()->getTerminator()->getIterator();
2256
2257 assert(ReplacementValue &&
2258 "Expected copy/create callback to set replacement value!");
2259 if (ReplacementValue == &V)
2260 return Error::success();
2261 }
2262
2263 for (Use *UPtr : Uses)
2264 UPtr->set(ReplacementValue);
2265
2266 return Error::success();
2267 };
2268
2269 // Reset the inner alloca insertion as it will be used for loading the values
2270 // wrapped into pointers before passing them into the to-be-outlined region.
2271 // Configure it to insert immediately after the fake use of zero address so
2272 // that they are available in the generated body and so that the
2273 // OpenMP-related values (thread ID and zero address pointers) remain leading
2274 // in the argument list.
2275 InnerAllocaIP = ZeroAddrUse->getNextNode()->getIterator();
2276
2277 // Reset the outer alloca insertion point to the entry of the relevant block
2278 // in case it was invalidated.
2279 OuterAllocIP = OuterAllocaBlock->getFirstInsertionPt();
2280
2281 for (Value *Input : Inputs) {
2282 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2283 if (Error Err = PrivHelper(*Input))
2284 return Err;
2285 }
2286 LLVM_DEBUG({
2287 for (Value *Output : Outputs)
2288 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2289 });
2290 assert(Outputs.empty() &&
2291 "OpenMP outlining should not produce live-out values!");
2292
2293 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2294 LLVM_DEBUG({
2295 for (auto *BB : Blocks)
2296 dbgs() << " PBR: " << BB->getName() << "\n";
2297 });
2298
2299 // Adjust the finalization stack, verify the adjustment, and call the
2300 // finalize function a last time to finalize values between the pre-fini
2301 // block and the exit block if we left the parallel "the normal way".
2302 auto FiniInfo = FinalizationStack.pop_back_val();
2303 (void)FiniInfo;
2304 assert(FiniInfo.DK == OMPD_parallel &&
2305 "Unexpected finalization stack state!");
2306
2307 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2308
2309 InsertPointTy PreFiniIP(PRegPreFiniTI->getIterator());
2310 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2311 if (!FiniBBOrErr)
2312 return FiniBBOrErr.takeError();
2313 {
2315 Builder.restoreIP(PreFiniIP);
2316 Builder.CreateBr(*FiniBBOrErr);
2317 // There's currently a branch to omp.par.exit. Delete it. We will get there
2318 // via the fini block
2319 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2320 Term->eraseFromParent();
2321 }
2322
2323 // Register the outlined info.
2324 addOutlineInfo(std::move(OI));
2325
2326 InsertPointTy AfterIP(UI->getParent()->end());
2327 UI->eraseFromParent();
2328
2329 return AfterIP;
2330}
2331
2333 // Build call void __kmpc_flush(ident_t *loc)
2334 uint32_t SrcLocStrSize;
2335 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2336 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2337
2339 Args);
2340}
2341
2343 if (!updateToLocation(Loc))
2344 return;
2345 emitFlush(Loc);
2346}
2347
2349 Value *Message) {
2350 if (!updateToLocation(Loc))
2351 return;
2352
2353 // Build call void __kmpc_error(ident_t *loc, int severity,
2354 // const char *message)
2355 uint32_t SrcLocStrSize;
2356 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2357 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2358 // Severity: 1 = warning, 2 = fatal.
2359 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2360 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2361 Value *Args[] = {Ident, Severity, MessageArg};
2362
2364 Args);
2365}
2366
2368 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2369 uint32_t SrcLocStrSize;
2370 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2371 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2372 Constant *I32Null = ConstantInt::getNullValue(Int32);
2373 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2374
2376 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2377}
2378
2384
2386 const DependData &Dep) {
2387 // Store the pointer to the variable
2388 Value *Addr = Builder.CreateStructGEP(
2389 DependInfo, Entry,
2390 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2391 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2392 Builder.CreateStore(DepValPtr, Addr);
2393 // Store the size of the variable
2394 Value *Size = Builder.CreateStructGEP(
2395 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2396 Builder.CreateStore(
2397 ConstantInt::get(SizeTy,
2398 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2399 Size);
2400 // Store the dependency kind
2401 Value *Flags = Builder.CreateStructGEP(
2402 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2403 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2404 static_cast<unsigned int>(Dep.DepKind)),
2405 Flags);
2406}
2407
2408// Processes the dependencies in Dependencies and does the following
2409// - Allocates space on the stack of an array of DependInfo objects
2410// - Populates each DependInfo object with relevant information of
2411// the corresponding dependence.
2412// - All code is inserted in the entry block of the current function.
2414 OpenMPIRBuilder &OMPBuilder,
2416 // Early return if we have no dependencies to process
2417 if (Dependencies.empty())
2418 return nullptr;
2419
2420 // Given a vector of DependData objects, in this function we create an
2421 // array on the stack that holds kmp_depend_info objects corresponding
2422 // to each dependency. This is then passed to the OpenMP runtime.
2423 // For example, if there are 'n' dependencies then the following psedo
2424 // code is generated. Assume the first dependence is on a variable 'a'
2425 //
2426 // \code{c}
2427 // DepArray = alloc(n x sizeof(kmp_depend_info);
2428 // idx = 0;
2429 // DepArray[idx].base_addr = ptrtoint(&a);
2430 // DepArray[idx].len = 8;
2431 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2432 // ++idx;
2433 // DepArray[idx].base_addr = ...;
2434 // \endcode
2435
2436 IRBuilderBase &Builder = OMPBuilder.Builder;
2437 Type *DependInfo = OMPBuilder.DependInfo;
2438
2439 Value *DepArray = nullptr;
2440 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2441 {
2442 // Use a InsertPointGuard to restore the location back along with the
2443 // insertion point.
2444 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2445 Builder.SetInsertPoint(
2446 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2447 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2448 }
2449
2450 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2451 Value *Base =
2452 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2453 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2454 }
2455 return DepArray;
2456}
2457
2459 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2460 // global_tid);
2461 uint32_t SrcLocStrSize;
2462 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2463 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2464 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2465
2466 // Ignore return result until untied tasks are supported.
2468 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2469}
2470
2472 DependenciesInfo Dependencies,
2473 bool IsNowait) {
2474 if (!updateToLocation(Loc))
2475 return;
2476
2477 Value *DepArray = nullptr;
2478 Type *DepArrayTy = nullptr;
2479 Value *NumDeps = nullptr;
2480 if (Dependencies.DepArray) {
2481 DepArray = Dependencies.DepArray;
2482 NumDeps = Dependencies.NumDeps;
2483 } else if (!Dependencies.Deps.empty()) {
2484 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2485 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2486 {
2488 BasicBlock &entryBB =
2489 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2490 Builder.SetInsertPoint(entryBB.getFirstInsertionPt());
2491 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2492 }
2493
2494 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2495 Value *Base =
2496 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2497 this->emitTaskDependency(Builder, Base, Dep);
2498 }
2499 }
2500
2501 if (DepArray) {
2502 uint32_t SrcLocStrSize;
2503 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2504 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2505 Value *Args[] = {
2506 Ident,
2507 getOrCreateThreadID(Ident),
2508 NumDeps,
2509 DepArray,
2510 ConstantInt::get(Builder.getInt32Ty(), 0),
2512 ConstantInt::get(Builder.getInt32Ty(), IsNowait)};
2515 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2516 Args);
2517 } else {
2519 }
2520}
2521
2522/// Create the task duplication function passed to kmpc_taskloop.
2523Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2524 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2525 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2526 if (!DupCB)
2528 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2529
2530 // From OpenMP Runtime p_task_dup_t:
2531 // Routine optionally generated by the compiler for setting the lastprivate
2532 // flag and calling needed constructors for private/firstprivate objects (used
2533 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2534 // lastprivate flag.
2535 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2536
2537 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2538
2539 FunctionType *DupFuncTy = FunctionType::get(
2540 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2541 /*isVarArg=*/false);
2542
2543 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2544 "omp_taskloop_dup", M);
2545 Value *DestTaskArg = DupFunction->getArg(0);
2546 Value *SrcTaskArg = DupFunction->getArg(1);
2547 Value *LastprivateFlagArg = DupFunction->getArg(2);
2548 DestTaskArg->setName("dest_task");
2549 SrcTaskArg->setName("src_task");
2550 LastprivateFlagArg->setName("lastprivate_flag");
2551
2552 IRBuilderBase::InsertPointGuard Guard(Builder);
2553 Builder.SetInsertPoint(
2554 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2555
2556 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2557 Type *TaskWithPrivatesTy =
2558 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2559 Value *TaskPrivates = Builder.CreateGEP(
2560 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2561 Value *ContextPtr = Builder.CreateGEP(
2562 PrivatesTy, TaskPrivates,
2563 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2564 return ContextPtr;
2565 };
2566
2567 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2568 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2569
2570 DestTaskContextPtr->setName("destPtr");
2571 SrcTaskContextPtr->setName("srcPtr");
2572
2573 InsertPointTy AllocaIP(DupFunction->getEntryBlock().begin());
2574 InsertPointTy CodeGenIP = Builder.saveIP();
2575 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2576 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2577 if (!AfterIPOrError)
2578 return AfterIPOrError.takeError();
2579 Builder.restoreIP(*AfterIPOrError);
2580
2581 Builder.CreateRetVoid();
2582
2583 return DupFunction;
2584}
2585
2586OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2587 const LocationDescription &Loc, InsertPointTy AllocaIP,
2588 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2589 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2590 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2591 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2592 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2593 Value *TaskContextStructPtrVal, bool FreeAgent) {
2594
2595 if (!updateToLocation(Loc))
2596 return InsertPointTy();
2597
2598 uint32_t SrcLocStrSize;
2599 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2600 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2601
2602 BasicBlock *TaskloopExitBB =
2603 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2604 BasicBlock *TaskloopBodyBB =
2605 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2606 BasicBlock *TaskloopAllocaBB =
2607 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2608
2609 InsertPointTy TaskloopAllocaIP = TaskloopAllocaBB->begin();
2610 InsertPointTy TaskloopBodyIP = TaskloopBodyBB->begin();
2611
2612 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2613 return Err;
2614
2615 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2616 if (!result) {
2617 return result.takeError();
2618 }
2619
2620 llvm::CanonicalLoopInfo *CLI = result.get();
2621 auto OI = std::make_unique<OutlineInfo>();
2622 OI->EntryBB = TaskloopAllocaBB;
2623 OI->OuterAllocBB = AllocaIP.getNodeParent();
2624 OI->ExitBB = TaskloopExitBB;
2625 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2626 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2627
2628 // Add the thread ID argument.
2629 SmallVector<Instruction *> ToBeDeleted;
2630 // dummy instruction to be used as a fake argument
2631 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2632 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2633 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2634 TaskloopAllocaIP, "lb", false, true);
2635 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2636 TaskloopAllocaIP, "ub", false, true);
2637 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2638 TaskloopAllocaIP, "step", false, true);
2639 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2640 // aggregate struct
2641 OI->Inputs.insert(FakeLB);
2642 OI->Inputs.insert(FakeUB);
2643 OI->Inputs.insert(FakeStep);
2644 if (TaskContextStructPtrVal)
2645 OI->Inputs.insert(TaskContextStructPtrVal);
2646 assert(((TaskContextStructPtrVal && DupCB) ||
2647 (!TaskContextStructPtrVal && !DupCB)) &&
2648 "Task context struct ptr and duplication callback must be both set "
2649 "or both null");
2650
2651 // It isn't safe to run the duplication bodygen callback inside the post
2652 // outlining callback so this has to be run now before we know the real task
2653 // shareds structure type.
2654 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2655 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2656 Type *FakeSharedsTy = StructType::get(
2657 Builder.getContext(),
2658 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2659 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2660 FakeSharedsTy,
2661 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2662 if (!TaskDupFnOrErr) {
2663 return TaskDupFnOrErr.takeError();
2664 }
2665 Value *TaskDupFn = *TaskDupFnOrErr;
2666
2667 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2668 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2669 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2670 FakeSharedsTy, Final, Mergeable, Priority,
2671 NumOfCollapseLoops,
2672 FreeAgent](Function &OutlinedFn) mutable {
2673 // Replace the Stale CI by appropriate RTL function call.
2674 assert(OutlinedFn.hasOneUse() &&
2675 "there must be a single user for the outlined function");
2676 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2677
2678 /* Create the casting for the Bounds Values that can be used when outlining
2679 * to replace the uses of the fakes with real values */
2680 BasicBlock *CodeReplBB = StaleCI->getParent();
2681 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2682 Value *CastedLBVal =
2683 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2684 Value *CastedUBVal =
2685 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2686 Value *CastedStepVal =
2687 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2688
2689 Builder.SetInsertPoint(StaleCI);
2690
2691 // Gather the arguments for emitting the runtime call for
2692 // @__kmpc_omp_task_alloc
2693 Function *TaskAllocFn =
2694 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2695
2696 Value *ThreadID = getOrCreateThreadID(Ident);
2697
2698 if (!NoGroup) {
2699 // Emit runtime call for @__kmpc_taskgroup
2700 Function *TaskgroupFn =
2701 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2702 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2703 }
2704
2705 // `flags` Argument Configuration
2706 // Task is tied if (Flags & 1) == 1.
2707 // Task is untied if (Flags & 1) == 0.
2708 // Task is final if (Flags & 2) == 2.
2709 // Task is not final if (Flags & 2) == 0.
2710 // Task is mergeable if (Flags & 4) == 4.
2711 // Task is not mergeable if (Flags & 4) == 0.
2712 // Task is priority if (Flags & 32) == 32.
2713 // Task is not priority if (Flags & 32) == 0.
2714 // Task is free-agent eligible if (Flags & 128) == 128.
2715 // Task is not free-agent eligible if (Flags & 128) == 0.
2716 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2717 if (Final)
2718 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2719 if (Mergeable)
2720 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2721 if (Priority)
2722 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2723 if (FreeAgent)
2724 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2725
2726 Value *TaskSize = Builder.getInt64(
2727 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2728
2729 AllocaInst *ArgStructAlloca =
2731 assert(ArgStructAlloca &&
2732 "Unable to find the alloca instruction corresponding to arguments "
2733 "for extracted function");
2734 std::optional<TypeSize> ArgAllocSize =
2735 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2736 assert(ArgAllocSize &&
2737 "Unable to determine size of arguments for extracted function");
2738 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2739
2740 // Emit the @__kmpc_omp_task_alloc runtime call
2741 // The runtime call returns a pointer to an area where the task captured
2742 // variables must be copied before the task is run (TaskData)
2743 CallInst *TaskData = Builder.CreateCall(
2744 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2745 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2746 /*task_func=*/&OutlinedFn});
2747
2748 Value *Shareds = StaleCI->getArgOperand(1);
2749 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2750 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2751 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2752 SharedsSize);
2753 // Get the pointer to loop lb, ub, step from task ptr
2754 // and set up the lowerbound,upperbound and step values
2755 llvm::Value *Lb = Builder.CreateGEP(
2756 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2757
2758 llvm::Value *Ub = Builder.CreateGEP(
2759 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2760
2761 llvm::Value *Step = Builder.CreateGEP(
2762 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2763 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2764
2765 // set up the arguments for emitting kmpc_taskloop runtime call
2766 // setting values for ifval, nogroup, sched, grainsize, task_dup
2767 Value *IfCondVal =
2768 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2769 : Builder.getInt32(1);
2770 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2771 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2772 Value *NoGroupVal = Builder.getInt32(1);
2773 Value *SchedVal = Builder.getInt32(Sched);
2774 Value *GrainSizeVal =
2775 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2776 : Builder.getInt64(0);
2777 Value *TaskDup = TaskDupFn;
2778
2779 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2780 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2781
2782 // taskloop runtime call
2783 Function *TaskloopFn =
2784 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2785 Builder.CreateCall(TaskloopFn, Args);
2786
2787 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2788 // nogroup is not defined
2789 if (!NoGroup) {
2790 Function *EndTaskgroupFn =
2791 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2792 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2793 }
2794
2795 StaleCI->eraseFromParent();
2796
2797 Builder.SetInsertPoint(TaskloopAllocaBB->begin());
2798
2799 LoadInst *SharedsOutlined =
2800 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2801 OutlinedFn.getArg(1)->replaceUsesWithIf(
2802 SharedsOutlined,
2803 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2804
2805 Value *IV = CLI->getIndVar();
2806 Type *IVTy = IV->getType();
2807 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2808
2809 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2810 // UpperBound. These GEP's can be reused for loading the tasks respective
2811 // bounds.
2812 Value *TaskLB = nullptr;
2813 Value *TaskUB = nullptr;
2814 Value *TaskStep = nullptr;
2815 Value *LoadTaskLB = nullptr;
2816 Value *LoadTaskUB = nullptr;
2817 Value *LoadTaskStep = nullptr;
2818 for (Instruction &I : *TaskloopAllocaBB) {
2819 if (I.getOpcode() == Instruction::GetElementPtr) {
2820 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2821 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2822 switch (CI->getZExtValue()) {
2823 case 0:
2824 TaskLB = &I;
2825 break;
2826 case 1:
2827 TaskUB = &I;
2828 break;
2829 case 2:
2830 TaskStep = &I;
2831 break;
2832 }
2833 }
2834 } else if (I.getOpcode() == Instruction::Load) {
2835 LoadInst &Load = cast<LoadInst>(I);
2836 if (Load.getPointerOperand() == TaskLB) {
2837 assert(TaskLB != nullptr && "Expected value for TaskLB");
2838 LoadTaskLB = &I;
2839 } else if (Load.getPointerOperand() == TaskUB) {
2840 assert(TaskUB != nullptr && "Expected value for TaskUB");
2841 LoadTaskUB = &I;
2842 } else if (Load.getPointerOperand() == TaskStep) {
2843 assert(TaskStep != nullptr && "Expected value for TaskStep");
2844 LoadTaskStep = &I;
2845 }
2846 }
2847 }
2848
2849 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2850
2851 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2852 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2853 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2854 Value *TripCountMinusOne = Builder.CreateSDiv(
2855 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2856 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2857 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2858 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2859 // set the trip count in the CLI
2860 CLI->setTripCount(CastedTripCount);
2861
2862 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
2863
2864 if (NumOfCollapseLoops > 1) {
2865 llvm::SmallVector<User *> UsersToReplace;
2866 // When using the collapse clause, the bounds of the loop have to be
2867 // adjusted to properly represent the iterator of the outer loop.
2868 Value *IVPlusTaskLB = Builder.CreateAdd(
2869 CLI->getIndVar(),
2870 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2871 // To ensure every Use is correctly captured, we first want to record
2872 // which users to replace the value in, and then replace the value.
2873 for (auto IVUse = CLI->getIndVar()->uses().begin();
2874 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2875 User *IVUser = IVUse->getUser();
2876 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2877 if (Op->getOpcode() == Instruction::URem ||
2878 Op->getOpcode() == Instruction::UDiv) {
2879 UsersToReplace.push_back(IVUser);
2880 }
2881 }
2882 }
2883 for (User *User : UsersToReplace) {
2884 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2885 }
2886 } else {
2887 // The canonical loop is generated with a fixed lower bound. We need to
2888 // update the index calculation code to use the task's lower bound. The
2889 // generated code looks like this:
2890 // %omp_loop.iv = phi ...
2891 // ...
2892 // %tmp = mul [type] %omp_loop.iv, step
2893 // %user_index = add [type] tmp, lb
2894 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2895 // of the normalised induction variable:
2896 // 1. This one: converting the normalised IV to the user IV
2897 // 2. The increment (add)
2898 // 3. The comparison against the trip count (icmp)
2899 // (1) is the only use that is a mul followed by an add so this cannot
2900 // match other IR.
2901 assert(CLI->getIndVar()->getNumUses() == 3 &&
2902 "Canonical loop should have exactly three uses of the ind var");
2903 for (User *IVUser : CLI->getIndVar()->users()) {
2904 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2905 if (Mul->getOpcode() == Instruction::Mul) {
2906 for (User *MulUser : Mul->users()) {
2907 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2908 if (Add->getOpcode() == Instruction::Add) {
2909 Add->setOperand(1, CastedTaskLB);
2910 }
2911 }
2912 }
2913 }
2914 }
2915 }
2916 }
2917
2918 FakeLB->replaceAllUsesWith(CastedLBVal);
2919 FakeUB->replaceAllUsesWith(CastedUBVal);
2920 FakeStep->replaceAllUsesWith(CastedStepVal);
2921 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2922 I->eraseFromParent();
2923 }
2924 };
2925
2926 addOutlineInfo(std::move(OI));
2927 Builder.SetInsertPoint(TaskloopExitBB->begin());
2928 return Builder.saveIP();
2929}
2930
2933 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2935 llvm::Type::getInt32Ty(M.getContext()));
2936}
2937
2939 const LocationDescription &Loc, InsertPointTy AllocaIP,
2940 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2941 bool Tied, Value *Final, Value *IfCondition,
2942 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2943 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2944
2945 if (!updateToLocation(Loc))
2946 return InsertPointTy();
2947
2948 uint32_t SrcLocStrSize;
2949 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2950 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2951 // The current basic block is split into four basic blocks. After outlining,
2952 // they will be mapped as follows:
2953 // ```
2954 // def current_fn() {
2955 // current_basic_block:
2956 // br label %task.exit
2957 // task.exit:
2958 // ; instructions after task
2959 // }
2960 // def outlined_fn() {
2961 // task.alloca:
2962 // br label %task.body
2963 // task.body:
2964 // ret void
2965 // }
2966 // ```
2967 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2968 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2969 BasicBlock *TaskAllocaBB =
2970 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2971
2972 InsertPointTy TaskAllocaIP = TaskAllocaBB->begin();
2973 InsertPointTy TaskBodyIP = TaskBodyBB->begin();
2974 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2975 return Err;
2976
2977 auto OI = std::make_unique<OutlineInfo>();
2978 OI->EntryBB = TaskAllocaBB;
2979 OI->OuterAllocBB = AllocaIP.getNodeParent();
2980 OI->ExitBB = TaskExitBB;
2981 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2982 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2983
2984 // Add the thread ID argument.
2986 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2987 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2988
2989 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2990 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2991 TaskAllocaBB,
2992 ToBeDeleted](Function &OutlinedFn) mutable {
2993 // Replace the Stale CI by appropriate RTL function call.
2994 assert(OutlinedFn.hasOneUse() &&
2995 "there must be a single user for the outlined function");
2996 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2997
2998 // HasShareds is true if any variables are captured in the outlined region,
2999 // false otherwise.
3000 bool HasShareds = StaleCI->arg_size() > 1;
3001 Builder.SetInsertPoint(StaleCI);
3002
3003 // Gather the arguments for emitting the runtime call for
3004 // @__kmpc_omp_task_alloc
3005 Function *TaskAllocFn =
3006 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
3007
3008 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
3009 // call.
3010 Value *ThreadID = getOrCreateThreadID(Ident);
3011
3012 // Argument - `flags`
3013 // Task is tied iff (Flags & 1) == 1.
3014 // Task is untied iff (Flags & 1) == 0.
3015 // Task is final iff (Flags & 2) == 2.
3016 // Task is not final iff (Flags & 2) == 0.
3017 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
3018 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
3019 // Task is detachable iff (Flags & 64) == 64.
3020 // Task is not detachable iff (Flags & 64) == 0.
3021 // Task is priority iff (Flags & 32) == 32.
3022 // Task is not priority iff (Flags & 32) == 0.
3023 // Task is free-agent eligible iff (Flags & 128) == 128.
3024 // Task is not free-agent eligible iff (Flags & 128) == 0.
3025 // TODO: Handle the other flags.
3026 Value *Flags = Builder.getInt32(Tied);
3027 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
3028 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
3029 if (Final) {
3030 Value *FinalFlag =
3031 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3032 Flags = Builder.CreateOr(FinalFlag, Flags);
3033 }
3034
3035 if (Mergeable || UseMergedIf0Path)
3036 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3037 if (EventHandle)
3038 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3039 if (Priority)
3040 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3041 if (FreeAgent)
3042 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3043
3044 // Argument - `sizeof_kmp_task_t` (TaskSize)
3045 // Tasksize refers to the size in bytes of kmp_task_t data structure
3046 // including private vars accessed in task.
3047 // TODO: add kmp_task_t_with_privates (privates)
3048 Value *TaskSize = Builder.getInt64(
3049 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3050
3051 // Argument - `sizeof_shareds` (SharedsSize)
3052 // SharedsSize refers to the shareds array size in the kmp_task_t data
3053 // structure.
3054 Value *SharedsSize = Builder.getInt64(0);
3055 if (HasShareds) {
3056 AllocaInst *ArgStructAlloca =
3058 assert(ArgStructAlloca &&
3059 "Unable to find the alloca instruction corresponding to arguments "
3060 "for extracted function");
3061 std::optional<TypeSize> ArgAllocSize =
3062 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3063 assert(ArgAllocSize &&
3064 "Unable to determine size of arguments for extracted function");
3065 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3066 }
3067 // Emit the @__kmpc_omp_task_alloc runtime call
3068 // The runtime call returns a pointer to an area where the task captured
3069 // variables must be copied before the task is run (TaskData)
3071 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3072 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3073 /*task_func=*/&OutlinedFn});
3074
3075 if (Affinities.Count && Affinities.Info) {
3077 OMPRTL___kmpc_omp_reg_task_with_affinity);
3078
3079 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3080 Affinities.Count, Affinities.Info});
3081 }
3082
3083 // Emit detach clause initialization.
3084 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3085 // task_descriptor);
3086 if (EventHandle) {
3088 OMPRTL___kmpc_task_allow_completion_event);
3089 llvm::Value *EventVal =
3090 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3091 llvm::Value *EventHandleAddr =
3092 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3093 Builder.getPtrTy(0));
3094 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3095 Builder.CreateStore(EventVal, EventHandleAddr);
3096 }
3097 // Copy the arguments for outlined function
3098 if (HasShareds) {
3099 Value *Shareds = StaleCI->getArgOperand(1);
3100 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3101 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3102 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3103 SharedsSize);
3104 }
3105
3106 if (Priority) {
3107 //
3108 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3109 // we populate the priority information into the "kmp_task_t" here
3110 //
3111 // The struct "kmp_task_t" definition is available in kmp.h
3112 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3113 // data2 is used for priority
3114 //
3115 Type *Int32Ty = Builder.getInt32Ty();
3116 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3117 // kmp_task_t* => { ptr }
3118 Type *TaskPtr = StructType::get(VoidPtr);
3119 Value *TaskGEP =
3120 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3121 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3122 Type *TaskStructType = StructType::get(
3123 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3124 Value *PriorityData = Builder.CreateInBoundsGEP(
3125 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3126 // kmp_cmplrdata_t => { ptr, ptr }
3127 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3128 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3129 PriorityData, {Zero, Zero});
3130 Builder.CreateStore(Priority, CmplrData);
3131 }
3132
3133 Value *DepArray = nullptr;
3134 Value *NumDeps = nullptr;
3135 if (Dependencies.DepArray) {
3136 DepArray = Dependencies.DepArray;
3137 NumDeps = Dependencies.NumDeps;
3138 } else if (!Dependencies.Deps.empty()) {
3139 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3140 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3141 }
3142
3143 // In the presence of the `if` clause, the following IR is generated:
3144 // ...
3145 // %data = call @__kmpc_omp_task_alloc(...)
3146 // br i1 %if_condition, label %then, label %else
3147 // then:
3148 // call @__kmpc_omp_task(...)
3149 // br label %exit
3150 // else:
3151 // ;; Wait for resolution of dependencies, if any, before
3152 // ;; beginning the task
3153 // call @__kmpc_omp_wait_deps(...)
3154 // call @__kmpc_omp_task_begin_if0(...)
3155 // call @outlined_fn(...)
3156 // call @__kmpc_omp_task_complete_if0(...)
3157 // br label %exit
3158 // exit:
3159 // ...
3160 if (IfCondition && !UseMergedIf0Path) {
3161 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3162 // terminator.
3163 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3164 Instruction *IfTerminator =
3165 Builder.GetInsertPoint()->getParent()->getTerminator();
3166 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3167 Builder.SetInsertPoint(IfTerminator);
3168 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3169 &ElseTI);
3170 Builder.SetInsertPoint(ElseTI);
3171
3172 if (DepArray) {
3173 Function *TaskWaitFn =
3174 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3176 TaskWaitFn,
3177 {Ident, ThreadID, NumDeps, DepArray,
3178 ConstantInt::get(Builder.getInt32Ty(), 0),
3180 }
3181 Function *TaskBeginFn =
3182 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3183 Function *TaskCompleteFn =
3184 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3185 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3186 CallInst *CI = nullptr;
3187 if (HasShareds)
3188 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3189 else
3190 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3191 CI->setDebugLoc(StaleCI->getDebugLoc());
3192 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3193 Builder.SetInsertPoint(ThenTI);
3194 }
3195
3196 if (DepArray) {
3197 Function *TaskFn =
3198 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3200 TaskFn,
3201 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3202 ConstantInt::get(Builder.getInt32Ty(), 0),
3204
3205 } else {
3206 // Emit the @__kmpc_omp_task runtime call to spawn the task
3207 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3208 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3209 }
3210
3211 StaleCI->eraseFromParent();
3212
3213 Builder.SetInsertPoint(TaskAllocaBB->begin());
3214 if (HasShareds) {
3215 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3216 OutlinedFn.getArg(1)->replaceUsesWithIf(
3217 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3218 }
3219
3220 // The insert point may refer to one of the instructions about to be
3221 // deleted. It is not needed anymore so clear it instead of leaving it
3222 // dangling.
3223 Builder.ClearInsertionPoint();
3224 for (Instruction *I : llvm::reverse(ToBeDeleted))
3225 I->eraseFromParent();
3226 };
3227
3228 addOutlineInfo(std::move(OI));
3229 Builder.SetInsertPoint(TaskExitBB->begin());
3230
3231 return Builder.saveIP();
3232}
3233
3235 const LocationDescription &Loc, InsertPointTy AllocaIP,
3236 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3237 if (!updateToLocation(Loc))
3238 return InsertPointTy();
3239
3240 uint32_t SrcLocStrSize;
3241 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3242 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3243 Value *ThreadID = getOrCreateThreadID(Ident);
3244
3245 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3246 Function *TaskgroupFn =
3247 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3248 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3249
3250 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3251 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3252 return Err;
3253
3254 Builder.SetInsertPoint(TaskgroupExitBB);
3255 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3256 Function *EndTaskgroupFn =
3257 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3258 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3259
3260 return Builder.saveIP();
3261}
3262
3264 const LocationDescription &Loc, InsertPointTy AllocaIP,
3266 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3267 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3268
3269 if (!updateToLocation(Loc))
3270 return Loc.IP;
3271
3272 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3273
3274 // Each section is emitted as a switch case
3275 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3276 // -> OMP.createSection() which generates the IR for each section
3277 // Iterate through all sections and emit a switch construct:
3278 // switch (IV) {
3279 // case 0:
3280 // <SectionStmt[0]>;
3281 // break;
3282 // ...
3283 // case <NumSection> - 1:
3284 // <SectionStmt[<NumSection> - 1]>;
3285 // break;
3286 // }
3287 // ...
3288 // section_loop.after:
3289 // <FiniCB>;
3290 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3291 Builder.restoreIP(CodeGenIP);
3293 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3294 Function *CurFn = Continue->getParent();
3295 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3296
3297 unsigned CaseNumber = 0;
3298 for (auto SectionCB : SectionCBs) {
3300 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3301 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3302 Builder.SetInsertPoint(CaseBB);
3303 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3304 if (Error Err = SectionCB(InsertPointTy(), CaseEndBr->getIterator(), {}))
3305 return Err;
3306 CaseNumber++;
3307 }
3308 // remove the existing terminator from body BB since there can be no
3309 // terminators after switch/case
3310 return Error::success();
3311 };
3312 // Loop body ends here
3313 // LowerBound, UpperBound, and STride for createCanonicalLoop
3314 Type *I32Ty = Type::getInt32Ty(M.getContext());
3315 Value *LB = ConstantInt::get(I32Ty, 0);
3316 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3317 Value *ST = ConstantInt::get(I32Ty, 1);
3319 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3320 if (!LoopInfo)
3321 return LoopInfo.takeError();
3322
3323 InsertPointOrErrorTy WsloopIP =
3324 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3325 WorksharingLoopType::ForStaticLoop, !IsNowait);
3326 if (!WsloopIP)
3327 return WsloopIP.takeError();
3328 InsertPointTy AfterIP = *WsloopIP;
3329
3330 BasicBlock *LoopFini = AfterIP.getNodeParent()->getSinglePredecessor();
3331 assert(LoopFini && "Bad structure of static workshare loop finalization");
3332
3333 // Apply the finalization callback in LoopAfterBB
3334 auto FiniInfo = FinalizationStack.pop_back_val();
3335 assert(FiniInfo.DK == OMPD_sections &&
3336 "Unexpected finalization stack state!");
3337 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3338 return Err;
3339
3340 return AfterIP;
3341}
3342
3345 BodyGenCallbackTy BodyGenCB,
3346 FinalizeCallbackTy FiniCB) {
3347 if (!updateToLocation(Loc))
3348 return Loc.IP;
3349
3350 auto FiniCBWrapper = [&](InsertPointTy IP) {
3351 if (IP != IP.getNodeParent()->end())
3352 return FiniCB(IP);
3353 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3354 // will fail because that function requires the Finalization Basic Block to
3355 // have a terminator, which is already removed by EmitOMPRegionBody.
3356 // IP is currently at cancelation block.
3357 // We need to backtrack to the condition block to fetch
3358 // the exit block and create a branch from cancelation
3359 // to exit block.
3361 Builder.restoreIP(IP);
3362 auto *CaseBB = Loc.IP.getNodeParent();
3363 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3364 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3365 Instruction *I = Builder.CreateBr(ExitBB);
3366 IP = I->getIterator();
3367 return FiniCB(IP);
3368 };
3369
3370 Directive OMPD = Directive::OMPD_sections;
3371 // Since we are using Finalization Callback here, HasFinalize
3372 // and IsCancellable have to be true
3373 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3374 /*Conditional*/ false, /*hasFinalize*/ true,
3375 /*IsCancellable*/ true);
3376}
3377
3383
3384Value *OpenMPIRBuilder::getGPUThreadID() {
3387 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3388 {});
3389}
3390
3391Value *OpenMPIRBuilder::getGPUWarpSize() {
3393 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3394}
3395
3396Value *OpenMPIRBuilder::getNVPTXWarpID() {
3397 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3398 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3399}
3400
3401Value *OpenMPIRBuilder::getNVPTXLaneID() {
3402 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3403 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3404 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3405 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3406 "nvptx_lane_id");
3407}
3408
3409Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3410 Type *ToType) {
3411 Type *FromType = From->getType();
3412 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3413 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3414 assert(FromSize > 0 && "From size must be greater than zero");
3415 assert(ToSize > 0 && "To size must be greater than zero");
3416 if (FromType == ToType)
3417 return From;
3418 if (FromSize == ToSize)
3419 return Builder.CreateBitCast(From, ToType);
3420 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3421 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3422 InsertPointTy SaveIP = Builder.saveIP();
3423 Builder.restoreIP(AllocaIP);
3424 Value *CastItem = Builder.CreateAlloca(ToType);
3425 Builder.restoreIP(SaveIP);
3426
3427 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3428 CastItem, Builder.getPtrTy(0));
3429 Builder.CreateStore(From, ValCastItem);
3430 return Builder.CreateLoad(ToType, CastItem);
3431}
3432
3433Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3434 Value *Element,
3435 Type *ElementType,
3436 Value *Offset) {
3437 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3438 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3439
3440 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3441 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3442 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3443 Value *WarpSize =
3444 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3446 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3447 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3448 Value *WarpSizeCast =
3449 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3450 Value *ShuffleCall =
3451 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3452 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3453 // down to the requested element type, otherwise storing the result would
3454 // write past the end of an element narrower than the shuffle width.
3455 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3456}
3457
3458void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3459 Value *DstAddr, Type *ElemType,
3460 Value *Offset, Type *ReductionArrayTy,
3461 bool IsByRefElem) {
3462 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3463 // Create the loop over the big sized data.
3464 // ptr = (void*)Elem;
3465 // ptrEnd = (void*) Elem + 1;
3466 // Step = 8;
3467 // while (ptr + Step < ptrEnd)
3468 // shuffle((int64_t)*ptr);
3469 // Step = 4;
3470 // while (ptr + Step < ptrEnd)
3471 // shuffle((int32_t)*ptr);
3472 // ...
3473 Type *IndexTy = Builder.getIndexTy(
3474 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3475 Value *ElemPtr = DstAddr;
3476 Value *Ptr = SrcAddr;
3477 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3478 if (Size < IntSize)
3479 continue;
3480 Type *IntType = Builder.getIntNTy(IntSize * 8);
3481 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3482 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3483 Value *SrcAddrGEP =
3484 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3485 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3486 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3487
3488 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3489 if ((Size / IntSize) > 1) {
3490 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3491 SrcAddrGEP, Builder.getPtrTy());
3492 BasicBlock *PreCondBB =
3493 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3494 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3495 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3496 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3497 emitBlock(PreCondBB, CurFunc);
3498 PHINode *PhiSrc =
3499 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3500 PhiSrc->addIncoming(Ptr, CurrentBB);
3501 PHINode *PhiDest =
3502 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3503 PhiDest->addIncoming(ElemPtr, CurrentBB);
3504 Ptr = PhiSrc;
3505 ElemPtr = PhiDest;
3506 Value *PtrDiff = Builder.CreatePtrDiff(
3507 Builder.getInt8Ty(), PtrEnd,
3508 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3509 Builder.CreateCondBr(
3510 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3511 ExitBB);
3512 emitBlock(ThenBB, CurFunc);
3513 Value *Res = createRuntimeShuffleFunction(
3514 AllocaIP,
3515 Builder.CreateAlignedLoad(
3516 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3517 IntType, Offset);
3518 Builder.CreateAlignedStore(Res, ElemPtr,
3519 M.getDataLayout().getPrefTypeAlign(ElemType));
3520 Value *LocalPtr =
3521 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3522 Value *LocalElemPtr =
3523 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3524 PhiSrc->addIncoming(LocalPtr, ThenBB);
3525 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3526 emitBranch(PreCondBB);
3527 emitBlock(ExitBB, CurFunc);
3528 } else {
3529 // The shuffled value comes back as the chunk's integer type, so the
3530 // store covers exactly this chunk regardless of what ElemType is.
3531 Value *Res = createRuntimeShuffleFunction(
3532 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3533 Builder.CreateStore(Res, ElemPtr);
3534 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3535 ElemPtr =
3536 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3537 }
3538 Size = Size % IntSize;
3539 }
3540}
3541
3542Error OpenMPIRBuilder::emitReductionListCopy(
3543 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3544 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3545 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3546 Type *IndexTy = Builder.getIndexTy(
3547 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3548 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3549
3550 // Iterates, element-by-element, through the source Reduce list and
3551 // make a copy.
3552 for (auto En : enumerate(ReductionInfos)) {
3553 const ReductionInfo &RI = En.value();
3554 Value *SrcElementAddr = nullptr;
3555 AllocaInst *DestAlloca = nullptr;
3556 Value *DestElementAddr = nullptr;
3557 Value *DestElementPtrAddr = nullptr;
3558 // Should we shuffle in an element from a remote lane?
3559 bool ShuffleInElement = false;
3560 // Set to true to update the pointer in the dest Reduce list to a
3561 // newly created element.
3562 bool UpdateDestListPtr = false;
3563
3564 // Step 1.1: Get the address for the src element in the Reduce list.
3565 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3566 ReductionArrayTy, SrcBase,
3567 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3568 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3569
3570 // Step 1.2: Create a temporary to store the element in the destination
3571 // Reduce list.
3572 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3573 ReductionArrayTy, DestBase,
3574 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3575 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3576 switch (Action) {
3578 InsertPointTy CurIP = Builder.saveIP();
3579 Builder.restoreIP(AllocaIP);
3580
3581 Type *DestAllocaType =
3582 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3583 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3584 ".omp.reduction.element");
3585 DestAlloca->setAlignment(
3586 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3587 DestElementAddr = DestAlloca;
3588 DestElementAddr =
3589 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3590 DestElementAddr->getName() + ".ascast");
3591 Builder.restoreIP(CurIP);
3592 ShuffleInElement = true;
3593 UpdateDestListPtr = true;
3594 break;
3595 }
3597 DestElementAddr =
3598 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3599 break;
3600 }
3601 }
3602
3603 // Now that all active lanes have read the element in the
3604 // Reduce list, shuffle over the value from the remote lane.
3605 if (ShuffleInElement) {
3606 Type *ShuffleType = RI.ElementType;
3607 Value *ShuffleSrcAddr = SrcElementAddr;
3608 Value *ShuffleDestAddr = DestElementAddr;
3609 AllocaInst *LocalStorage = nullptr;
3610
3611 if (IsByRefElem) {
3612 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3613 assert(RI.ByRefAllocatedType &&
3614 "Expected by-ref allocated type to be set");
3615 // For by-ref reductions, we need to copy from the remote lane the
3616 // actual value of the partial reduction computed by that remote lane;
3617 // rather than, for example, a pointer to that data or, even worse, a
3618 // pointer to the descriptor of the by-ref reduction element.
3619 ShuffleType = RI.ByRefElementType;
3620
3621 if (RI.DataPtrPtrGen) {
3622 // Descriptor-based by-ref: extract data pointer from descriptor.
3623 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3624 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3625
3626 if (!GenResult)
3627 return GenResult.takeError();
3628
3629 ShuffleSrcAddr =
3630 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3631
3632 {
3633 InsertPointTy OldIP = Builder.saveIP();
3634 Builder.restoreIP(AllocaIP);
3635
3636 LocalStorage = Builder.CreateAlloca(ShuffleType);
3637 Builder.restoreIP(OldIP);
3638 ShuffleDestAddr = LocalStorage;
3639 }
3640 } else {
3641 // Non-descriptor by-ref: the pointer already references data
3642 // directly. Shuffle into the destination alloca.
3643 ShuffleDestAddr = DestElementAddr;
3644 }
3645 }
3646
3647 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3648 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3649
3650 if (IsByRefElem && RI.DataPtrPtrGen) {
3651 // Copy descriptor from source and update base_ptr to shuffled data
3652 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3653 DestAlloca, Builder.getPtrTy(), ".ascast");
3654
3655 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3656 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3657 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3658
3659 if (!GenResult)
3660 return GenResult.takeError();
3661 }
3662 } else {
3663 switch (RI.EvaluationKind) {
3664 case EvalKind::Scalar: {
3665 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3666 // Store the source element value to the dest element address.
3667 Builder.CreateStore(Elem, DestElementAddr);
3668 break;
3669 }
3670 case EvalKind::Complex: {
3671 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3672 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3673 Value *SrcReal = Builder.CreateLoad(
3674 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3675 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3676 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3677 Value *SrcImg = Builder.CreateLoad(
3678 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3679
3680 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3681 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3682 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3683 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3684 Builder.CreateStore(SrcReal, DestRealPtr);
3685 Builder.CreateStore(SrcImg, DestImgPtr);
3686 break;
3687 }
3688 case EvalKind::Aggregate: {
3689 Value *SizeVal = Builder.getInt64(
3690 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3691 Builder.CreateMemCpy(
3692 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3693 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3694 SizeVal, false);
3695 break;
3696 }
3697 };
3698 }
3699
3700 // Step 3.1: Modify reference in dest Reduce list as needed.
3701 // Modifying the reference in Reduce list to point to the newly
3702 // created element. The element is live in the current function
3703 // scope and that of functions it invokes (i.e., reduce_function).
3704 // RemoteReduceData[i] = (void*)&RemoteElem
3705 if (UpdateDestListPtr) {
3706 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3707 DestElementAddr, Builder.getPtrTy(),
3708 DestElementAddr->getName() + ".ascast");
3709 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3710 }
3711 }
3712
3713 return Error::success();
3714}
3715
3716Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3717 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3718 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3719 IRBuilder<>::InsertPointGuard IPG(Builder);
3720 LLVMContext &Ctx = M.getContext();
3721 FunctionType *FuncTy = FunctionType::get(
3722 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3723 /* IsVarArg */ false);
3724 Function *WcFunc =
3726 "_omp_reduction_inter_warp_copy_func", &M);
3727 WcFunc->setCallingConv(Config.getRuntimeCC());
3728 WcFunc->setAttributes(FuncAttrs);
3729 WcFunc->addParamAttr(0, Attribute::NoUndef);
3730 WcFunc->addParamAttr(1, Attribute::NoUndef);
3731 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3732 Builder.SetInsertPoint(EntryBB);
3733 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3734
3735 // ReduceList: thread local Reduce list.
3736 // At the stage of the computation when this function is called, partially
3737 // aggregated values reside in the first lane of every active warp.
3738 Argument *ReduceListArg = WcFunc->getArg(0);
3739 // NumWarps: number of warps active in the parallel region. This could
3740 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3741 Argument *NumWarpsArg = WcFunc->getArg(1);
3742
3743 // This array is used as a medium to transfer, one reduce element at a time,
3744 // the data from the first lane of every warp to lanes in the first warp
3745 // in order to perform the final step of a reduction in a parallel region
3746 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3747 // for reduced latency, as well as to have a distinct copy for concurrently
3748 // executing target regions. The array is declared with common linkage so
3749 // as to be shared across compilation units.
3750 StringRef TransferMediumName =
3751 "__openmp_nvptx_data_transfer_temporary_storage";
3752 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3753 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3754 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3755 if (!TransferMedium) {
3756 TransferMedium = new GlobalVariable(
3757 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3758 UndefValue::get(ArrayTy), TransferMediumName,
3759 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3760 /*AddressSpace=*/3);
3761 }
3762
3763 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3764 Value *GPUThreadID = getGPUThreadID();
3765 // nvptx_lane_id = nvptx_id % warpsize
3766 Value *LaneID = getNVPTXLaneID();
3767 // nvptx_warp_id = nvptx_id / warpsize
3768 Value *WarpID = getNVPTXWarpID();
3769
3770 InsertPointTy AllocaIP = Builder.GetInsertBlock()->getFirstInsertionPt();
3771 Type *Arg0Type = ReduceListArg->getType();
3772 Type *Arg1Type = NumWarpsArg->getType();
3773 Builder.restoreIP(AllocaIP);
3774 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3775 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3776 AllocaInst *NumWarpsAlloca =
3777 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3778 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3779 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3780 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3781 NumWarpsAlloca, Builder.getPtrTy(0),
3782 NumWarpsAlloca->getName() + ".ascast");
3783 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3784 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3785 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3786 InsertPointTy CodeGenIP =
3787 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3788 Builder.restoreIP(CodeGenIP);
3789
3790 Value *ReduceList =
3791 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3792
3793 for (auto En : enumerate(ReductionInfos)) {
3794 //
3795 // Warp master copies reduce element to transfer medium in __shared__
3796 // memory.
3797 //
3798 const ReductionInfo &RI = En.value();
3799 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3800 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3801 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3802 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3803 Type *CType = Builder.getIntNTy(TySize * 8);
3804
3805 unsigned NumIters = RealTySize / TySize;
3806 if (NumIters == 0)
3807 continue;
3808 Value *Cnt = nullptr;
3809 Value *CntAddr = nullptr;
3810 BasicBlock *PrecondBB = nullptr;
3811 BasicBlock *ExitBB = nullptr;
3812 if (NumIters > 1) {
3813 CodeGenIP = Builder.saveIP();
3814 Builder.restoreIP(AllocaIP);
3815 CntAddr =
3816 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3817
3818 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3819 CntAddr->getName() + ".ascast");
3820 Builder.restoreIP(CodeGenIP);
3821 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3822 CntAddr,
3823 /*Volatile=*/false);
3824 PrecondBB = BasicBlock::Create(Ctx, "precond");
3825 ExitBB = BasicBlock::Create(Ctx, "exit");
3826 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3827 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3828 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3829 /*Volatile=*/false);
3830 Value *Cmp = Builder.CreateICmpULT(
3831 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3832 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3833 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3834 }
3835
3836 // kmpc_barrier.
3837 InsertPointOrErrorTy BarrierIP1 =
3839 omp::Directive::OMPD_unknown,
3840 /* ForceSimpleCall */ false,
3841 /* CheckCancelFlag */ true);
3842 if (!BarrierIP1)
3843 return BarrierIP1.takeError();
3844 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3845 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3846 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3847
3848 // if (lane_id == 0)
3849 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3850 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3851 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3852
3853 // Reduce element = LocalReduceList[i]
3854 auto *RedListArrayTy =
3855 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3856 Type *IndexTy = Builder.getIndexTy(
3857 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3858 Value *ElemPtrPtr =
3859 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3860 {ConstantInt::get(IndexTy, 0),
3861 ConstantInt::get(IndexTy, En.index())});
3862 // elemptr = ((CopyType*)(elemptrptr)) + I
3863 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3864
3865 if (IsByRefElem && RI.DataPtrPtrGen) {
3866 InsertPointOrErrorTy GenRes =
3867 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3868
3869 if (!GenRes)
3870 return GenRes.takeError();
3871
3872 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3873 }
3874
3875 if (NumIters > 1)
3876 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3877
3878 // Get pointer to location in transfer medium.
3879 // MediumPtr = &medium[warp_id]
3880 Value *MediumPtr = Builder.CreateInBoundsGEP(
3881 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3882 // elem = *elemptr
3883 //*MediumPtr = elem
3884 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3885 // Store the source element value to the dest element address.
3886 Builder.CreateStore(Elem, MediumPtr,
3887 /*IsVolatile*/ true);
3888 Builder.CreateBr(MergeBB);
3889
3890 // else
3891 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3892 Builder.CreateBr(MergeBB);
3893
3894 // endif
3895 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3896 InsertPointOrErrorTy BarrierIP2 =
3898 omp::Directive::OMPD_unknown,
3899 /* ForceSimpleCall */ false,
3900 /* CheckCancelFlag */ true);
3901 if (!BarrierIP2)
3902 return BarrierIP2.takeError();
3903
3904 // Warp 0 copies reduce element from transfer medium
3905 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3906 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3907 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3908
3909 Value *NumWarpsVal =
3910 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3911 // Up to 32 threads in warp 0 are active.
3912 Value *IsActiveThread =
3913 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3914 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3915
3916 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3917
3918 // SecMediumPtr = &medium[tid]
3919 // SrcMediumVal = *SrcMediumPtr
3920 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3921 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3922 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3923 Value *TargetElemPtrPtr =
3924 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3925 {ConstantInt::get(IndexTy, 0),
3926 ConstantInt::get(IndexTy, En.index())});
3927 Value *TargetElemPtrVal =
3928 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3929 Value *TargetElemPtr = TargetElemPtrVal;
3930
3931 if (IsByRefElem && RI.DataPtrPtrGen) {
3932 InsertPointOrErrorTy GenRes =
3933 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3934
3935 if (!GenRes)
3936 return GenRes.takeError();
3937
3938 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3939 }
3940
3941 if (NumIters > 1)
3942 TargetElemPtr =
3943 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3944
3945 // *TargetElemPtr = SrcMediumVal;
3946 Value *SrcMediumValue =
3947 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3948 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3949 Builder.CreateBr(W0MergeBB);
3950
3951 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3952 Builder.CreateBr(W0MergeBB);
3953
3954 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3955
3956 if (NumIters > 1) {
3957 Cnt = Builder.CreateNSWAdd(
3958 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3959 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3960
3961 auto *CurFn = Builder.GetInsertBlock()->getParent();
3962 emitBranch(PrecondBB);
3963 emitBlock(ExitBB, CurFn);
3964 }
3965 RealTySize %= TySize;
3966 }
3967 }
3968
3969 Builder.CreateRetVoid();
3970
3971 return WcFunc;
3972}
3973
3974Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3975 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3976 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3977 LLVMContext &Ctx = M.getContext();
3978 IRBuilder<>::InsertPointGuard IPG(Builder);
3979 FunctionType *FuncTy =
3980 FunctionType::get(Builder.getVoidTy(),
3981 {Builder.getPtrTy(), Builder.getInt16Ty(),
3982 Builder.getInt16Ty(), Builder.getInt16Ty()},
3983 /* IsVarArg */ false);
3984 Function *SarFunc =
3986 "_omp_reduction_shuffle_and_reduce_func", &M);
3987 SarFunc->setCallingConv(Config.getRuntimeCC());
3988 SarFunc->setAttributes(FuncAttrs);
3989 SarFunc->addParamAttr(0, Attribute::NoUndef);
3990 SarFunc->addParamAttr(1, Attribute::NoUndef);
3991 SarFunc->addParamAttr(2, Attribute::NoUndef);
3992 SarFunc->addParamAttr(3, Attribute::NoUndef);
3993 SarFunc->addParamAttr(1, Attribute::SExt);
3994 SarFunc->addParamAttr(2, Attribute::SExt);
3995 SarFunc->addParamAttr(3, Attribute::SExt);
3996 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3997 Builder.SetInsertPoint(EntryBB);
3998 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3999
4000 // Thread local Reduce list used to host the values of data to be reduced.
4001 Argument *ReduceListArg = SarFunc->getArg(0);
4002 // Current lane id; could be logical.
4003 Argument *LaneIDArg = SarFunc->getArg(1);
4004 // Offset of the remote source lane relative to the current lane.
4005 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
4006 // Algorithm version. This is expected to be known at compile time.
4007 Argument *AlgoVerArg = SarFunc->getArg(3);
4008
4009 Type *ReduceListArgType = ReduceListArg->getType();
4010 Type *LaneIDArgType = LaneIDArg->getType();
4011 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
4012 Value *ReduceListAlloca = Builder.CreateAlloca(
4013 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
4014 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4015 LaneIDArg->getName() + ".addr");
4016 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
4017 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
4018 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4019 AlgoVerArg->getName() + ".addr");
4020 ArrayType *RedListArrayTy =
4021 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4022
4023 // Create a local thread-private variable to host the Reduce list
4024 // from a remote lane.
4025 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
4026 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
4027
4028 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4029 ReduceListAlloca, ReduceListArgType,
4030 ReduceListAlloca->getName() + ".ascast");
4031 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4032 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4033 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4034 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4035 RemoteLaneOffsetAlloca->getName() + ".ascast");
4036 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4037 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4038 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4039 RemoteReductionListAlloca, Builder.getPtrTy(),
4040 RemoteReductionListAlloca->getName() + ".ascast");
4041
4042 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4043 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4044 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4045 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4046
4047 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4048 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4049 Value *RemoteLaneOffset =
4050 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4051 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4052
4053 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4054
4055 // This loop iterates through the list of reduce elements and copies,
4056 // element by element, from a remote lane in the warp to RemoteReduceList,
4057 // hosted on the thread's stack.
4058 Error EmitRedLsCpRes = emitReductionListCopy(
4059 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4060 ReduceList, RemoteListAddrCast, IsByRef,
4061 {RemoteLaneOffset, nullptr, nullptr});
4062
4063 if (EmitRedLsCpRes)
4064 return EmitRedLsCpRes;
4065
4066 // The actions to be performed on the Remote Reduce list is dependent
4067 // on the algorithm version.
4068 //
4069 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4070 // LaneId % 2 == 0 && Offset > 0):
4071 // do the reduction value aggregation
4072 //
4073 // The thread local variable Reduce list is mutated in place to host the
4074 // reduced data, which is the aggregated value produced from local and
4075 // remote lanes.
4076 //
4077 // Note that AlgoVer is expected to be a constant integer known at compile
4078 // time.
4079 // When AlgoVer==0, the first conjunction evaluates to true, making
4080 // the entire predicate true during compile time.
4081 // When AlgoVer==1, the second conjunction has only the second part to be
4082 // evaluated during runtime. Other conjunctions evaluates to false
4083 // during compile time.
4084 // When AlgoVer==2, the third conjunction has only the second part to be
4085 // evaluated during runtime. Other conjunctions evaluates to false
4086 // during compile time.
4087 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4088 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4089 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4090 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4091 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4092 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4093 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4094 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4095 Value *RemoteOffsetComp =
4096 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4097 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4098 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4099 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4100
4101 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4102 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4103 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4104
4105 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4106 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4107 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4108 ReduceList, Builder.getPtrTy());
4109 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4110 RemoteListAddrCast, Builder.getPtrTy());
4111 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4112 ->addFnAttr(Attribute::NoUnwind);
4113 Builder.CreateBr(MergeBB);
4114
4115 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4116 Builder.CreateBr(MergeBB);
4117
4118 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4119
4120 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4121 // Reduce list.
4122 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4123 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4124 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4125
4126 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4127 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4128 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4129 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4130
4131 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4132
4133 EmitRedLsCpRes = emitReductionListCopy(
4134 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4135 RemoteListAddrCast, ReduceList, IsByRef);
4136
4137 if (EmitRedLsCpRes)
4138 return EmitRedLsCpRes;
4139
4140 Builder.CreateBr(CpyMergeBB);
4141
4142 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4143 Builder.CreateBr(CpyMergeBB);
4144
4145 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4146
4147 Builder.CreateRetVoid();
4148
4149 return SarFunc;
4150}
4151
4153OpenMPIRBuilder::generateReductionDescriptor(
4154 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4155 Type *DescriptorType,
4156 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4157 DataPtrPtrGen) {
4158
4159 // Copy the source descriptor to preserve all metadata (rank, extents,
4160 // strides, etc.)
4161 Value *DescriptorSize =
4162 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4163 Builder.CreateMemCpy(
4164 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4165 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4166 DescriptorSize);
4167
4168 // Update the base pointer field to point to the local shuffled data
4169 Value *DataPtrField;
4170 InsertPointOrErrorTy GenResult =
4171 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4172
4173 if (!GenResult)
4174 return GenResult.takeError();
4175
4176 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4177 DataPtr, Builder.getPtrTy(), ".ascast"),
4178 DataPtrField);
4179
4180 return Builder.saveIP();
4181}
4182
4183Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4184 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4185 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4186 InsertPointTy OldIP = Builder.saveIP();
4187 Builder.restoreIP(AllocaIP);
4188
4189 AllocaInst *DescriptorAlloca =
4190 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4191 DescriptorAlloca->setAlignment(
4192 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4193 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4194 DescriptorAlloca, DescriptorPtrTy,
4195 DescriptorAlloca->getName() + ".ascast");
4196
4197 Builder.restoreIP(OldIP);
4198
4199 InsertPointOrErrorTy GenResult =
4200 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4201 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4202 if (!GenResult)
4203 return GenResult.takeError();
4204
4205 return DescriptorAddr;
4206}
4207
4208Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4209 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4210 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4211 IRBuilder<>::InsertPointGuard IPG(Builder);
4212 LLVMContext &Ctx = M.getContext();
4213 FunctionType *FuncTy = FunctionType::get(
4214 Builder.getVoidTy(),
4215 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4216 /* IsVarArg */ false);
4217 Function *LtGCFunc =
4219 "_omp_reduction_list_to_global_copy_func", &M);
4220 LtGCFunc->setAttributes(FuncAttrs);
4221 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4222 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4223 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4224
4225 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4226 Builder.SetInsertPoint(EntryBlock);
4227 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4228
4229 // Buffer: global reduction buffer.
4230 Argument *BufferArg = LtGCFunc->getArg(0);
4231 // Idx: index of the buffer.
4232 Argument *IdxArg = LtGCFunc->getArg(1);
4233 // ReduceList: thread local Reduce list.
4234 Argument *ReduceListArg = LtGCFunc->getArg(2);
4235
4236 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4237 BufferArg->getName() + ".addr");
4238 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4239 IdxArg->getName() + ".addr");
4240 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4241 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4242 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4243 BufferArgAlloca, Builder.getPtrTy(),
4244 BufferArgAlloca->getName() + ".ascast");
4245 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4246 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4247 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4248 ReduceListArgAlloca, Builder.getPtrTy(),
4249 ReduceListArgAlloca->getName() + ".ascast");
4250
4251 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4252 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4253 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4254
4255 Value *LocalReduceList =
4256 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4257 Value *BufferArgVal =
4258 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4259 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4260 Type *IndexTy = Builder.getIndexTy(
4261 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4262 for (auto En : enumerate(ReductionInfos)) {
4263 const ReductionInfo &RI = En.value();
4264 auto *RedListArrayTy =
4265 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4266 // Reduce element = LocalReduceList[i]
4267 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4268 RedListArrayTy, LocalReduceList,
4269 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4270 // elemptr = ((CopyType*)(elemptrptr)) + I
4271 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4272
4273 // Global = Buffer.VD[Idx];
4274 Value *BufferVD =
4275 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4276 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4277 ReductionsBufferTy, BufferVD, 0, En.index());
4278
4279 switch (RI.EvaluationKind) {
4280 case EvalKind::Scalar: {
4281 Value *TargetElement;
4282
4283 if (IsByRef.empty() || !IsByRef[En.index()]) {
4284 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4285 } else {
4286 if (RI.DataPtrPtrGen) {
4287 InsertPointOrErrorTy GenResult =
4288 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4289
4290 if (!GenResult)
4291 return GenResult.takeError();
4292
4293 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4294 }
4295 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4296 }
4297
4298 Builder.CreateStore(TargetElement, GlobVal);
4299 break;
4300 }
4301 case EvalKind::Complex: {
4302 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4303 RI.ElementType, ElemPtr, 0, 0, ".realp");
4304 Value *SrcReal = Builder.CreateLoad(
4305 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4306 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4307 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4308 Value *SrcImg = Builder.CreateLoad(
4309 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4310
4311 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4312 RI.ElementType, GlobVal, 0, 0, ".realp");
4313 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4314 RI.ElementType, GlobVal, 0, 1, ".imagp");
4315 Builder.CreateStore(SrcReal, DestRealPtr);
4316 Builder.CreateStore(SrcImg, DestImgPtr);
4317 break;
4318 }
4319 case EvalKind::Aggregate: {
4320 Value *SizeVal =
4321 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4322 Builder.CreateMemCpy(
4323 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4324 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4325 break;
4326 }
4327 }
4328 }
4329
4330 Builder.CreateRetVoid();
4331 return LtGCFunc;
4332}
4333
4334Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4335 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4336 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4337 IRBuilder<>::InsertPointGuard IPG(Builder);
4338 LLVMContext &Ctx = M.getContext();
4339 FunctionType *FuncTy = FunctionType::get(
4340 Builder.getVoidTy(),
4341 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4342 /* IsVarArg */ false);
4343 Function *LtGRFunc =
4345 "_omp_reduction_list_to_global_reduce_func", &M);
4346 LtGRFunc->setAttributes(FuncAttrs);
4347 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4348 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4349 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4350
4351 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4352 Builder.SetInsertPoint(EntryBlock);
4353 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4354
4355 // Buffer: global reduction buffer.
4356 Argument *BufferArg = LtGRFunc->getArg(0);
4357 // Idx: index of the buffer.
4358 Argument *IdxArg = LtGRFunc->getArg(1);
4359 // ReduceList: thread local Reduce list.
4360 Argument *ReduceListArg = LtGRFunc->getArg(2);
4361
4362 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4363 BufferArg->getName() + ".addr");
4364 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4365 IdxArg->getName() + ".addr");
4366 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4367 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4368 auto *RedListArrayTy =
4369 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4370
4371 // 1. Build a list of reduction variables.
4372 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4373 Value *LocalReduceList =
4374 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4375
4376 InsertPointTy AllocaIP(EntryBlock->begin());
4377
4378 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4379 BufferArgAlloca, Builder.getPtrTy(),
4380 BufferArgAlloca->getName() + ".ascast");
4381 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4382 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4383 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4384 ReduceListArgAlloca, Builder.getPtrTy(),
4385 ReduceListArgAlloca->getName() + ".ascast");
4386 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4387 LocalReduceList, Builder.getPtrTy(),
4388 LocalReduceList->getName() + ".ascast");
4389
4390 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4391 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4392 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4393
4394 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4395 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4396 Type *IndexTy = Builder.getIndexTy(
4397 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4398 for (auto En : enumerate(ReductionInfos)) {
4399 const ReductionInfo &RI = En.value();
4400
4401 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4402 RedListArrayTy, LocalReduceListAddrCast,
4403 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4404 Value *BufferVD =
4405 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4406 // Global = Buffer.VD[Idx];
4407 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4408 ReductionsBufferTy, BufferVD, 0, En.index());
4409
4410 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4411 // Get source descriptor from the reduce list argument
4412 Value *ReduceList =
4413 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4414 Value *SrcElementPtrPtr =
4415 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4416 {ConstantInt::get(IndexTy, 0),
4417 ConstantInt::get(IndexTy, En.index())});
4418 Value *SrcDescriptorAddr =
4419 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4420
4421 // Copy descriptor from source and update base_ptr to global buffer data
4422 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4423 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4424 if (!ByRefAlloc)
4425 return ByRefAlloc.takeError();
4426
4427 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4428 } else {
4429 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4430 }
4431 }
4432
4433 // Call reduce_function(GlobalReduceList, ReduceList)
4434 Value *ReduceList =
4435 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4436 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4437 ->addFnAttr(Attribute::NoUnwind);
4438 Builder.CreateRetVoid();
4439 return LtGRFunc;
4440}
4441
4442Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4443 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4444 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4445 IRBuilder<>::InsertPointGuard IPG(Builder);
4446 LLVMContext &Ctx = M.getContext();
4447 FunctionType *FuncTy = FunctionType::get(
4448 Builder.getVoidTy(),
4449 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4450 /* IsVarArg */ false);
4451 Function *GtLCFunc =
4453 "_omp_reduction_global_to_list_copy_func", &M);
4454 GtLCFunc->setAttributes(FuncAttrs);
4455 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4456 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4457 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4458
4459 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4460 Builder.SetInsertPoint(EntryBlock);
4461 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4462
4463 // Buffer: global reduction buffer.
4464 Argument *BufferArg = GtLCFunc->getArg(0);
4465 // Idx: index of the buffer.
4466 Argument *IdxArg = GtLCFunc->getArg(1);
4467 // ReduceList: thread local Reduce list.
4468 Argument *ReduceListArg = GtLCFunc->getArg(2);
4469
4470 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4471 BufferArg->getName() + ".addr");
4472 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4473 IdxArg->getName() + ".addr");
4474 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4475 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4476 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4477 BufferArgAlloca, Builder.getPtrTy(),
4478 BufferArgAlloca->getName() + ".ascast");
4479 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4480 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4481 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4482 ReduceListArgAlloca, Builder.getPtrTy(),
4483 ReduceListArgAlloca->getName() + ".ascast");
4484 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4485 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4486 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4487
4488 Value *LocalReduceList =
4489 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4490 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4491 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4492 Type *IndexTy = Builder.getIndexTy(
4493 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4494 for (auto En : enumerate(ReductionInfos)) {
4495 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4496 auto *RedListArrayTy =
4497 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4498 // Reduce element = LocalReduceList[i]
4499 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4500 RedListArrayTy, LocalReduceList,
4501 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4502 // elemptr = ((CopyType*)(elemptrptr)) + I
4503 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4504 // Global = Buffer.VD[Idx];
4505 Value *BufferVD =
4506 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4507 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4508 ReductionsBufferTy, BufferVD, 0, En.index());
4509
4510 switch (RI.EvaluationKind) {
4511 case EvalKind::Scalar: {
4512 Type *ElemType = RI.ElementType;
4513
4514 if (!IsByRef.empty() && IsByRef[En.index()]) {
4515 ElemType = RI.ByRefElementType;
4516 if (RI.DataPtrPtrGen) {
4517 InsertPointOrErrorTy GenResult =
4518 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4519
4520 if (!GenResult)
4521 return GenResult.takeError();
4522
4523 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4524 }
4525 }
4526
4527 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4528 Builder.CreateStore(TargetElement, ElemPtr);
4529 break;
4530 }
4531 case EvalKind::Complex: {
4532 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4533 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4534 Value *SrcReal = Builder.CreateLoad(
4535 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4536 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4537 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4538 Value *SrcImg = Builder.CreateLoad(
4539 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4540
4541 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4542 RI.ElementType, ElemPtr, 0, 0, ".realp");
4543 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4544 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4545 Builder.CreateStore(SrcReal, DestRealPtr);
4546 Builder.CreateStore(SrcImg, DestImgPtr);
4547 break;
4548 }
4549 case EvalKind::Aggregate: {
4550 Value *SizeVal =
4551 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4552 Builder.CreateMemCpy(
4553 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4554 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4555 SizeVal, false);
4556 break;
4557 }
4558 }
4559 }
4560
4561 Builder.CreateRetVoid();
4562 return GtLCFunc;
4563}
4564
4565Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4566 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4567 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4568 IRBuilder<>::InsertPointGuard IPG(Builder);
4569 LLVMContext &Ctx = M.getContext();
4570 auto *FuncTy = FunctionType::get(
4571 Builder.getVoidTy(),
4572 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4573 /* IsVarArg */ false);
4574 Function *GtLRFunc =
4576 "_omp_reduction_global_to_list_reduce_func", &M);
4577 GtLRFunc->setAttributes(FuncAttrs);
4578 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4579 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4580 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4581
4582 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4583 Builder.SetInsertPoint(EntryBlock);
4584 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4585
4586 // Buffer: global reduction buffer.
4587 Argument *BufferArg = GtLRFunc->getArg(0);
4588 // Idx: index of the buffer.
4589 Argument *IdxArg = GtLRFunc->getArg(1);
4590 // ReduceList: thread local Reduce list.
4591 Argument *ReduceListArg = GtLRFunc->getArg(2);
4592
4593 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4594 BufferArg->getName() + ".addr");
4595 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4596 IdxArg->getName() + ".addr");
4597 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4598 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4599 ArrayType *RedListArrayTy =
4600 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4601
4602 // 1. Build a list of reduction variables.
4603 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4604 Value *LocalReduceList =
4605 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4606
4607 InsertPointTy AllocaIP(EntryBlock->begin());
4608
4609 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4610 BufferArgAlloca, Builder.getPtrTy(),
4611 BufferArgAlloca->getName() + ".ascast");
4612 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4613 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4614 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4615 ReduceListArgAlloca, Builder.getPtrTy(),
4616 ReduceListArgAlloca->getName() + ".ascast");
4617 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4618 LocalReduceList, Builder.getPtrTy(),
4619 LocalReduceList->getName() + ".ascast");
4620
4621 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4622 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4623 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4624
4625 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4626 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4627 Type *IndexTy = Builder.getIndexTy(
4628 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4629 for (auto En : enumerate(ReductionInfos)) {
4630 const ReductionInfo &RI = En.value();
4631
4632 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4633 RedListArrayTy, ReductionList,
4634 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4635 // Global = Buffer.VD[Idx];
4636 Value *BufferVD =
4637 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4638 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4639 ReductionsBufferTy, BufferVD, 0, En.index());
4640
4641 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4642 // Get source descriptor from the reduce list
4643 Value *ReduceListVal =
4644 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4645 Value *SrcElementPtrPtr =
4646 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4647 {ConstantInt::get(IndexTy, 0),
4648 ConstantInt::get(IndexTy, En.index())});
4649 Value *SrcDescriptorAddr =
4650 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4651
4652 // Copy descriptor from source and update base_ptr to global buffer data
4653 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4654 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4655 if (!ByRefAlloc)
4656 return ByRefAlloc.takeError();
4657
4658 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4659 } else {
4660 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4661 }
4662 }
4663
4664 // Call reduce_function(ReduceList, GlobalReduceList)
4665 Value *ReduceList =
4666 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4667 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4668 ->addFnAttr(Attribute::NoUnwind);
4669 Builder.CreateRetVoid();
4670 return GtLRFunc;
4671}
4672
4673std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4674 std::string Suffix =
4675 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4676 return (Name + Suffix).str();
4677}
4678
4679Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4680 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4682 AttributeList FuncAttrs) {
4683 IRBuilder<>::InsertPointGuard IPG(Builder);
4684 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4685 {Builder.getPtrTy(), Builder.getPtrTy()},
4686 /* IsVarArg */ false);
4687 std::string Name = getReductionFuncName(ReducerName);
4688 Function *ReductionFunc =
4690 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4691 ReductionFunc->setAttributes(FuncAttrs);
4692 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4693 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4694 BasicBlock *EntryBB =
4695 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4696 Builder.SetInsertPoint(EntryBB);
4697 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4698
4699 // Need to alloca memory here and deal with the pointers before getting
4700 // LHS/RHS pointers out
4701 Value *LHSArrayPtr = nullptr;
4702 Value *RHSArrayPtr = nullptr;
4703 Argument *Arg0 = ReductionFunc->getArg(0);
4704 Argument *Arg1 = ReductionFunc->getArg(1);
4705 Type *Arg0Type = Arg0->getType();
4706 Type *Arg1Type = Arg1->getType();
4707
4708 Value *LHSAlloca =
4709 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4710 Value *RHSAlloca =
4711 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4712 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4713 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4714 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4715 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4716 Builder.CreateStore(Arg0, LHSAddrCast);
4717 Builder.CreateStore(Arg1, RHSAddrCast);
4718 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4719 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4720
4721 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4722 Type *IndexTy = Builder.getIndexTy(
4723 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4724 SmallVector<Value *> LHSPtrs, RHSPtrs;
4725 for (auto En : enumerate(ReductionInfos)) {
4726 const ReductionInfo &RI = En.value();
4727 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4728 RedArrayTy, RHSArrayPtr,
4729 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4730 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4731 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4732 RHSI8Ptr, RI.PrivateVariable->getType(),
4733 RHSI8Ptr->getName() + ".ascast");
4734
4735 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4736 RedArrayTy, LHSArrayPtr,
4737 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4738 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4739 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4740 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4741
4743 LHSPtrs.emplace_back(LHSPtr);
4744 RHSPtrs.emplace_back(RHSPtr);
4745 } else {
4746 Value *LHS = LHSPtr;
4747 Value *RHS = RHSPtr;
4748
4749 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4750 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4751 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4752 }
4753
4754 Value *Reduced;
4755 InsertPointOrErrorTy AfterIP =
4756 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4757 if (!AfterIP)
4758 return AfterIP.takeError();
4759 if (!Builder.GetInsertBlock())
4760 return ReductionFunc;
4761
4762 Builder.restoreIP(*AfterIP);
4763
4764 if (!IsByRef.empty() && !IsByRef[En.index()])
4765 Builder.CreateStore(Reduced, LHSPtr);
4766 }
4767 }
4768
4770 for (auto En : enumerate(ReductionInfos)) {
4771 unsigned Index = En.index();
4772 const ReductionInfo &RI = En.value();
4773 Value *LHSFixupPtr, *RHSFixupPtr;
4774 Builder.restoreIP(RI.ReductionGenClang(
4775 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4776
4777 // Fix the CallBack code genereated to use the correct Values for the LHS
4778 // and RHS
4779 LHSFixupPtr->replaceUsesWithIf(
4780 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4781 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4782 ReductionFunc;
4783 });
4784 RHSFixupPtr->replaceUsesWithIf(
4785 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4786 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4787 ReductionFunc;
4788 });
4789 }
4790
4791 Builder.CreateRetVoid();
4792 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4793 // to the entry block (this is dones for higher opt levels by later passes in
4794 // the pipeline). This has caused issues because non-entry `alloca`s force the
4795 // function to use dynamic stack allocations and we might run out of scratch
4796 // memory.
4797 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4798
4799 return ReductionFunc;
4800}
4801
4802static void
4804 bool IsGPU) {
4805 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4806 (void)RI;
4807 assert(RI.Variable && "expected non-null variable");
4808 assert(RI.PrivateVariable && "expected non-null private variable");
4809 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4810 "expected non-null reduction generator callback");
4811 if (!IsGPU) {
4812 assert(
4813 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4814 "expected variables and their private equivalents to have the same "
4815 "type");
4816 }
4817 assert(RI.Variable->getType()->isPointerTy() &&
4818 "expected variables to be pointers");
4819 }
4820}
4821
4822// The atomic cross-team reduction fast path applies when every reduction in the
4823// set can be represented by an atomicrmw. Clang only populates it for scalar
4824// reductions with a supported atomic operator.
4827 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4828 return static_cast<bool>(RI.AtomicReductionGen);
4829 });
4830}
4831
4833 const LocationDescription &Loc, InsertPointTy AllocaIP,
4834 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4835 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4836 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4837 Value *SrcLocInfo) {
4838 if (!updateToLocation(Loc))
4839 return InsertPointTy();
4840 Builder.restoreIP(CodeGenIP);
4841 Builder.SetCurrentDebugLocation(Loc.DL);
4842 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4843 LLVMContext &Ctx = M.getContext();
4844
4845 // Source location for the ident struct
4846 if (!SrcLocInfo) {
4847 uint32_t SrcLocStrSize;
4848 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4849 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4850 }
4851
4852 if (ReductionInfos.size() == 0)
4853 return Builder.saveIP();
4854
4855 BasicBlock *ContinuationBlock = nullptr;
4857 // Copied code from createReductions
4858 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
4859 ContinuationBlock = InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
4860 InsertBlock->getTerminator()->eraseFromParent();
4861 Builder.SetInsertPoint(InsertBlock->end());
4862 }
4863
4864 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4865 AttributeList FuncAttrs;
4866 AttrBuilder AttrBldr(Ctx);
4867 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4868 AttrBldr.addAttribute(Attr);
4869 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4870 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4871
4872 Expected<Function *> ReductionResult = createReductionFunction(
4873 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4874 ReductionGenCBKind, FuncAttrs);
4875 if (!ReductionResult)
4876 return ReductionResult.takeError();
4877 Function *ReductionFunc = *ReductionResult;
4878
4879 // Set the grid value in the config needed for lowering later on
4880 if (GridValue.has_value())
4881 Config.setGridValue(GridValue.value());
4882 else
4883 Config.setGridValue(getGridValue(T, ReductionFunc));
4884
4885 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4886 // RedList, shuffle_reduce_func, interwarp_copy_func);
4887 // or
4888 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4889 Value *Res;
4890
4891 // 1. Build a list of reduction variables.
4892 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4893 auto Size = ReductionInfos.size();
4894 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4895 Type *FuncPtrTy =
4896 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4897 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4898 Value *ReductionList;
4899 {
4901 Builder.restoreIP(AllocaIP);
4902 Value *ReductionListAlloca =
4903 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4904 ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4905 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4906 }
4907 Type *IndexTy = Builder.getIndexTy(
4908 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4909 for (auto En : enumerate(ReductionInfos)) {
4910 const ReductionInfo &RI = En.value();
4911 Value *ElemPtr = Builder.CreateInBoundsGEP(
4912 RedArrayTy, ReductionList,
4913 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4914
4915 Value *PrivateVar = RI.PrivateVariable;
4916 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4917 if (IsByRefElem)
4918 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4919
4920 Value *CastElem =
4921 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4922 Builder.CreateStore(CastElem, ElemPtr);
4923 }
4924 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4925 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4926
4927 if (!SarFunc)
4928 return SarFunc.takeError();
4929
4930 Expected<Function *> CopyResult =
4931 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4932 if (!CopyResult)
4933 return CopyResult.takeError();
4934 Function *WcFunc = *CopyResult;
4935
4936 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4937
4938 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4939 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4940 // not currently use it. It is computed here conservatively as max(element
4941 // sizes) * N rather than the exact sum, which over-calculates the size for
4942 // mixed reduction types but is harmless given the argument is unused.
4943 // TODO: Consider dropping this computation if the runtime API is ever revised
4944 // to remove the unused parameter.
4945 unsigned MaxDataSize = 0;
4946 SmallVector<Type *> ReductionTypeArgs;
4947 for (auto En : enumerate(ReductionInfos)) {
4948 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4949 // the actual data size stored in the global reduction buffer, consistent
4950 // with the ReductionsBufferTy struct used for GEP offsets below.
4951 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4952 ? En.value().ByRefElementType
4953 : En.value().ElementType;
4954 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4955 if (Size > MaxDataSize)
4956 MaxDataSize = Size;
4957 ReductionTypeArgs.emplace_back(RedTypeArg);
4958 }
4959 Value *ReductionDataSize =
4960 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4961
4962 // Helper function to copy thread-local data back to the original reduction
4963 // list.
4964 Function *CopyScratchToListFunc = nullptr;
4965 // Thread-local storage for the reduction variables.
4966 Value *ScratchForCopyBack = nullptr;
4967 // RL pointer to which the final value from the per-thread scratch should be
4968 // copied back. (Basically RL, appropriately casted if necessary.)
4969 Value *RLForCopyBack = RL;
4970
4971 bool IsAtomicReduction =
4972 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4973
4974 if (!IsTeamsReduction) {
4975 Value *SarFuncCast =
4976 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4977 Value *WcFuncCast =
4978 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4979 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4980 WcFuncCast};
4982 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4983 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4984 } else if (IsAtomicReduction) {
4985 // Atomic cross-team reduction fast path: determine the team's main thread
4986 // that is later to fold its value atomically into the mapped variable.
4987 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4988 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4989 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4990 } else {
4991 StructType *ReductionsBufferTy = StructType::create(
4992 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4993
4994 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4995 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4996 if (!LtGCFunc)
4997 return LtGCFunc.takeError();
4998
4999 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
5000 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
5001 if (!GtLCFunc)
5002 return GtLCFunc.takeError();
5003
5004 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
5005 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
5006 if (!GtLRFunc)
5007 return GtLRFunc.takeError();
5008
5009 // The runtime's cross-team final aggregate uses the storage pointed at by
5010 // its reduce-list argument as per-thread scratch. When the surrounding
5011 // kernel is already in SPMD execution mode, clang emitted each reduction
5012 // private as a per-thread `alloca addrspace(5)`, so the original red_list
5013 // (RL) is already per-thread and nothing else is needed.
5014 //
5015 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
5016 // Generic-mode globalization put the reduction private into team-shared
5017 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
5018 // point all threads of the last team would race on the shared LDS slot.
5019 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
5020 // value in, and hand the per-thread RL to the runtime instead. The writer
5021 // thread copies the final value from that per-thread scratch back to RL
5022 // before running the existing combine path below.
5023
5024 // Thread-local RL (might need localization below before being passed to the
5025 // runtime).
5026 Value *RuntimeRL = RL;
5027
5028 if (!IsSPMD) {
5029 Value *PerThreadScratch;
5030
5031 {
5033 Builder.restoreIP(AllocaIP);
5034 // Allocate thread-local buffer for the reduction variables.
5035 Value *PerThreadScratchAlloca =
5036 Builder.CreateAlloca(ReductionsBufferTy, /*ArraySize=*/nullptr,
5037 ".omp.reduction.scratch");
5038 PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5039 PerThreadScratchAlloca, PtrTy,
5040 PerThreadScratchAlloca->getName() + ".ascast");
5041 // Allocate thread-local buffer for the pointers to the reduction
5042 // variables.
5043 Value *PerThreadRedListAlloca =
5044 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5045 ".omp.reduction.per_thread_red_list");
5046 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5047 PerThreadRedListAlloca, PtrTy,
5048 PerThreadRedListAlloca->getName() + ".ascast");
5049 }
5050
5051 // Iterate over the reduction variables and copy the team-local value to
5052 // the thread-local buffer.
5053 for (auto En : enumerate(ReductionInfos)) {
5054 const ReductionInfo &RI = En.value();
5055 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5056
5057 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5058 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5059 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5060 0, En.index());
5061
5062 Value *RuntimeListEntry = FieldPtr;
5063 if (IsByRefElem && RI.DataPtrPtrGen) {
5064 Value *SrcDescriptor =
5065 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5066 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5067 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5068 if (!Descriptor)
5069 return Descriptor.takeError();
5070 RuntimeListEntry = *Descriptor;
5071 }
5072 Builder.CreateStore(RuntimeListEntry, Slot);
5073 }
5074 // The copy helpers were emitted with default-AS (AS 0) pointer params
5075 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5076 // but PerThreadScratch and RL live in the target's default AS, which
5077 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5078 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5079 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5080 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5081 PerThreadScratch, CopyArg0Ty);
5082 RLForCopyBack =
5083 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5084 // Use index 0 because there is no array of target values to index into,
5085 // there is only one thread-local memory slot.
5086 // restoreIP above left a stale/empty debug location; this inlinable call
5087 // to a debug-info-bearing helper needs one or the verifier rejects the
5088 // module ("!dbg attachment points at wrong subprogram") after inlining.
5089 Builder.SetCurrentDebugLocation(Loc.DL);
5090 Builder.CreateCall(
5091 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5092 CopyScratchToListFunc = *GtLCFunc;
5093 }
5094
5095 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5096 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5097
5098 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5099 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5100 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5101 }
5102
5103 // 5. Build if (res == 1)
5104 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5105 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5106 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5107 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5108
5109 // 6. Build then branch: where we have reduced values in the master
5110 // thread in each team.
5111 // __kmpc_end_reduce{_nowait}(<gtid>);
5112 // break;
5113 emitBlock(ThenBB, CurFunc);
5114
5115 // Copy the writer thread's per-thread scratch result back into the original
5116 // red-list storage before the existing combine path reads RI.PrivateVariable.
5117 // Set a debug location: this inlinable call to a debug-info-bearing helper
5118 // needs one or the verifier rejects the module after inlining.
5119 if (ScratchForCopyBack) {
5120 Builder.SetCurrentDebugLocation(Loc.DL);
5121 Builder.CreateCall(
5122 CopyScratchToListFunc,
5123 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5124 }
5125
5126 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5127 for (auto En : enumerate(ReductionInfos)) {
5128 const ReductionInfo &RI = En.value();
5129
5130 // Atomic cross-team fast path: each team's main thread folds its
5131 // team-reduced value directly into the mapped reduction variable with a
5132 // single atomicrmw.
5133 if (IsAtomicReduction) {
5135 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5136 if (!AfterIP)
5137 return AfterIP.takeError();
5138 Builder.restoreIP(*AfterIP);
5139 continue;
5140 }
5141
5143 Value *RedValue = RI.Variable;
5144
5145 Value *RHS =
5146 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5147
5149 Value *LHSPtr, *RHSPtr;
5150 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5151 &LHSPtr, &RHSPtr, CurFunc));
5152
5153 // Fix the CallBack code genereated to use the correct Values for the LHS
5154 // and RHS. Cast to match types before replacing (necessary to handle
5155 // different address spaces).
5156 if (LHSPtr->getType() != RedValue->getType())
5157 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5158 RedValue, LHSPtr->getType());
5159 if (RHSPtr->getType() != RHS->getType())
5160 RHS =
5161 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5162
5163 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5164 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5165 ReductionFunc;
5166 });
5167 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5168 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5169 ReductionFunc;
5170 });
5171 } else {
5172 if (IsByRef.empty() || !IsByRef[En.index()]) {
5173 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5174 "red.value." + Twine(En.index()));
5175 }
5176 Value *PrivateRedValue = Builder.CreateLoad(
5177 ValueType, RHS, "red.private.value" + Twine(En.index()));
5178 Value *Reduced;
5179 InsertPointOrErrorTy AfterIP =
5180 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5181 if (!AfterIP)
5182 return AfterIP.takeError();
5183 Builder.restoreIP(*AfterIP);
5184
5185 if (!IsByRef.empty() && !IsByRef[En.index()])
5186 Builder.CreateStore(Reduced, RI.Variable);
5187 }
5188 }
5189 emitBlock(ExitBB, CurFunc);
5190 if (ContinuationBlock) {
5191 Builder.CreateBr(ContinuationBlock);
5192 Builder.SetInsertPoint(ContinuationBlock);
5193 }
5194 Config.setEmitLLVMUsed();
5195
5196 return Builder.saveIP();
5197}
5198
5200 Type *VoidTy = Type::getVoidTy(M.getContext());
5201 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5202 auto *FuncTy =
5203 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5205 ".omp.reduction.func", &M);
5206}
5207
5209 Function *ReductionFunc,
5211 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5212 IRBuilder<>::InsertPointGuard IPG(Builder);
5213 Module *Module = ReductionFunc->getParent();
5214 BasicBlock *ReductionFuncBlock =
5215 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5216 Builder.SetInsertPoint(ReductionFuncBlock);
5217 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5218 Value *LHSArrayPtr = nullptr;
5219 Value *RHSArrayPtr = nullptr;
5220 if (IsGPU) {
5221 // Need to alloca memory here and deal with the pointers before getting
5222 // LHS/RHS pointers out
5223 //
5224 Argument *Arg0 = ReductionFunc->getArg(0);
5225 Argument *Arg1 = ReductionFunc->getArg(1);
5226 Type *Arg0Type = Arg0->getType();
5227 Type *Arg1Type = Arg1->getType();
5228
5229 Value *LHSAlloca =
5230 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5231 Value *RHSAlloca =
5232 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5233 Value *LHSAddrCast =
5234 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5235 Value *RHSAddrCast =
5236 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5237 Builder.CreateStore(Arg0, LHSAddrCast);
5238 Builder.CreateStore(Arg1, RHSAddrCast);
5239 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5240 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5241 } else {
5242 LHSArrayPtr = ReductionFunc->getArg(0);
5243 RHSArrayPtr = ReductionFunc->getArg(1);
5244 }
5245
5246 unsigned NumReductions = ReductionInfos.size();
5247 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5248
5249 for (auto En : enumerate(ReductionInfos)) {
5250 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5251 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5252 RedArrayTy, LHSArrayPtr, 0, En.index());
5253 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5254 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5255 LHSI8Ptr, RI.Variable->getType());
5256 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5257 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5258 RedArrayTy, RHSArrayPtr, 0, En.index());
5259 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5260 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5261 RHSI8Ptr, RI.PrivateVariable->getType());
5262 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5263 Value *Reduced;
5265 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5266 if (!AfterIP)
5267 return AfterIP.takeError();
5268
5269 Builder.restoreIP(*AfterIP);
5270 // TODO: Consider flagging an error.
5271 if (!Builder.GetInsertBlock())
5272 return Error::success();
5273
5274 // store is inside of the reduction region when using by-ref
5275 if (!IsByRef[En.index()])
5276 Builder.CreateStore(Reduced, LHSPtr);
5277 }
5278 Builder.CreateRetVoid();
5279 return Error::success();
5280}
5281
5283 const LocationDescription &Loc, InsertPointTy AllocaIP,
5284 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5285 bool IsNoWait, bool IsTeamsReduction) {
5286 assert(ReductionInfos.size() == IsByRef.size());
5287 if (Config.isGPU())
5288 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5289 IsByRef, IsNoWait, IsTeamsReduction);
5290
5291 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5292
5293 if (!updateToLocation(Loc))
5294 return InsertPointTy();
5295
5296 if (ReductionInfos.size() == 0)
5297 return Builder.saveIP();
5298
5299 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
5300 BasicBlock *ContinuationBlock =
5301 InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
5302 InsertBlock->getTerminator()->eraseFromParent();
5303
5304 // Create and populate array of type-erased pointers to private reduction
5305 // values.
5306 unsigned NumReductions = ReductionInfos.size();
5307 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5308 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getTerminator());
5309 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5310
5311 Builder.SetInsertPoint(InsertBlock->end());
5312 // Emitting the alloca moved the insertion point into the alloca block and
5313 // can clear the debug loc. Restore back to Loc.DL.
5314 Builder.SetCurrentDebugLocation(Loc.DL);
5315
5316 for (auto En : enumerate(ReductionInfos)) {
5317 unsigned Index = En.index();
5318 const ReductionInfo &RI = En.value();
5319 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5320 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5321 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5322 }
5323
5324 // Emit a call to the runtime function that orchestrates the reduction.
5325 // Declare the reduction function in the process.
5326 Type *IndexTy = Builder.getIndexTy(
5327 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5328 Function *Func = Builder.GetInsertBlock()->getParent();
5329 Module *Module = Func->getParent();
5330 uint32_t SrcLocStrSize;
5331 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5332 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5333 return RI.AtomicReductionGen;
5334 });
5335 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5336 CanGenerateAtomic
5337 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5338 : IdentFlag(0));
5339 Value *ThreadId = getOrCreateThreadID(Ident);
5340 Constant *NumVariables = Builder.getInt32(NumReductions);
5341 const DataLayout &DL = Module->getDataLayout();
5342 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5343 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5344 Function *ReductionFunc = getFreshReductionFunc(*Module);
5345 Value *Lock = getOMPCriticalRegionLock(".reduction");
5347 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5348 : RuntimeFunction::OMPRTL___kmpc_reduce);
5349 CallInst *ReduceCall =
5350 createRuntimeFunctionCall(ReduceFunc,
5351 {Ident, ThreadId, NumVariables, RedArraySize,
5352 RedArray, ReductionFunc, Lock},
5353 "reduce");
5354
5355 // Create final reduction entry blocks for the atomic and non-atomic case.
5356 // Emit IR that dispatches control flow to one of the blocks based on the
5357 // reduction supporting the atomic mode.
5358 BasicBlock *NonAtomicRedBlock =
5359 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5360 BasicBlock *AtomicRedBlock =
5361 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5362 SwitchInst *Switch =
5363 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5364 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5365 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5366
5367 // Populate the non-atomic reduction using the elementwise reduction function.
5368 // This loads the elements from the global and private variables and reduces
5369 // them before storing back the result to the global variable.
5370 Builder.SetInsertPoint(NonAtomicRedBlock);
5371 for (auto En : enumerate(ReductionInfos)) {
5372 const ReductionInfo &RI = En.value();
5374 // We have one less load for by-ref case because that load is now inside of
5375 // the reduction region
5376 Value *RedValue = RI.Variable;
5377 if (!IsByRef[En.index()]) {
5378 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5379 "red.value." + Twine(En.index()));
5380 }
5381 Value *PrivateRedValue =
5382 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5383 "red.private.value." + Twine(En.index()));
5384 Value *Reduced;
5385 InsertPointOrErrorTy AfterIP =
5386 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5387 if (!AfterIP)
5388 return AfterIP.takeError();
5389 Builder.restoreIP(*AfterIP);
5390
5391 if (!Builder.GetInsertBlock())
5392 return InsertPointTy();
5393 // for by-ref case, the load is inside of the reduction region
5394 if (!IsByRef[En.index()])
5395 Builder.CreateStore(Reduced, RI.Variable);
5396 }
5397 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5398 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5399 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5400 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5401 Builder.CreateBr(ContinuationBlock);
5402
5403 // Populate the atomic reduction using the atomic elementwise reduction
5404 // function. There are no loads/stores here because they will be happening
5405 // inside the atomic elementwise reduction.
5406 Builder.SetInsertPoint(AtomicRedBlock);
5407 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5408 for (const ReductionInfo &RI : ReductionInfos) {
5410 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5411 if (!AfterIP)
5412 return AfterIP.takeError();
5413 Builder.restoreIP(*AfterIP);
5414 if (!Builder.GetInsertBlock())
5415 return InsertPointTy();
5416 }
5417 Builder.CreateBr(ContinuationBlock);
5418 } else {
5419 Builder.CreateUnreachable();
5420 }
5421
5422 // Populate the outlined reduction function using the elementwise reduction
5423 // function. Partial values are extracted from the type-erased array of
5424 // pointers to private variables.
5425 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5426 IsByRef, /*isGPU=*/false);
5427 if (Err)
5428 return Err;
5429
5430 if (!Builder.GetInsertBlock())
5431 return InsertPointTy();
5432
5433 Builder.SetInsertPoint(ContinuationBlock);
5434 return Builder.saveIP();
5435}
5436
5439 BodyGenCallbackTy BodyGenCB,
5440 FinalizeCallbackTy FiniCB) {
5441 if (!updateToLocation(Loc))
5442 return Loc.IP;
5443
5444 Directive OMPD = Directive::OMPD_master;
5445 uint32_t SrcLocStrSize;
5446 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5447 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5448 Value *ThreadId = getOrCreateThreadID(Ident);
5449 Value *Args[] = {Ident, ThreadId};
5450
5451 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5452 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5453
5454 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5455 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5456
5457 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5458 /*Conditional*/ true, /*hasFinalize*/ true);
5459}
5460
5463 BodyGenCallbackTy BodyGenCB,
5464 FinalizeCallbackTy FiniCB, Value *Filter) {
5466 if (!updateToLocation(Loc))
5467 return Loc.IP;
5468
5469 Directive OMPD = Directive::OMPD_masked;
5470 uint32_t SrcLocStrSize;
5471 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5472 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5473 Value *ThreadId = getOrCreateThreadID(Ident);
5474 Value *Args[] = {Ident, ThreadId, Filter};
5475 Value *ArgsEnd[] = {Ident, ThreadId};
5476
5477 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5478 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5479
5480 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5481 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5482
5483 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5484 /*Conditional*/ true, /*hasFinalize*/ true);
5485}
5486
5488 llvm::FunctionCallee Callee,
5490 const llvm::Twine &Name) {
5491 llvm::CallInst *Call = Builder.CreateCall(
5492 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5493 Call->setDoesNotThrow();
5494 return Call;
5495}
5496
5497// Expects input basic block is dominated by BeforeScanBB.
5498// Once Scan directive is encountered, the code after scan directive should be
5499// dominated by AfterScanBB. Scan directive splits the code sequence to
5500// scan and input phase. Based on whether inclusive or exclusive
5501// clause is used in the scan directive and whether input loop or scan loop
5502// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5503// input loop and second is the scan loop. The code generated handles only
5504// inclusive scans now.
5506 const LocationDescription &Loc, InsertPointTy AllocaIP,
5507 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5508 bool IsInclusive, ScanInfo *ScanRedInfo) {
5509 if (ScanRedInfo->OMPFirstScanLoop) {
5510 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5511 ScanVarsType, ScanRedInfo);
5512 if (Err)
5513 return Err;
5514 }
5515 if (!updateToLocation(Loc))
5516 return Loc.IP;
5517
5518 llvm::Value *IV = ScanRedInfo->IV;
5519
5520 if (ScanRedInfo->OMPFirstScanLoop) {
5521 // Emit buffer[i] = red; at the end of the input phase.
5522 for (size_t i = 0; i < ScanVars.size(); i++) {
5523 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5524 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5525 Type *DestTy = ScanVarsType[i];
5526 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5527 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5528
5529 Builder.CreateStore(Src, Val);
5530 }
5531 }
5532 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5533 emitBlock(ScanRedInfo->OMPScanDispatch,
5534 Builder.GetInsertBlock()->getParent());
5535
5536 if (!ScanRedInfo->OMPFirstScanLoop) {
5537 IV = ScanRedInfo->IV;
5538 // Emit red = buffer[i]; at the entrance to the scan phase.
5539 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5540 for (size_t i = 0; i < ScanVars.size(); i++) {
5541 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5542 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5543 Type *DestTy = ScanVarsType[i];
5544 Value *SrcPtr =
5545 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5546 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5547 Builder.CreateStore(Src, ScanVars[i]);
5548 }
5549 }
5550
5551 // TODO: Update it to CreateBr and remove dead blocks
5552 llvm::Value *CmpI = Builder.getInt1(true);
5553 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5554 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5555 ScanRedInfo->OMPAfterScanBlock);
5556 } else {
5557 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5558 ScanRedInfo->OMPBeforeScanBlock);
5559 }
5560 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5561 Builder.GetInsertBlock()->getParent());
5562 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5563 return Builder.saveIP();
5564}
5565
5566Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5567 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5568 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5569
5570 Builder.restoreIP(AllocaIP);
5571 // Create the shared pointer at alloca IP.
5572 for (size_t i = 0; i < ScanVars.size(); i++) {
5573 llvm::Value *BuffPtr =
5574 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5575 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5576 }
5577
5578 // Allocate temporary buffer by master thread
5579 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5580 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5581 Builder.restoreIP(CodeGenIP);
5582 Value *AllocSpan =
5583 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5584 for (size_t i = 0; i < ScanVars.size(); i++) {
5585 Type *IntPtrTy = Builder.getInt32Ty();
5586 Value *Allocsize = Builder.CreateTypeSize(
5587 IntPtrTy, M.getDataLayout().getTypeAllocSize(ScanVarsType[i]));
5588 Value *Buff =
5589 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5590 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5591 }
5592 return Error::success();
5593 };
5594 // TODO: Perform finalization actions for variables. This has to be
5595 // called for variables which have destructors/finalizers.
5596 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5597
5598 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5599 llvm::Value *FilterVal = Builder.getInt32(0);
5601 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5602
5603 if (!AfterIP)
5604 return AfterIP.takeError();
5605 Builder.restoreIP(*AfterIP);
5606 BasicBlock *InputBB = Builder.GetInsertBlock();
5607 if (InputBB->hasTerminator())
5608 Builder.SetInsertPoint(InputBB->getTerminator());
5609 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5610 if (!AfterIP)
5611 return AfterIP.takeError();
5612 Builder.restoreIP(*AfterIP);
5613
5614 return Error::success();
5615}
5616
5617Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5618 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5619 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5620 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5621 Builder.restoreIP(CodeGenIP);
5622 for (ReductionInfo RedInfo : ReductionInfos) {
5623 Value *PrivateVar = RedInfo.PrivateVariable;
5624 Value *OrigVar = RedInfo.Variable;
5625 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5626 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5627
5628 Type *SrcTy = RedInfo.ElementType;
5629 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5630 "arrayOffset");
5631 Value *Src = Builder.CreateLoad(SrcTy, Val);
5632
5633 Builder.CreateStore(Src, OrigVar);
5634 Builder.CreateFree(Buff);
5635 }
5636 return Error::success();
5637 };
5638 // TODO: Perform finalization actions for variables. This has to be
5639 // called for variables which have destructors/finalizers.
5640 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5641
5642 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5643 Builder.SetInsertPoint(TI);
5644 else
5645 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5646
5647 llvm::Value *FilterVal = Builder.getInt32(0);
5649 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5650
5651 if (!AfterIP)
5652 return AfterIP.takeError();
5653 Builder.restoreIP(*AfterIP);
5654 BasicBlock *InputBB = Builder.GetInsertBlock();
5655 if (InputBB->hasTerminator())
5656 Builder.SetInsertPoint(InputBB->getTerminator());
5657 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5658 if (!AfterIP)
5659 return AfterIP.takeError();
5660 Builder.restoreIP(*AfterIP);
5661 return Error::success();
5662}
5663
5665 const LocationDescription &Loc,
5667 ScanInfo *ScanRedInfo) {
5668
5669 if (!updateToLocation(Loc))
5670 return Loc.IP;
5671 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5672 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5673 Builder.restoreIP(CodeGenIP);
5674 Function *CurFn = Builder.GetInsertBlock()->getParent();
5675 // for (int k = 0; k <= ceil(log2(n)); ++k)
5676 llvm::BasicBlock *LoopBB =
5677 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5678 llvm::BasicBlock *ExitBB =
5679 splitBB(Builder, false, "omp.outer.log.scan.exit");
5681 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::log2,
5682 Builder.getDoubleTy());
5683 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5684 llvm::Value *Arg =
5685 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5686 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5688 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::ceil,
5689 Builder.getDoubleTy());
5690 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5691 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5692 llvm::Value *NMin1 = Builder.CreateNUWSub(
5693 ScanRedInfo->Span,
5694 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5695 Builder.SetInsertPoint(InputBB);
5696 Builder.CreateBr(LoopBB);
5697 emitBlock(LoopBB, CurFn);
5698 Builder.SetInsertPoint(LoopBB);
5699
5700 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5701 // size pow2k = 1;
5702 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5703 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5704 InputBB);
5705 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5706 InputBB);
5707 // for (size i = n - 1; i >= 2 ^ k; --i)
5708 // tmp[i] op= tmp[i-pow2k];
5709 llvm::BasicBlock *InnerLoopBB =
5710 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5711 llvm::BasicBlock *InnerExitBB =
5712 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5713 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5714 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5715 emitBlock(InnerLoopBB, CurFn);
5716 Builder.SetInsertPoint(InnerLoopBB);
5717 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5718 IVal->addIncoming(NMin1, LoopBB);
5719 for (ReductionInfo RedInfo : ReductionInfos) {
5720 Value *ReductionVal = RedInfo.PrivateVariable;
5721 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5722 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5723 Type *DestTy = RedInfo.ElementType;
5724 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5725 Value *LHSPtr =
5726 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5727 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5728 Value *RHSPtr =
5729 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5730 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5731 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5732 llvm::Value *Result;
5733 InsertPointOrErrorTy AfterIP =
5734 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5735 if (!AfterIP)
5736 return AfterIP.takeError();
5737 Builder.CreateStore(Result, LHSPtr);
5738 }
5739 llvm::Value *NextIVal = Builder.CreateNUWSub(
5740 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5741 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5742 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5743 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5744 emitBlock(InnerExitBB, CurFn);
5745 llvm::Value *Next = Builder.CreateNUWAdd(
5746 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5747 Counter->addIncoming(Next, Builder.GetInsertBlock());
5748 // pow2k <<= 1;
5749 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5750 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5751 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5752 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5753 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5754 return Error::success();
5755 };
5756
5757 // TODO: Perform finalization actions for variables. This has to be
5758 // called for variables which have destructors/finalizers.
5759 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5760
5761 llvm::Value *FilterVal = Builder.getInt32(0);
5763 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5764
5765 if (!AfterIP)
5766 return AfterIP.takeError();
5767 Builder.restoreIP(*AfterIP);
5768 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5769
5770 if (!AfterIP)
5771 return AfterIP.takeError();
5772 Builder.restoreIP(*AfterIP);
5773 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5774 if (Err)
5775 return Err;
5776
5777 return AfterIP;
5778}
5779
5780Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5781 llvm::function_ref<Error()> InputLoopGen,
5782 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5783 ScanInfo *ScanRedInfo) {
5784
5785 {
5786 // Emit loop with input phase:
5787 // for (i: 0..<num_iters>) {
5788 // <input phase>;
5789 // buffer[i] = red;
5790 // }
5791 ScanRedInfo->OMPFirstScanLoop = true;
5792 Error Err = InputLoopGen();
5793 if (Err)
5794 return Err;
5795 }
5796 {
5797 // Emit loop with scan phase:
5798 // for (i: 0..<num_iters>) {
5799 // red = buffer[i];
5800 // <scan phase>;
5801 // }
5802 ScanRedInfo->OMPFirstScanLoop = false;
5803 Error Err = ScanLoopGen(Builder);
5804 if (Err)
5805 return Err;
5806 }
5807 return Error::success();
5808}
5809
5810void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5811 Function *Fun = Builder.GetInsertBlock()->getParent();
5812 ScanRedInfo->OMPScanDispatch =
5813 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5814 ScanRedInfo->OMPAfterScanBlock =
5815 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5816 ScanRedInfo->OMPBeforeScanBlock =
5817 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5818 ScanRedInfo->OMPScanLoopExit =
5819 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5820}
5822 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5823 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5824 Module *M = F->getParent();
5825 LLVMContext &Ctx = M->getContext();
5826 Type *IndVarTy = TripCount->getType();
5827
5828 // Create the basic block structure.
5829 BasicBlock *Preheader =
5830 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5831 BasicBlock *Header =
5832 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5833 BasicBlock *Cond =
5834 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5835 BasicBlock *Body =
5836 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5837 BasicBlock *Latch =
5838 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5839 BasicBlock *Exit =
5840 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5841 BasicBlock *After =
5842 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5843
5844 // Use specified DebugLoc for new instructions.
5845 Builder.SetCurrentDebugLocation(DL);
5846
5847 Builder.SetInsertPoint(Preheader);
5848 Builder.CreateBr(Header);
5849
5850 Builder.SetInsertPoint(Header);
5851 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5852 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5853 Builder.CreateBr(Cond);
5854
5855 Builder.SetInsertPoint(Cond);
5856 Value *Cmp =
5857 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5858 Builder.CreateCondBr(Cmp, Body, Exit);
5859
5860 Builder.SetInsertPoint(Body);
5861 Builder.CreateBr(Latch);
5862
5863 Builder.SetInsertPoint(Latch);
5864 // Decide whether the induction variable increment can carry nsw.
5865 //
5866 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5867 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5868 // for valid programs 0 <= count <= INT_MAX always holds.
5869 //
5870 // Collapsed loops: the trip count is a product that can overflow i32 even for
5871 // a conforming program, so nsw is kept only when the product is a constant
5872 // that provably fits, dropped otherwise.
5873 bool HasNSW = Config.hasNoSignedWrap();
5874 if (HasNSW) {
5875 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5876 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5878 if (CI->getValue().ugt(SignedMax))
5879 HasNSW = false;
5880 } else if (IsCollapsed) {
5881 HasNSW = false;
5882 }
5883 }
5884 Value *Next =
5885 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5886 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5887 Builder.CreateBr(Header);
5888 IndVarPHI->addIncoming(Next, Latch);
5889
5890 Builder.SetInsertPoint(Exit);
5891 Builder.CreateBr(After);
5892
5893 // Remember and return the canonical control flow.
5894 LoopInfos.emplace_front();
5895 CanonicalLoopInfo *CL = &LoopInfos.front();
5896
5897 CL->Header = Header;
5898 CL->Cond = Cond;
5899 CL->Latch = Latch;
5900 CL->Exit = Exit;
5901
5902#ifndef NDEBUG
5903 CL->assertOK();
5904#endif
5905 return CL;
5906}
5907
5910 LoopBodyGenCallbackTy BodyGenCB,
5911 Value *TripCount, const Twine &Name) {
5912 BasicBlock *BB = Loc.IP.getNodeParent();
5913 BasicBlock *NextBB = BB->getNextNode();
5914
5915 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5916 NextBB, NextBB, Name);
5917 BasicBlock *After = CL->getAfter();
5918
5919 // If location is not set, don't connect the loop.
5920 if (updateToLocation(Loc)) {
5921 // Split the loop at the insertion point: Branch to the preheader and move
5922 // every following instruction to after the loop (the After BB). Also, the
5923 // new successor is the loop's after block.
5924 spliceBB(Builder, After, /*CreateBranch=*/false);
5925 Builder.CreateBr(CL->getPreheader());
5926 }
5927
5928 // Emit the body content. We do it after connecting the loop to the CFG to
5929 // avoid that the callback encounters degenerate BBs.
5930 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5931 return Err;
5932
5933#ifndef NDEBUG
5934 CL->assertOK();
5935#endif
5936 return CL;
5937}
5938
5940 ScanInfos.emplace_front();
5941 ScanInfo *Result = &ScanInfos.front();
5942 return Result;
5943}
5944
5948 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5949 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5950 LocationDescription ComputeLoc =
5951 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5952 updateToLocation(ComputeLoc);
5953
5955
5957 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5958 ScanRedInfo->Span = TripCount;
5959 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5960 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5961
5962 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5963 Builder.restoreIP(CodeGenIP);
5964 ScanRedInfo->IV = IV;
5965 createScanBBs(ScanRedInfo);
5966 BasicBlock *InputBlock = Builder.GetInsertBlock();
5967 Instruction *Terminator = InputBlock->getTerminator();
5968 assert(Terminator->getNumSuccessors() == 1);
5969 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5970 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5971 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5972 Builder.GetInsertBlock()->getParent());
5973 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5974 emitBlock(ScanRedInfo->OMPScanLoopExit,
5975 Builder.GetInsertBlock()->getParent());
5976 Builder.CreateBr(ContinueBlock);
5977 Builder.SetInsertPoint(
5978 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5979 return BodyGenCB(Builder.saveIP(), IV);
5980 };
5981
5982 const auto &&InputLoopGen = [&]() -> Error {
5984 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5985 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5986 if (!LoopInfo)
5987 return LoopInfo.takeError();
5988 Result.push_back(*LoopInfo);
5989 Builder.restoreIP((*LoopInfo)->getAfterIP());
5990 return Error::success();
5991 };
5992 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5994 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5995 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5996 if (!LoopInfo)
5997 return LoopInfo.takeError();
5998 Result.push_back(*LoopInfo);
5999 Builder.restoreIP((*LoopInfo)->getAfterIP());
6000 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
6001 return Error::success();
6002 };
6003 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
6004 if (Err)
6005 return Err;
6006 return Result;
6007}
6008
6010 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
6011 bool IsSigned, bool InclusiveStop, const Twine &Name) {
6012
6013 // Consider the following difficulties (assuming 8-bit signed integers):
6014 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
6015 // DO I = 1, 100, 50
6016 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
6017 // DO I = 100, 0, -128
6018
6019 // Start, Stop and Step must be of the same integer type.
6020 auto *IndVarTy = cast<IntegerType>(Start->getType());
6021 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
6022 assert(IndVarTy == Step->getType() && "Step type mismatch");
6023
6025
6026 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
6027 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
6028
6029 // Like Step, but always positive.
6030 Value *Incr = Step;
6031
6032 // Distance between Start and Stop; always positive.
6033 Value *Span;
6034
6035 // Condition whether there are no iterations are executed at all, e.g. because
6036 // UB < LB.
6037 Value *ZeroCmp;
6038
6039 if (IsSigned) {
6040 // Ensure that increment is positive. If not, negate and invert LB and UB.
6041 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6042 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6043 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6044 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6045 Span = Builder.CreateSub(UB, LB, "", false, true);
6046 ZeroCmp = Builder.CreateICmp(
6047 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6048 } else {
6049 Span = Builder.CreateSub(Stop, Start, "", true);
6050 ZeroCmp = Builder.CreateICmp(
6051 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6052 }
6053
6054 Value *CountIfLooping;
6055 if (InclusiveStop) {
6056 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6057 } else {
6058 // Avoid incrementing past stop since it could overflow.
6059 Value *CountIfTwo = Builder.CreateAdd(
6060 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6061 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6062 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6063 }
6064
6065 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6066 "omp_" + Name + ".tripcount");
6067}
6068
6071 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6072 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6073 ScanInfo *ScanRedInfo) {
6074 LocationDescription ComputeLoc =
6075 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6076
6078 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6079
6080 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6081 Builder.restoreIP(CodeGenIP);
6082 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6083 /*HasNSW=*/Config.hasNoSignedWrap());
6084 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6085 /*HasNSW=*/Config.hasNoSignedWrap());
6086 if (InScan)
6087 ScanRedInfo->IV = IndVar;
6088 return BodyGenCB(Builder.saveIP(), IndVar);
6089 };
6090 LocationDescription LoopLoc =
6091 ComputeIP.isValid()
6092 ? Loc
6093 : LocationDescription(Builder.saveIP(),
6094 Builder.getCurrentDebugLocation());
6095 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6096}
6097
6098// Returns an LLVM function to call for initializing loop bounds using OpenMP
6099// static scheduling for composite `distribute parallel for` depending on
6100// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6101// integers as unsigned similarly to CanonicalLoopInfo.
6102static FunctionCallee
6104 OpenMPIRBuilder &OMPBuilder) {
6105 unsigned Bitwidth = Ty->getIntegerBitWidth();
6106 if (Bitwidth == 32)
6107 return OMPBuilder.getOrCreateRuntimeFunction(
6108 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6109 if (Bitwidth == 64)
6110 return OMPBuilder.getOrCreateRuntimeFunction(
6111 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6112 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6113}
6114
6115// Returns an LLVM function to call for initializing loop bounds using OpenMP
6116// static scheduling depending on `type`. Only i32 and i64 are supported by the
6117// runtime. Always interpret integers as unsigned similarly to
6118// CanonicalLoopInfo.
6120 OpenMPIRBuilder &OMPBuilder) {
6121 unsigned Bitwidth = Ty->getIntegerBitWidth();
6122 if (Bitwidth == 32)
6123 return OMPBuilder.getOrCreateRuntimeFunction(
6124 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6125 if (Bitwidth == 64)
6126 return OMPBuilder.getOrCreateRuntimeFunction(
6127 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6128 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6129}
6130
6131OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6132 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6133 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6134 OMPScheduleType DistScheduleSchedType) {
6135 assert(CLI->isValid() && "Requires a valid canonical loop");
6136 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6137 "Require dedicated allocate IP");
6138
6139 // Set up the source location value for OpenMP runtime.
6140 Builder.restoreIP(CLI->getPreheaderIP());
6141 Builder.SetCurrentDebugLocation(DL);
6142
6143 uint32_t SrcLocStrSize;
6144 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6146 switch (LoopType) {
6147 case WorksharingLoopType::ForStaticLoop:
6148 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6149 break;
6150 case WorksharingLoopType::DistributeStaticLoop:
6151 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6152 break;
6153 case WorksharingLoopType::DistributeForStaticLoop:
6154 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6155 break;
6156 }
6157 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6158
6159 // Declare useful OpenMP runtime functions.
6160 Value *IV = CLI->getIndVar();
6161 Type *IVTy = IV->getType();
6162 FunctionCallee StaticInit =
6163 LoopType == WorksharingLoopType::DistributeForStaticLoop
6164 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6165 : getKmpcForStaticInitForType(IVTy, M, *this);
6166 FunctionCallee StaticFini =
6167 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6168
6169 // Allocate space for computed loop bounds as expected by the "init" function.
6170 Builder.SetInsertPoint(
6171 AllocaIP.getNodeParent()->getFirstNonPHIOrDbgOrAlloca());
6172
6173 Type *I32Type = Type::getInt32Ty(M.getContext());
6174 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6175 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6176 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6177 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6178 CLI->setLastIter(PLastIter);
6179
6180 // At the end of the preheader, prepare for calling the "init" function by
6181 // storing the current loop bounds into the allocated space. A canonical loop
6182 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6183 // and produces an inclusive upper bound.
6184 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6185 Constant *Zero = ConstantInt::get(IVTy, 0);
6186 Constant *One = ConstantInt::get(IVTy, 1);
6187 Builder.CreateStore(Zero, PLowerBound);
6188 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6189 Builder.CreateStore(UpperBound, PUpperBound);
6190 Builder.CreateStore(One, PStride);
6191
6192 Value *ThreadNum =
6193 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6194
6195 OMPScheduleType SchedType =
6196 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6197 ? OMPScheduleType::OrderedDistribute
6199 Constant *SchedulingType =
6200 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6201
6202 // Call the "init" function and update the trip count of the loop with the
6203 // value it produced.
6204 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6205 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6206 this](Value *SchedulingType, auto &Builder) {
6207 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6208 PLowerBound, PUpperBound});
6209 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6210 Value *PDistUpperBound =
6211 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6212 Args.push_back(PDistUpperBound);
6213 }
6214 Args.append({PStride, One, Zero});
6215 createRuntimeFunctionCall(StaticInit, Args);
6216 };
6217 BuildInitCall(SchedulingType, Builder);
6218 if (HasDistSchedule &&
6219 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6220 Constant *DistScheduleSchedType = ConstantInt::get(
6221 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6222 // We want to emit a second init function call for the dist_schedule clause
6223 // to the Distribute construct. This should only be done however if a
6224 // Workshare Loop is nested within a Distribute Construct
6225 BuildInitCall(DistScheduleSchedType, Builder);
6226 }
6227 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6228 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6229 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6230 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6231 CLI->setTripCount(TripCount);
6232
6233 // Update all uses of the induction variable except the one in the condition
6234 // block that compares it with the actual upper bound, and the increment in
6235 // the latch block.
6236
6237 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6238 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6239 Builder.SetCurrentDebugLocation(DL);
6240 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6241 /*HasNSW=*/Config.hasNoSignedWrap());
6242 });
6243
6244 // In the "exit" block, call the "fini" function.
6245 Builder.SetInsertPoint(CLI->getExit()->getTerminator()->getIterator());
6246 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6247
6248 // Add the barrier if requested.
6249 if (NeedsBarrier) {
6250 InsertPointOrErrorTy BarrierIP =
6252 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6253 /* CheckCancelFlag */ false);
6254 if (!BarrierIP)
6255 return BarrierIP.takeError();
6256 }
6257
6258 InsertPointTy AfterIP = CLI->getAfterIP();
6259 CLI->invalidate();
6260
6261 return AfterIP;
6262}
6263
6264static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6265 LoopInfo &LI);
6266static void addLoopMetadata(CanonicalLoopInfo *Loop,
6268
6270 LLVMContext &Ctx, Loop *Loop,
6272 SmallVector<Metadata *> &LoopMDList) {
6273 SmallSet<BasicBlock *, 8> Reachable;
6274
6275 // Get the basic blocks from the loop in which memref instructions
6276 // can be found.
6277 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6278 // preferably without running any passes.
6279 for (BasicBlock *Block : Loop->getBlocks()) {
6280 if (Block == CLI->getCond() || Block == CLI->getHeader())
6281 continue;
6282 Reachable.insert(Block);
6283 }
6284
6285 // Add access group metadata to memory-access instructions.
6287 for (BasicBlock *BB : Reachable)
6289 // TODO: If the loop has existing parallel access metadata, have
6290 // to combine two lists.
6291 LoopMDList.push_back(MDNode::get(
6292 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6293}
6294
6296OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6297 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6298 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6299 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6300 assert(CLI->isValid() && "Requires a valid canonical loop");
6301 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6302
6303 LLVMContext &Ctx = CLI->getFunction()->getContext();
6304 Value *IV = CLI->getIndVar();
6305 Value *OrigTripCount = CLI->getTripCount();
6306 Type *IVTy = IV->getType();
6307 assert(IVTy->getIntegerBitWidth() <= 64 &&
6308 "Max supported tripcount bitwidth is 64 bits");
6309 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6310 : Type::getInt64Ty(Ctx);
6311 Type *I32Type = Type::getInt32Ty(M.getContext());
6312 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6313 Constant *One = ConstantInt::get(InternalIVTy, 1);
6314
6315 Function *F = CLI->getFunction();
6316 // Blocks must have terminators.
6317 // FIXME: Don't run analyses on incomplete/invalid IR.
6318 SmallVector<Instruction *> UIs;
6319 for (BasicBlock &BB : *F)
6320 if (!BB.hasTerminator())
6321 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6323 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6324 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6325 LoopAnalysis LIA;
6326 LoopInfo &&LI = LIA.run(*F, FAM);
6327 for (Instruction *I : UIs)
6328 I->eraseFromParent();
6329 Loop *L = LI.getLoopFor(CLI->getHeader());
6330 SmallVector<Metadata *> LoopMDList;
6331 if (ChunkSize || DistScheduleChunkSize)
6332 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6333 addLoopMetadata(CLI, LoopMDList);
6334
6335 // Declare useful OpenMP runtime functions.
6336 FunctionCallee StaticInit =
6337 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6338 FunctionCallee StaticFini =
6339 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6340
6341 // Allocate space for computed loop bounds as expected by the "init" function.
6342 Builder.restoreIP(AllocaIP);
6343 Builder.SetCurrentDebugLocation(DL);
6344 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6345 Value *PLowerBound =
6346 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6347 Value *PUpperBound =
6348 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6349 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6350 CLI->setLastIter(PLastIter);
6351
6352 // Set up the source location value for the OpenMP runtime.
6353 Builder.restoreIP(CLI->getPreheaderIP());
6354 Builder.SetCurrentDebugLocation(DL);
6355
6356 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6357 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6358 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6359 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6360 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6361 "distschedulechunksize");
6362 Value *CastedTripCount =
6363 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6364
6365 Constant *SchedulingType =
6366 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6367 Constant *DistSchedulingType =
6368 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6369 Builder.CreateStore(Zero, PLowerBound);
6370 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6371 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6372 Value *UpperBound =
6373 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6374 Builder.CreateStore(UpperBound, PUpperBound);
6375 Builder.CreateStore(One, PStride);
6376
6377 // Call the "init" function and update the trip count of the loop with the
6378 // value it produced.
6379 uint32_t SrcLocStrSize;
6380 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6381 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6382 if (DistScheduleSchedType != OMPScheduleType::None) {
6383 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6384 }
6385 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6386 Value *ThreadNum =
6387 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6388 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6389 PUpperBound, PStride, One,
6390 this](Value *SchedulingType, Value *ChunkSize,
6391 auto &Builder) {
6393 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6394 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6395 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6396 /*pstride=*/PStride, /*incr=*/One,
6397 /*chunk=*/ChunkSize});
6398 };
6399 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6400 if (DistScheduleSchedType != OMPScheduleType::None &&
6401 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6402 SchedType != OMPScheduleType::OrderedDistribute) {
6403 // We want to emit a second init function call for the dist_schedule clause
6404 // to the Distribute construct. This should only be done however if a
6405 // Workshare Loop is nested within a Distribute Construct
6406 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6407 }
6408
6409 // Load values written by the "init" function.
6410 Value *FirstChunkStart =
6411 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6412 Value *FirstChunkStop =
6413 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6414 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6415 Value *ChunkRange =
6416 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6417 Value *NextChunkStride =
6418 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6419
6420 // Create outer "dispatch" loop for enumerating the chunks.
6421 BasicBlock *DispatchEnter = splitBB(Builder, true);
6422 Value *DispatchCounter;
6423
6424 // It is safe to assume this didn't return an error because the callback
6425 // passed into createCanonicalLoop is the only possible error source, and it
6426 // always returns success.
6427 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6428 {Builder.saveIP(), DL},
6429 [&](InsertPointTy BodyIP, Value *Counter) {
6430 DispatchCounter = Counter;
6431 return Error::success();
6432 },
6433 FirstChunkStart, CastedTripCount, NextChunkStride,
6434 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6435 "dispatch"));
6436
6437 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6438 // not have to preserve the canonical invariant.
6439 BasicBlock *DispatchBody = DispatchCLI->getBody();
6440 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6441 BasicBlock *DispatchExit = DispatchCLI->getExit();
6442 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6443 DispatchCLI->invalidate();
6444
6445 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6446 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6447 redirectTo(CLI->getExit(), DispatchLatch, DL);
6448 redirectTo(DispatchBody, DispatchEnter, DL);
6449
6450 // Prepare the prolog of the chunk loop.
6451 Builder.restoreIP(CLI->getPreheaderIP());
6452 Builder.SetCurrentDebugLocation(DL);
6453
6454 // Compute the number of iterations of the chunk loop.
6455 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6456 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6457 Value *IsLastChunk =
6458 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6459 Value *CountUntilOrigTripCount =
6460 Builder.CreateSub(CastedTripCount, DispatchCounter);
6461 Value *ChunkTripCount = Builder.CreateSelect(
6462 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6463 Value *BackcastedChunkTC =
6464 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6465 CLI->setTripCount(BackcastedChunkTC);
6466
6467 // Update all uses of the induction variable except the one in the condition
6468 // block that compares it with the actual upper bound, and the increment in
6469 // the latch block.
6470 Value *BackcastedDispatchCounter =
6471 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6472 CLI->mapIndVar([&](Instruction *) -> Value * {
6473 Builder.restoreIP(CLI->getBodyIP());
6474 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6475 });
6476
6477 // In the "exit" block, call the "fini" function.
6478 Builder.SetInsertPoint(DispatchExit->getFirstInsertionPt());
6479 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6480
6481 // Add the barrier if requested.
6482 if (NeedsBarrier) {
6483 InsertPointOrErrorTy AfterIP =
6484 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6485 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6486 if (!AfterIP)
6487 return AfterIP.takeError();
6488 }
6489
6490#ifndef NDEBUG
6491 // Even though we currently do not support applying additional methods to it,
6492 // the chunk loop should remain a canonical loop.
6493 CLI->assertOK();
6494#endif
6495
6496 return DispatchAfter->getFirstInsertionPt();
6497}
6498
6499// Returns an LLVM function to call for executing an OpenMP static worksharing
6500// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6501// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6502static FunctionCallee
6504 WorksharingLoopType LoopType) {
6505 unsigned Bitwidth = Ty->getIntegerBitWidth();
6506 Module &M = OMPBuilder->M;
6507 switch (LoopType) {
6508 case WorksharingLoopType::ForStaticLoop:
6509 if (Bitwidth == 32)
6510 return OMPBuilder->getOrCreateRuntimeFunction(
6511 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6512 if (Bitwidth == 64)
6513 return OMPBuilder->getOrCreateRuntimeFunction(
6514 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6515 break;
6516 case WorksharingLoopType::DistributeStaticLoop:
6517 if (Bitwidth == 32)
6518 return OMPBuilder->getOrCreateRuntimeFunction(
6519 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6520 if (Bitwidth == 64)
6521 return OMPBuilder->getOrCreateRuntimeFunction(
6522 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6523 break;
6524 case WorksharingLoopType::DistributeForStaticLoop:
6525 if (Bitwidth == 32)
6526 return OMPBuilder->getOrCreateRuntimeFunction(
6527 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6528 if (Bitwidth == 64)
6529 return OMPBuilder->getOrCreateRuntimeFunction(
6530 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6531 break;
6532 }
6533 if (Bitwidth != 32 && Bitwidth != 64) {
6534 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6535 }
6536 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6537}
6538
6539// Inserts a call to proper OpenMP Device RTL function which handles
6540// loop worksharing.
6542 WorksharingLoopType LoopType,
6543 BasicBlock *InsertBlock, Value *Ident,
6544 Value *LoopBodyArg, Value *TripCount,
6545 Function &LoopBodyFn, bool NoLoop) {
6546 Type *TripCountTy = TripCount->getType();
6547 Module &M = OMPBuilder->M;
6548 IRBuilder<> &Builder = OMPBuilder->Builder;
6549 FunctionCallee RTLFn =
6550 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6551 SmallVector<Value *, 8> RealArgs;
6552 RealArgs.push_back(Ident);
6553 RealArgs.push_back(&LoopBodyFn);
6554 RealArgs.push_back(LoopBodyArg);
6555 RealArgs.push_back(TripCount);
6556 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6557 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6558 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6559 Builder.restoreIP(std::prev(InsertBlock->end()));
6560 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6561 return;
6562 }
6563 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6564 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6565 Builder.restoreIP(std::prev(InsertBlock->end()));
6566 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6567
6568 RealArgs.push_back(
6569 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6570 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6571 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6572 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6573 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6574 } else {
6575 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6576 }
6577
6578 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6579}
6580
6582 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6583 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6584 WorksharingLoopType LoopType, bool NoLoop) {
6585 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6586 BasicBlock *Preheader = CLI->getPreheader();
6587 Value *TripCount = CLI->getTripCount();
6588
6589 // After loop body outling, the loop body contains only set up
6590 // of loop body argument structure and the call to the outlined
6591 // loop body function. Firstly, we need to move setup of loop body args
6592 // into loop preheader.
6593 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6594 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6595
6596 // The next step is to remove the whole loop. We do not it need anymore.
6597 // That's why make an unconditional branch from loop preheader to loop
6598 // exit block
6599 Builder.restoreIP(Preheader->end());
6600 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6601 Preheader->getTerminator()->eraseFromParent();
6602 Builder.CreateBr(CLI->getExit());
6603
6604 // Delete dead loop blocks
6605 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6606 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6607 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6608 CleanUpInfo.EntryBB = CLI->getHeader();
6609 CleanUpInfo.ExitBB = CLI->getExit();
6610 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6611 DeleteDeadBlocks(BlocksToBeRemoved);
6612
6613 // Find the instruction which corresponds to loop body argument structure
6614 // and remove the call to loop body function instruction.
6615 Value *LoopBodyArg;
6616 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6617 assert(OutlinedFnUser &&
6618 "Expected unique undroppable user of outlined function");
6619 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6620 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6621 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6622 "Expected outlined function call to be located in loop preheader");
6623 // Check in case no argument structure has been passed.
6624 if (OutlinedFnCallInstruction->arg_size() > 1)
6625 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6626 else
6627 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6628 OutlinedFnCallInstruction->eraseFromParent();
6629
6630 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6631 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6632
6633 for (auto &ToBeDeletedItem : ToBeDeleted)
6634 ToBeDeletedItem->eraseFromParent();
6635 CLI->invalidate();
6636}
6637
6638OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6639 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6640 WorksharingLoopType LoopType, bool NeedsBarrier, bool NoLoop,
6641 bool NeedsLastIter) {
6642 uint32_t SrcLocStrSize;
6643 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6644
6645 // Mirrors host runtime reporting of last iteration by in-body computation.
6646 if (NeedsLastIter) {
6647 Type *I32Type = Type::getInt32Ty(M.getContext());
6648 Builder.restoreIP(AllocaIP);
6649 AllocaInst *PLastIter =
6650 Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6651 CLI->setLastIter(PLastIter);
6652
6653 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6654 Builder.CreateStore(ConstantInt::get(I32Type, 0), PLastIter);
6655
6656 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6657 Value *TripCount = CLI->getTripCount();
6658 Value *LastIter =
6659 Builder.CreateSub(TripCount, ConstantInt::get(TripCount->getType(), 1));
6660 Value *IsLast =
6661 Builder.CreateICmpEQ(CLI->getIndVar(), LastIter, "omp.is_last_iter");
6662 Builder.CreateStore(Builder.CreateZExt(IsLast, I32Type), PLastIter);
6663 }
6664
6666 switch (LoopType) {
6667 case WorksharingLoopType::ForStaticLoop:
6668 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6669 break;
6670 case WorksharingLoopType::DistributeStaticLoop:
6671 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6672 break;
6673 case WorksharingLoopType::DistributeForStaticLoop:
6674 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6675 break;
6676 }
6677 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6678
6679 auto OI = std::make_unique<OutlineInfo>();
6680 OI->OuterAllocBB = CLI->getPreheader();
6681 Function *OuterFn = CLI->getPreheader()->getParent();
6682
6683 // Instructions which need to be deleted at the end of code generation
6684 SmallVector<Instruction *, 4> ToBeDeleted;
6685
6686 OI->OuterAllocBB = AllocaIP.getNodeParent();
6687
6688 // Mark the body loop as region which needs to be extracted
6689 OI->EntryBB = CLI->getBody();
6690 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6691 "omp.prelatch");
6692
6693 // Prepare loop body for extraction
6694 Builder.restoreIP(CLI->getPreheader()->begin());
6695
6696 // Insert new loop counter variable which will be used only in loop
6697 // body.
6698 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6699 Instruction *NewLoopCntLoad =
6700 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6701 // New loop counter instructions are redundant in the loop preheader when
6702 // code generation for workshare loop is finshed. That's why mark them as
6703 // ready for deletion.
6704 ToBeDeleted.push_back(NewLoopCntLoad);
6705 ToBeDeleted.push_back(NewLoopCnt);
6706
6707 // Analyse loop body region. Find all input variables which are used inside
6708 // loop body region.
6709 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6711 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6712
6713 CodeExtractorAnalysisCache CEAC(*OuterFn);
6714 CodeExtractor Extractor(Blocks,
6715 /* DominatorTree */ nullptr,
6716 /* AggregateArgs */ true,
6717 /* BlockFrequencyInfo */ nullptr,
6718 /* BranchProbabilityInfo */ nullptr,
6719 /* AssumptionCache */ nullptr,
6720 /* AllowVarArgs */ true,
6721 /* AllowAlloca */ true,
6722 /* AllocationBlock */ CLI->getPreheader(),
6723 /* DeallocationBlocks */ {},
6724 /* Suffix */ ".omp_wsloop",
6725 /* AggrArgsIn0AddrSpace */ true);
6726
6727 BasicBlock *CommonExit = nullptr;
6728 SetVector<Value *> SinkingCands, HoistingCands;
6729
6730 // Find allocas outside the loop body region which are used inside loop
6731 // body
6732 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6733
6734 // We need to model loop body region as the function f(cnt, loop_arg).
6735 // That's why we replace loop induction variable by the new counter
6736 // which will be one of loop body function argument
6738 CLI->getIndVar()->user_end());
6739 for (auto Use : Users) {
6740 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6741 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6742 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6743 }
6744 }
6745 }
6746 // Make sure that loop counter variable is not merged into loop body
6747 // function argument structure and it is passed as separate variable
6748 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6749
6750 // PostOutline CB is invoked when loop body function is outlined and
6751 // loop body is replaced by call to outlined function. We need to add
6752 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6753 // function will handle loop control logic.
6754 //
6755 OI->PostOutlineCB = [=, ToBeDeletedVec =
6756 std::move(ToBeDeleted)](Function &OutlinedFn) {
6757 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6758 LoopType, NoLoop);
6759 };
6760 addOutlineInfo(std::move(OI));
6761
6762 // Keep the barrier outside the outlined loop body so that every thread
6763 // encounters it, including threads that execute no iterations.
6764 if (NeedsBarrier) {
6765 Builder.SetInsertPoint(CLI->getExit()->getTerminator());
6766 // Standalone distribute loops never request a barrier. For both regular
6767 // worksharing loops and combined distribute/for loops, the barrier is
6768 // associated with the worksharing loop, hence OMPD_for.
6769 InsertPointOrErrorTy BarrierIP =
6770 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6771 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6772 if (!BarrierIP)
6773 return BarrierIP.takeError();
6774 }
6775 return CLI->getAfterIP();
6776}
6777
6780 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6781 bool HasSimdModifier, bool HasMonotonicModifier,
6782 bool HasNonmonotonicModifier, bool HasOrderedClause,
6783 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6784 Value *DistScheduleChunkSize, bool NeedsLastIter) {
6785 if (Config.isTargetDevice())
6786 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6787 NoLoop, NeedsLastIter);
6788 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6789 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6790 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6791
6792 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6793 OMPScheduleType::ModifierOrdered;
6794 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6795 if (HasDistSchedule) {
6796 DistScheduleSchedType = DistScheduleChunkSize
6797 ? OMPScheduleType::OrderedDistributeChunked
6798 : OMPScheduleType::OrderedDistribute;
6799 }
6800 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6801 case OMPScheduleType::BaseStatic:
6802 case OMPScheduleType::BaseDistribute:
6803 assert((!ChunkSize || !DistScheduleChunkSize) &&
6804 "No chunk size with static-chunked schedule");
6805 if (IsOrdered && !HasDistSchedule)
6806 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6807 NeedsBarrier, ChunkSize);
6808 // FIXME: Monotonicity ignored?
6809 if (DistScheduleChunkSize)
6810 return applyStaticChunkedWorkshareLoop(
6811 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6812 DistScheduleChunkSize, DistScheduleSchedType);
6813 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6814 HasDistSchedule);
6815
6816 case OMPScheduleType::BaseStaticChunked:
6817 case OMPScheduleType::BaseDistributeChunked:
6818 if (IsOrdered && !HasDistSchedule)
6819 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6820 NeedsBarrier, ChunkSize);
6821 // FIXME: Monotonicity ignored?
6822 return applyStaticChunkedWorkshareLoop(
6823 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6824 DistScheduleChunkSize, DistScheduleSchedType);
6825
6826 case OMPScheduleType::BaseRuntime:
6827 case OMPScheduleType::BaseAuto:
6828 case OMPScheduleType::BaseGreedy:
6829 case OMPScheduleType::BaseBalanced:
6830 case OMPScheduleType::BaseSteal:
6831 case OMPScheduleType::BaseRuntimeSimd:
6832 assert(!ChunkSize &&
6833 "schedule type does not support user-defined chunk sizes");
6834 [[fallthrough]];
6835 case OMPScheduleType::BaseGuidedSimd:
6836 case OMPScheduleType::BaseDynamicChunked:
6837 case OMPScheduleType::BaseGuidedChunked:
6838 case OMPScheduleType::BaseGuidedIterativeChunked:
6839 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6840 case OMPScheduleType::BaseStaticBalancedChunked:
6841 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6842 NeedsBarrier, ChunkSize);
6843
6844 default:
6845 llvm_unreachable("Unknown/unimplemented schedule kind");
6846 }
6847}
6848
6849/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6850/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6851/// the runtime. Always interpret integers as unsigned similarly to
6852/// CanonicalLoopInfo.
6853static FunctionCallee
6855 unsigned Bitwidth = Ty->getIntegerBitWidth();
6856 if (Bitwidth == 32)
6857 return OMPBuilder.getOrCreateRuntimeFunction(
6858 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6859 if (Bitwidth == 64)
6860 return OMPBuilder.getOrCreateRuntimeFunction(
6861 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6862 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6863}
6864
6865/// Returns an LLVM function to call for updating the next loop using OpenMP
6866/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6867/// the runtime. Always interpret integers as unsigned similarly to
6868/// CanonicalLoopInfo.
6869static FunctionCallee
6871 unsigned Bitwidth = Ty->getIntegerBitWidth();
6872 if (Bitwidth == 32)
6873 return OMPBuilder.getOrCreateRuntimeFunction(
6874 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6875 if (Bitwidth == 64)
6876 return OMPBuilder.getOrCreateRuntimeFunction(
6877 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6878 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6879}
6880
6881/// Returns an LLVM function to call for finalizing the dynamic loop using
6882/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6883/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6884static FunctionCallee
6886 unsigned Bitwidth = Ty->getIntegerBitWidth();
6887 if (Bitwidth == 32)
6888 return OMPBuilder.getOrCreateRuntimeFunction(
6889 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6890 if (Bitwidth == 64)
6891 return OMPBuilder.getOrCreateRuntimeFunction(
6892 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6893 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6894}
6895
6897OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6898 InsertPointTy AllocaIP,
6899 OMPScheduleType SchedType,
6900 bool NeedsBarrier, Value *Chunk) {
6901 assert(CLI->isValid() && "Requires a valid canonical loop");
6902 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6903 "Require dedicated allocate IP");
6905 "Require valid schedule type");
6906
6907 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6908 OMPScheduleType::ModifierOrdered;
6909
6910 // Set up the source location value for OpenMP runtime.
6911 Builder.SetCurrentDebugLocation(DL);
6912
6913 uint32_t SrcLocStrSize;
6914 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6915 Value *SrcLoc =
6916 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6917
6918 // Declare useful OpenMP runtime functions.
6919 Value *IV = CLI->getIndVar();
6920 Type *IVTy = IV->getType();
6921 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6922 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6923
6924 // Allocate space for computed loop bounds as expected by the "init" function.
6925 Builder.SetInsertPoint(
6926 AllocaIP.getNodeParent()->getFirstNonPHIOrDbgOrAlloca());
6927 Type *I32Type = Type::getInt32Ty(M.getContext());
6928 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6929 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6930 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6931 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6932 CLI->setLastIter(PLastIter);
6933
6934 // At the end of the preheader, prepare for calling the "init" function by
6935 // storing the current loop bounds into the allocated space. A canonical loop
6936 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6937 // and produces an inclusive upper bound.
6938 BasicBlock *PreHeader = CLI->getPreheader();
6939 Builder.SetInsertPoint(PreHeader->getTerminator());
6940 Constant *One = ConstantInt::get(IVTy, 1);
6941 Builder.CreateStore(One, PLowerBound);
6942 Value *UpperBound = CLI->getTripCount();
6943 Builder.CreateStore(UpperBound, PUpperBound);
6944 Builder.CreateStore(One, PStride);
6945
6946 BasicBlock *Header = CLI->getHeader();
6947 BasicBlock *Exit = CLI->getExit();
6948 BasicBlock *Cond = CLI->getCond();
6949 BasicBlock *Latch = CLI->getLatch();
6950 InsertPointTy AfterIP = CLI->getAfterIP();
6951
6952 // The CLI will be "broken" in the code below, as the loop is no longer
6953 // a valid canonical loop.
6954
6955 if (!Chunk)
6956 Chunk = One;
6957
6958 Value *ThreadNum =
6959 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6960
6961 Constant *SchedulingType =
6962 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6963
6964 // Call the "init" function.
6965 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6966 /* LowerBound */ One, UpperBound,
6967 /* step */ One, Chunk});
6968
6969 // An outer loop around the existing one.
6970 BasicBlock *OuterCond = BasicBlock::Create(
6971 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6972 PreHeader->getParent());
6973 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6974 Builder.SetInsertPoint(OuterCond->getFirstInsertionPt());
6976 DynamicNext,
6977 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6978 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6979 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6980 Value *LowerBound =
6981 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6982 Builder.CreateCondBr(MoreWork, Header, Exit);
6983
6984 // Change PHI-node in loop header to use outer cond rather than preheader,
6985 // and set IV to the LowerBound.
6986 Instruction *Phi = &Header->front();
6987 auto *PI = cast<PHINode>(Phi);
6988 PI->setIncomingBlock(0, OuterCond);
6989 PI->setIncomingValue(0, LowerBound);
6990
6991 // Then set the pre-header to jump to the OuterCond
6992 Instruction *Term = PreHeader->getTerminator();
6993 auto *Br = cast<UncondBrInst>(Term);
6994 Br->setSuccessor(OuterCond);
6995
6996 // Modify the inner condition:
6997 // * Use the UpperBound returned from the DynamicNext call.
6998 // * jump to the loop outer loop when done with one of the inner loops.
6999 Builder.SetInsertPoint(Cond->getFirstInsertionPt());
7000 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
7001 Instruction *Comp = &*Builder.GetInsertPoint();
7002 auto *CI = cast<CmpInst>(Comp);
7003 CI->setOperand(1, UpperBound);
7004 // Redirect the inner exit to branch to outer condition.
7005 Instruction *Branch = &Cond->back();
7006 auto *BI = cast<CondBrInst>(Branch);
7007 assert(BI->getSuccessor(1) == Exit);
7008 BI->setSuccessor(1, OuterCond);
7009
7010 // Call the "fini" function if "ordered" is present in wsloop directive.
7011 if (Ordered) {
7012 Builder.SetInsertPoint(&Latch->back());
7013 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
7014 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
7015 }
7016
7017 // Add the barrier if requested.
7018 if (NeedsBarrier) {
7019 Builder.SetInsertPoint(&Exit->back());
7020 InsertPointOrErrorTy BarrierIP =
7022 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
7023 /* CheckCancelFlag */ false);
7024 if (!BarrierIP)
7025 return BarrierIP.takeError();
7026 }
7027
7028 CLI->invalidate();
7029 return AfterIP;
7030}
7031
7032/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
7033/// after this \p OldTarget will be orphaned.
7035 BasicBlock *NewTarget, DebugLoc DL) {
7036 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
7037 redirectTo(Pred, NewTarget, DL);
7038}
7039
7041 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
7042 // We add a block to BBsToKeep iff we have proven it has an external use.
7044
7045 while (true) {
7046 bool Changed = false;
7047
7048 for (BasicBlock *BB : BBs) {
7049 if (BBsToKeep.contains(BB))
7050 continue;
7051
7052 for (Use &U : BB->uses()) {
7053 auto *UseInst = dyn_cast<Instruction>(U.getUser());
7054 if (!UseInst)
7055 continue;
7056 BasicBlock *UseBB = UseInst->getParent();
7057 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
7058 BBsToKeep.insert(BB);
7059 Changed = true;
7060 break;
7061 }
7062 }
7063 }
7064
7065 if (!Changed)
7066 break;
7067 }
7068
7070 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7071 DeleteDeadBlocks(BBsToDelete);
7072}
7073
7074CanonicalLoopInfo *
7076 InsertPointTy ComputeIP) {
7077 assert(Loops.size() >= 1 && "At least one loop required");
7078 size_t NumLoops = Loops.size();
7079
7080 // Nothing to do if there is already just one loop.
7081 if (NumLoops == 1)
7082 return Loops.front();
7083
7084 CanonicalLoopInfo *Outermost = Loops.front();
7085 CanonicalLoopInfo *Innermost = Loops.back();
7086 BasicBlock *OrigPreheader = Outermost->getPreheader();
7087 BasicBlock *OrigAfter = Outermost->getAfter();
7088 Function *F = OrigPreheader->getParent();
7089
7090 // Loop control blocks that may become orphaned later.
7091 SmallVector<BasicBlock *, 12> OldControlBBs;
7092 OldControlBBs.reserve(6 * Loops.size());
7094 Loop->collectControlBlocks(OldControlBBs);
7095
7096 // Setup the IRBuilder for inserting the trip count computation.
7097 Builder.SetCurrentDebugLocation(DL);
7098 if (ComputeIP.isValid())
7099 Builder.restoreIP(ComputeIP);
7100 else
7101 Builder.restoreIP(Outermost->getPreheaderIP());
7102
7103 // Derive the collapsed' loop trip count.
7104 // TODO: Find common/largest indvar type.
7105 Value *CollapsedTripCount = nullptr;
7106 for (CanonicalLoopInfo *L : Loops) {
7107 assert(L->isValid() &&
7108 "All loops to collapse must be valid canonical loops");
7109 Value *OrigTripCount = L->getTripCount();
7110 if (!CollapsedTripCount) {
7111 CollapsedTripCount = OrigTripCount;
7112 continue;
7113 }
7114
7115 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7116 CollapsedTripCount =
7117 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7118 }
7119
7120 // Create the collapsed loop control flow.
7121 CanonicalLoopInfo *Result =
7122 createLoopSkeleton(DL, CollapsedTripCount, F,
7123 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7124 /*IsCollapsed=*/true);
7125
7126 // Build the collapsed loop body code.
7127 // Start with deriving the input loop induction variables from the collapsed
7128 // one, using a divmod scheme. To preserve the original loops' order, the
7129 // innermost loop use the least significant bits.
7130 Builder.restoreIP(Result->getBodyIP());
7131
7132 Value *Leftover = Result->getIndVar();
7133 SmallVector<Value *> NewIndVars;
7134 NewIndVars.resize(NumLoops);
7135 for (int i = NumLoops - 1; i >= 1; --i) {
7136 Value *OrigTripCount = Loops[i]->getTripCount();
7137
7138 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7139 NewIndVars[i] = NewIndVar;
7140
7141 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7142 }
7143 // Outermost loop gets all the remaining bits.
7144 NewIndVars[0] = Leftover;
7145
7146 // Construct the loop body control flow.
7147 // We progressively construct the branch structure following in direction of
7148 // the control flow, from the leading in-between code, the loop nest body, the
7149 // trailing in-between code, and rejoining the collapsed loop's latch.
7150 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7151 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7152 // its predecessors as sources.
7153 BasicBlock *ContinueBlock = Result->getBody();
7154 BasicBlock *ContinuePred = nullptr;
7155 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7156 BasicBlock *NextSrc) {
7157 if (ContinueBlock)
7158 redirectTo(ContinueBlock, Dest, DL);
7159 else
7160 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7161
7162 ContinueBlock = nullptr;
7163 ContinuePred = NextSrc;
7164 };
7165
7166 // The code before the nested loop of each level.
7167 // Because we are sinking it into the nest, it will be executed more often
7168 // that the original loop. More sophisticated schemes could keep track of what
7169 // the in-between code is and instantiate it only once per thread.
7170 for (size_t i = 0; i < NumLoops - 1; ++i)
7171 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7172
7173 // Connect the loop nest body.
7174 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7175
7176 // The code after the nested loop at each level.
7177 for (size_t i = NumLoops - 1; i > 0; --i)
7178 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7179
7180 // Connect the finished loop to the collapsed loop latch.
7181 ContinueWith(Result->getLatch(), nullptr);
7182
7183 // Replace the input loops with the new collapsed loop.
7184 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7185 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7186
7187 // Replace the input loop indvars with the derived ones.
7188 for (size_t i = 0; i < NumLoops; ++i)
7189 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7190
7191 // Remove unused parts of the input loops.
7192 removeUnusedBlocksFromParent(OldControlBBs);
7193
7194 for (CanonicalLoopInfo *L : Loops)
7195 L->invalidate();
7196
7197#ifndef NDEBUG
7198 Result->assertOK();
7199#endif
7200 return Result;
7201}
7202
7203std::vector<CanonicalLoopInfo *>
7205 ArrayRef<Value *> TileSizes) {
7206 assert(TileSizes.size() == Loops.size() &&
7207 "Must pass as many tile sizes as there are loops");
7208 int NumLoops = Loops.size();
7209 assert(NumLoops >= 1 && "At least one loop to tile required");
7210
7211 CanonicalLoopInfo *OutermostLoop = Loops.front();
7212 CanonicalLoopInfo *InnermostLoop = Loops.back();
7213 Function *F = OutermostLoop->getBody()->getParent();
7214 BasicBlock *InnerEnter = InnermostLoop->getBody();
7215 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7216
7217 // Loop control blocks that may become orphaned later.
7218 SmallVector<BasicBlock *, 12> OldControlBBs;
7219 OldControlBBs.reserve(6 * Loops.size());
7221 Loop->collectControlBlocks(OldControlBBs);
7222
7223 // Collect original trip counts and induction variable to be accessible by
7224 // index. Also, the structure of the original loops is not preserved during
7225 // the construction of the tiled loops, so do it before we scavenge the BBs of
7226 // any original CanonicalLoopInfo.
7227 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7228 for (CanonicalLoopInfo *L : Loops) {
7229 assert(L->isValid() && "All input loops must be valid canonical loops");
7230 OrigTripCounts.push_back(L->getTripCount());
7231 OrigIndVars.push_back(L->getIndVar());
7232 }
7233
7234 // Collect the code between loop headers. These may contain SSA definitions
7235 // that are used in the loop nest body. To be usable with in the innermost
7236 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7237 // these instructions may be executed more often than before the tiling.
7238 // TODO: It would be sufficient to only sink them into body of the
7239 // corresponding tile loop.
7241 for (int i = 0; i < NumLoops - 1; ++i) {
7242 CanonicalLoopInfo *Surrounding = Loops[i];
7243 CanonicalLoopInfo *Nested = Loops[i + 1];
7244
7245 BasicBlock *EnterBB = Surrounding->getBody();
7246 BasicBlock *ExitBB = Nested->getHeader();
7247 InbetweenCode.emplace_back(EnterBB, ExitBB);
7248 }
7249
7250 // Compute the trip counts of the floor loops.
7251 Builder.SetCurrentDebugLocation(DL);
7252 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7253 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7254 for (int i = 0; i < NumLoops; ++i) {
7255 Value *TileSize = TileSizes[i];
7256 Value *OrigTripCount = OrigTripCounts[i];
7257 Type *IVType = OrigTripCount->getType();
7258
7259 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7260 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7261
7262 // 0 if tripcount divides the tilesize, 1 otherwise.
7263 // 1 means we need an additional iteration for a partial tile.
7264 //
7265 // Unfortunately we cannot just use the roundup-formula
7266 // (tripcount + tilesize - 1)/tilesize
7267 // because the summation might overflow. We do not want introduce undefined
7268 // behavior when the untiled loop nest did not.
7269 Value *FloorTripOverflow =
7270 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7271
7272 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7273 Value *FloorTripCount =
7274 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7275 "omp_floor" + Twine(i) + ".tripcount", true);
7276
7277 // Remember some values for later use.
7278 FloorCompleteCount.push_back(FloorCompleteTripCount);
7279 FloorCount.push_back(FloorTripCount);
7280 FloorRems.push_back(FloorTripRem);
7281 }
7282
7283 // Generate the new loop nest, from the outermost to the innermost.
7284 std::vector<CanonicalLoopInfo *> Result;
7285 Result.reserve(NumLoops * 2);
7286
7287 // The basic block of the surrounding loop that enters the nest generated
7288 // loop.
7289 BasicBlock *Enter = OutermostLoop->getPreheader();
7290
7291 // The basic block of the surrounding loop where the inner code should
7292 // continue.
7293 BasicBlock *Continue = OutermostLoop->getAfter();
7294
7295 // Where the next loop basic block should be inserted.
7296 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7297
7298 auto EmbeddNewLoop =
7299 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7300 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7301 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7302 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7303 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7304 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7305
7306 // Setup the position where the next embedded loop connects to this loop.
7307 Enter = EmbeddedLoop->getBody();
7308 Continue = EmbeddedLoop->getLatch();
7309 OutroInsertBefore = EmbeddedLoop->getLatch();
7310 return EmbeddedLoop;
7311 };
7312
7313 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7314 const Twine &NameBase) {
7315 for (auto P : enumerate(TripCounts)) {
7316 CanonicalLoopInfo *EmbeddedLoop =
7317 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7318 Result.push_back(EmbeddedLoop);
7319 }
7320 };
7321
7322 EmbeddNewLoops(FloorCount, "floor");
7323
7324 // Within the innermost floor loop, emit the code that computes the tile
7325 // sizes.
7326 Builder.SetInsertPoint(Enter->getTerminator());
7327 SmallVector<Value *, 4> TileCounts;
7328 for (int i = 0; i < NumLoops; ++i) {
7329 CanonicalLoopInfo *FloorLoop = Result[i];
7330 Value *TileSize = TileSizes[i];
7331
7332 Value *FloorIsEpilogue =
7333 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7334 Value *TileTripCount =
7335 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7336
7337 TileCounts.push_back(TileTripCount);
7338 }
7339
7340 // Create the tile loops.
7341 EmbeddNewLoops(TileCounts, "tile");
7342
7343 // Insert the inbetween code into the body.
7344 BasicBlock *BodyEnter = Enter;
7345 BasicBlock *BodyEntered = nullptr;
7346 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7347 BasicBlock *EnterBB = P.first;
7348 BasicBlock *ExitBB = P.second;
7349
7350 if (BodyEnter)
7351 redirectTo(BodyEnter, EnterBB, DL);
7352 else
7353 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7354
7355 BodyEnter = nullptr;
7356 BodyEntered = ExitBB;
7357 }
7358
7359 // Append the original loop nest body into the generated loop nest body.
7360 if (BodyEnter)
7361 redirectTo(BodyEnter, InnerEnter, DL);
7362 else
7363 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7365
7366 // Replace the original induction variable with an induction variable computed
7367 // from the tile and floor induction variables.
7368 Builder.restoreIP(Result.back()->getBodyIP());
7369 for (int i = 0; i < NumLoops; ++i) {
7370 CanonicalLoopInfo *FloorLoop = Result[i];
7371 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7372 Value *OrigIndVar = OrigIndVars[i];
7373 Value *Size = TileSizes[i];
7374
7375 Value *Scale =
7376 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7377 Value *Shift =
7378 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7379 OrigIndVar->replaceAllUsesWith(Shift);
7380 }
7381
7382 // Remove unused parts of the original loops.
7383 removeUnusedBlocksFromParent(OldControlBBs);
7384
7385 for (CanonicalLoopInfo *L : Loops)
7386 L->invalidate();
7387
7388#ifndef NDEBUG
7389 for (CanonicalLoopInfo *GenL : Result)
7390 GenL->assertOK();
7391#endif
7392 return Result;
7393}
7394
7395/// Attach metadata \p Properties to the basic block described by \p BB. If the
7396/// basic block already has metadata, the basic block properties are appended.
7399 // Nothing to do if no property to attach.
7400 if (Properties.empty())
7401 return;
7402
7403 LLVMContext &Ctx = BB->getContext();
7404 SmallVector<Metadata *> NewProperties;
7405 NewProperties.push_back(nullptr);
7406
7407 // If the basic block already has metadata, prepend it to the new metadata.
7408 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7409 if (Existing)
7410 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7411
7412 append_range(NewProperties, Properties);
7413 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7414 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7415
7416 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7417}
7418
7419/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7420/// loop already has metadata, the loop properties are appended.
7423 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7424
7425 // Attach metadata to the loop's latch
7426 BasicBlock *Latch = Loop->getLatch();
7427 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7429}
7430
7431/// Attach llvm.access.group metadata to the memref instructions of \p Block
7433 LoopInfo &LI) {
7434 for (Instruction &I : *Block) {
7435 if (I.mayReadOrWriteMemory()) {
7436 // TODO: This instruction may already have access group from
7437 // other pragmas e.g. #pragma clang loop vectorize. Append
7438 // so that the existing metadata is not overwritten.
7439 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7440 }
7441 }
7442}
7443
7444CanonicalLoopInfo *
7446 CanonicalLoopInfo *firstLoop = Loops.front();
7447 CanonicalLoopInfo *lastLoop = Loops.back();
7448 Function *F = firstLoop->getPreheader()->getParent();
7449
7450 // Loop control blocks that will become orphaned later
7451 SmallVector<BasicBlock *> oldControlBBs;
7453 Loop->collectControlBlocks(oldControlBBs);
7454
7455 // Collect original trip counts
7456 SmallVector<Value *> origTripCounts;
7457 for (CanonicalLoopInfo *L : Loops) {
7458 assert(L->isValid() && "All input loops must be valid canonical loops");
7459 origTripCounts.push_back(L->getTripCount());
7460 }
7461
7462 Builder.SetCurrentDebugLocation(DL);
7463
7464 // Compute max trip count.
7465 // The fused loop will be from 0 to max(origTripCounts)
7466 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7467 F, firstLoop->getHeader());
7468 Builder.SetInsertPoint(TCBlock);
7469 Value *fusedTripCount = nullptr;
7470 for (CanonicalLoopInfo *L : Loops) {
7471 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7472 Value *origTripCount = L->getTripCount();
7473 if (!fusedTripCount) {
7474 fusedTripCount = origTripCount;
7475 continue;
7476 }
7477 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7478 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7479 ".omp.fuse.tc");
7480 }
7481
7482 // Generate new loop
7483 CanonicalLoopInfo *fused =
7484 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7485 lastLoop->getLatch(), "fused");
7486
7487 // Replace original loops with the fused loop
7488 // Preheader and After are not considered inside the CLI.
7489 // These are used to compute the individual TCs of the loops
7490 // so they have to be put before the resulting fused loop.
7491 // Moving them up for readability.
7492 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7493 Loops[i]->getPreheader()->moveBefore(TCBlock);
7494 Loops[i]->getAfter()->moveBefore(TCBlock);
7495 }
7496 lastLoop->getPreheader()->moveBefore(TCBlock);
7497
7498 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7499 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7500 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7501 }
7502 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7503 redirectTo(TCBlock, fused->getPreheader(), DL);
7504 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7505
7506 // Build the fused body
7507 // Create new Blocks with conditions that jump to the original loop bodies
7509 SmallVector<Value *> condValues;
7510 for (size_t i = 0; i < Loops.size(); ++i) {
7511 BasicBlock *condBlock = BasicBlock::Create(
7512 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7513 Builder.SetInsertPoint(condBlock);
7514 Value *condValue =
7515 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7516 condBBs.push_back(condBlock);
7517 condValues.push_back(condValue);
7518 }
7519 // Join the condition blocks with the bodies of the original loops
7520 redirectTo(fused->getBody(), condBBs[0], DL);
7521 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7522 Builder.SetInsertPoint(condBBs[i]);
7523 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7524 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7525 // Replace the IV with the fused IV
7526 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7527 }
7528 // Last body jumps to the created end body block
7529 Builder.SetInsertPoint(condBBs.back());
7530 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7531 fused->getLatch());
7532 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7533 // Replace the IV with the fused IV
7534 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7535
7536 // The loop latch must have only one predecessor. Currently it is branched to
7537 // from both the last condition block and the last loop body
7538 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7539 "omp.fused.pre_latch");
7540
7541 // Remove unused parts
7542 removeUnusedBlocksFromParent(oldControlBBs);
7543
7544 // Invalidate old CLIs
7545 for (CanonicalLoopInfo *L : Loops)
7546 L->invalidate();
7547
7548#ifndef NDEBUG
7549 fused->assertOK();
7550#endif
7551 return fused;
7552}
7553
7555 LLVMContext &Ctx = Builder.getContext();
7557 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7558 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7559}
7560
7562 LLVMContext &Ctx = Builder.getContext();
7564 Loop, {
7565 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7566 });
7567}
7568
7569void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7570 Value *IfCond, ValueToValueMapTy &VMap,
7571 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7572 const Twine &NamePrefix) {
7573 Function *F = CanonicalLoop->getFunction();
7574
7575 // We can't do
7576 // if (cond) {
7577 // simd_loop;
7578 // } else {
7579 // non_simd_loop;
7580 // }
7581 // because then the CanonicalLoopInfo would only point to one of the loops:
7582 // leading to other constructs operating on the same loop to malfunction.
7583 // Instead generate
7584 // while (...) {
7585 // if (cond) {
7586 // simd_body;
7587 // } else {
7588 // not_simd_body;
7589 // }
7590 // }
7591 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7592 // body at -O3
7593
7594 // Define where if branch should be inserted
7595 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7596
7597 // Create additional blocks for the if statement
7598 BasicBlock *Cond = SplitBeforeIt->getParent();
7599 llvm::LLVMContext &C = Cond->getContext();
7601 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7603 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7604
7605 // Create if condition branch.
7606 Builder.SetInsertPoint(SplitBeforeIt);
7607 Instruction *BrInstr =
7608 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7609 InsertPointTy IP(++BrInstr->getIterator());
7610 // Then block contains branch to omp loop body which needs to be vectorized
7611 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7612 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7613
7614 Builder.SetInsertPoint(ElseBlock);
7615
7616 // Clone loop for the else branch
7618
7619 SmallVector<BasicBlock *, 8> ExistingBlocks;
7620 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7621 ExistingBlocks.push_back(ThenBlock);
7622 ExistingBlocks.append(L->block_begin(), L->block_end());
7623 // Cond is the block that has the if clause condition
7624 // LoopCond is omp_loop.cond
7625 // LoopHeader is omp_loop.header
7626 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7627 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7628 assert(LoopCond && LoopHeader && "Invalid loop structure");
7629 for (BasicBlock *Block : ExistingBlocks) {
7630 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7631 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7632 continue;
7633 }
7634 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7635
7636 // fix name not to be omp.if.then
7637 if (Block == ThenBlock)
7638 NewBB->setName(NamePrefix + ".if.else");
7639
7640 NewBB->moveBefore(CanonicalLoop->getExit());
7641 VMap[Block] = NewBB;
7642 NewBlocks.push_back(NewBB);
7643 }
7644 remapInstructionsInBlocks(NewBlocks, VMap);
7645 Builder.CreateBr(NewBlocks.front());
7646
7647 // The loop latch must have only one predecessor. Currently it is branched to
7648 // from both the 'then' and 'else' branches.
7649 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7650 NamePrefix + ".pre_latch");
7651
7652 // Ensure that the then block is added to the loop so we add the attributes in
7653 // the next step
7654 L->addBasicBlockToLoop(ThenBlock, LI);
7655}
7656
7657unsigned
7659 const StringMap<bool> &Features) {
7660 if (TargetTriple.isX86()) {
7661 if (Features.lookup("avx512f"))
7662 return 512;
7663 else if (Features.lookup("avx"))
7664 return 256;
7665 return 128;
7666 }
7667 if (TargetTriple.isPPC())
7668 return 128;
7669 if (TargetTriple.isWasm())
7670 return 128;
7671 if (TargetTriple.isSystemZ())
7672 return 64;
7673 return 0;
7674}
7675
7677 MapVector<Value *, Value *> AlignedVars,
7678 Value *IfCond, OrderKind Order,
7679 ConstantInt *Simdlen, ConstantInt *Safelen) {
7680 LLVMContext &Ctx = Builder.getContext();
7681
7682 Function *F = CanonicalLoop->getFunction();
7683
7684 // Blocks must have terminators.
7685 // FIXME: Don't run analyses on incomplete/invalid IR.
7687 for (BasicBlock &BB : *F)
7688 if (!BB.hasTerminator())
7689 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7690
7691 // TODO: We should not rely on pass manager. Currently we use pass manager
7692 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7693 // object. We should have a method which returns all blocks between
7694 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7696 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7697 FAM.registerPass([]() { return LoopAnalysis(); });
7698 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7699
7700 LoopAnalysis LIA;
7701 LoopInfo &&LI = LIA.run(*F, FAM);
7702
7703 for (Instruction *I : UIs)
7704 I->eraseFromParent();
7705
7706 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7707 if (AlignedVars.size()) {
7708 InsertPointTy IP = Builder.saveIP();
7709 for (auto &AlignedItem : AlignedVars) {
7710 Value *AlignedPtr = AlignedItem.first;
7711 Value *Alignment = AlignedItem.second;
7712 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7713 Builder.SetInsertPoint(loadInst->getNextNode());
7714 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7715 Alignment);
7716 }
7717 Builder.restoreIP(IP);
7718 }
7719
7720 if (IfCond) {
7721 ValueToValueMapTy VMap;
7722 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7723 }
7724
7726
7727 // Get the basic blocks from the loop in which memref instructions
7728 // can be found.
7729 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7730 // preferably without running any passes.
7731 for (BasicBlock *Block : L->getBlocks()) {
7732 if (Block == CanonicalLoop->getCond() ||
7733 Block == CanonicalLoop->getHeader())
7734 continue;
7735 Reachable.insert(Block);
7736 }
7737
7738 SmallVector<Metadata *> LoopMDList;
7739
7740 // In presence of finite 'safelen', it may be unsafe to mark all
7741 // the memory instructions parallel, because loop-carried
7742 // dependences of 'safelen' iterations are possible.
7743 // If clause order(concurrent) is specified then the memory instructions
7744 // are marked parallel even if 'safelen' is finite.
7745 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7746 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7747
7748 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7749 // versions so we can't add the loop attributes in that case.
7750 if (IfCond) {
7751 // we can still add llvm.loop.parallel_access
7752 addLoopMetadata(CanonicalLoop, LoopMDList);
7753 return;
7754 }
7755
7756 // Use the above access group metadata to create loop level
7757 // metadata, which should be distinct for each loop.
7758 LoopMDList.push_back(
7759 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7760
7761 if (Simdlen || Safelen) {
7762 // If both simdlen and safelen clauses are specified, the value of the
7763 // simdlen parameter must be less than or equal to the value of the safelen
7764 // parameter. Therefore, use safelen only in the absence of simdlen.
7765 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7766 LoopMDList.push_back(
7767 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7768 ConstantAsMetadata::get(VectorizeWidth)}));
7769 }
7770
7771 addLoopMetadata(CanonicalLoop, LoopMDList);
7772}
7773
7774/// Create the TargetMachine object to query the backend for optimization
7775/// preferences.
7776///
7777/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7778/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7779/// needed for the LLVM pass pipline. We use some default options to avoid
7780/// having to pass too many settings from the frontend that probably do not
7781/// matter.
7782///
7783/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7784/// method. If we are going to use TargetMachine for more purposes, especially
7785/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7786/// might become be worth requiring front-ends to pass on their TargetMachine,
7787/// or at least cache it between methods. Note that while fontends such as Clang
7788/// have just a single main TargetMachine per translation unit, "target-cpu" and
7789/// "target-features" that determine the TargetMachine are per-function and can
7790/// be overrided using __attribute__((target("OPTIONS"))).
7791static std::unique_ptr<TargetMachine>
7793 Module *M = F->getParent();
7794
7795 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7796 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7797 const llvm::Triple &Triple = M->getTargetTriple();
7798
7799 std::string Error;
7801 if (!TheTarget)
7802 return {};
7803
7805 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7806 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7807 /*CodeModel=*/std::nullopt, OptLevel));
7808}
7809
7810/// Heuristically determine the best-performant unroll factor for \p CLI. This
7811/// depends on the target processor. We are re-using the same heuristics as the
7812/// LoopUnrollPass.
7814 Function *F = CLI->getFunction();
7815
7816 // Assume the user requests the most aggressive unrolling, even if the rest of
7817 // the code is optimized using a lower setting.
7819 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7820
7821 // Blocks must have terminators.
7822 // FIXME: Don't run analyses on incomplete/invalid IR.
7824 for (BasicBlock &BB : *F)
7825 if (!BB.hasTerminator())
7826 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7827
7829 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7830 FAM.registerPass([]() { return AssumptionAnalysis(); });
7831 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7832 FAM.registerPass([]() { return LoopAnalysis(); });
7833 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7834 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7835 TargetIRAnalysis TIRA;
7836 if (TM)
7837 TIRA = TargetIRAnalysis(
7838 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7839 FAM.registerPass([&]() { return TIRA; });
7840
7841 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7843 ScalarEvolution &&SE = SEA.run(*F, FAM);
7845 DominatorTree &&DT = DTA.run(*F, FAM);
7846 LoopAnalysis LIA;
7847 LoopInfo &&LI = LIA.run(*F, FAM);
7849 AssumptionCache &&AC = ACT.run(*F, FAM);
7851
7852 for (Instruction *I : UIs)
7853 I->eraseFromParent();
7854
7855 Loop *L = LI.getLoopFor(CLI->getHeader());
7856 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7857
7859 L, SE, TTI,
7860 /*BlockFrequencyInfo=*/nullptr,
7861 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7862 /*UserThreshold=*/std::nullopt,
7863 /*UserAllowPartial=*/true,
7864 /*UserAllowRuntime=*/true,
7865 /*UserUpperBound=*/std::nullopt,
7866 /*UserFullUnrollMaxCount=*/std::nullopt);
7867
7868 UP.Force = true;
7869
7870 // Account for additional optimizations taking place before the LoopUnrollPass
7871 // would unroll the loop.
7874
7875 // Use normal unroll factors even if the rest of the code is optimized for
7876 // size.
7879
7880 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7881 << " Threshold=" << UP.Threshold << "\n"
7882 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7883 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7884 << " PartialOptSizeThreshold="
7885 << UP.PartialOptSizeThreshold << "\n");
7886
7887 // Disable peeling.
7890 /*UserAllowPeeling=*/false,
7891 /*UserAllowProfileBasedPeeling=*/false,
7892 /*UnrollingSpecficValues=*/false);
7893
7895 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7896
7897 // Assume that reads and writes to stack variables can be eliminated by
7898 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7899 // size.
7900 for (BasicBlock *BB : L->blocks()) {
7901 for (Instruction &I : *BB) {
7902 Value *Ptr;
7903 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7904 Ptr = Load->getPointerOperand();
7905 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7906 Ptr = Store->getPointerOperand();
7907 } else
7908 continue;
7909
7910 Ptr = Ptr->stripPointerCasts();
7911
7912 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7913 if (Alloca->getParent() == &F->getEntryBlock())
7914 EphValues.insert(&I);
7915 }
7916 }
7917 }
7918
7919 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7920
7921 // Loop is not unrollable if the loop contains certain instructions.
7922 if (!UCE.canUnroll()) {
7923 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7924 return 1;
7925 }
7926
7927 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7928 << "\n");
7929
7930 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7931 // be able to use it.
7932 int TripCount = 0;
7933 int MaxTripCount = 0;
7934 bool MaxOrZero = false;
7935 unsigned TripMultiple = 0;
7936
7937 unsigned Factor =
7938 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7939 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7940 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7941
7942 // This function returns 1 to signal to not unroll a loop.
7943 if (Factor == 0)
7944 return 1;
7945 return Factor;
7946}
7947
7949 int32_t Factor,
7950 CanonicalLoopInfo **UnrolledCLI) {
7951 assert(Factor >= 0 && "Unroll factor must not be negative");
7952
7953 Function *F = Loop->getFunction();
7954 LLVMContext &Ctx = F->getContext();
7955
7956 // If the unrolled loop is not used for another loop-associated directive, it
7957 // is sufficient to add metadata for the LoopUnrollPass.
7958 if (!UnrolledCLI) {
7959 SmallVector<Metadata *, 2> LoopMetadata;
7960 LoopMetadata.push_back(
7961 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7962
7963 if (Factor >= 1) {
7965 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7966 LoopMetadata.push_back(MDNode::get(
7967 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7968 }
7969
7970 addLoopMetadata(Loop, LoopMetadata);
7971 return;
7972 }
7973
7974 // Heuristically determine the unroll factor.
7975 if (Factor == 0)
7977
7978 // No change required with unroll factor 1.
7979 if (Factor == 1) {
7980 *UnrolledCLI = Loop;
7981 return;
7982 }
7983
7984 assert(Factor >= 2 &&
7985 "unrolling only makes sense with a factor of 2 or larger");
7986
7987 Type *IndVarTy = Loop->getIndVarType();
7988
7989 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7990 // unroll the inner loop.
7991 Value *FactorVal =
7992 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7993 /*isSigned=*/false));
7994 std::vector<CanonicalLoopInfo *> LoopNest =
7995 tileLoops(DL, {Loop}, {FactorVal});
7996 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7997 *UnrolledCLI = LoopNest[0];
7998 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7999
8000 // LoopUnrollPass can only fully unroll loops with constant trip count.
8001 // Unroll by the unroll factor with a fallback epilog for the remainder
8002 // iterations if necessary.
8004 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
8006 InnerLoop,
8007 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
8009 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
8010
8011#ifndef NDEBUG
8012 (*UnrolledCLI)->assertOK();
8013#endif
8014}
8015
8018 llvm::Value *BufSize, llvm::Value *CpyBuf,
8019 llvm::Value *CpyFn, llvm::Value *DidIt) {
8020 if (!updateToLocation(Loc))
8021 return Loc.IP;
8022
8023 uint32_t SrcLocStrSize;
8024 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8025 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8026 Value *ThreadId = getOrCreateThreadID(Ident);
8027
8028 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
8029
8030 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
8031
8032 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
8033 createRuntimeFunctionCall(Fn, Args);
8034
8035 return Builder.saveIP();
8036}
8037
8039 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8040 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
8042
8043 if (!updateToLocation(Loc))
8044 return Loc.IP;
8045
8046 // If needed allocate and initialize `DidIt` with 0.
8047 // DidIt: flag variable: 1=single thread; 0=not single thread.
8048 llvm::Value *DidIt = nullptr;
8049 if (!CPVars.empty()) {
8050 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
8051 Builder.CreateStore(Builder.getInt32(0), DidIt);
8052 }
8053
8054 Directive OMPD = Directive::OMPD_single;
8055 uint32_t SrcLocStrSize;
8056 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8057 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8058 Value *ThreadId = getOrCreateThreadID(Ident);
8059 Value *Args[] = {Ident, ThreadId};
8060
8061 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8062 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8063
8064 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8065 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8066
8067 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8068 if (Error Err = FiniCB(IP))
8069 return Err;
8070
8071 // The thread that executes the single region must set `DidIt` to 1.
8072 // This is used by __kmpc_copyprivate, to know if the caller is the
8073 // single thread or not.
8074 if (DidIt)
8075 Builder.CreateStore(Builder.getInt32(1), DidIt);
8076
8077 return Error::success();
8078 };
8079
8080 // generates the following:
8081 // if (__kmpc_single()) {
8082 // .... single region ...
8083 // __kmpc_end_single
8084 // }
8085 // __kmpc_copyprivate
8086 // __kmpc_barrier
8087
8088 InsertPointOrErrorTy AfterIP =
8089 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8090 /*Conditional*/ true,
8091 /*hasFinalize*/ true);
8092 if (!AfterIP)
8093 return AfterIP.takeError();
8094
8095 if (DidIt) {
8096 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8097 // NOTE BufSize is currently unused, so just pass 0.
8099 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8100 CPFuncs[I], DidIt);
8101 // NOTE __kmpc_copyprivate already inserts a barrier
8102 } else if (!IsNowait) {
8103 InsertPointOrErrorTy AfterIP =
8105 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8106 /* CheckCancelFlag */ false);
8107 if (!AfterIP)
8108 return AfterIP.takeError();
8109 }
8110 return Builder.saveIP();
8111}
8112
8115 BodyGenCallbackTy BodyGenCB,
8116 FinalizeCallbackTy FiniCB, bool IsNowait) {
8117
8118 if (!updateToLocation(Loc))
8119 return Loc.IP;
8120
8121 // All threads execute the scope body — no conditional entry.
8122 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8123 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8124 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8125 /*IsCancellable=*/false);
8126 if (!AfterIP)
8127 return AfterIP.takeError();
8128
8129 Builder.restoreIP(*AfterIP);
8130 if (!IsNowait) {
8131 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8132 omp::Directive::OMPD_unknown,
8133 /*ForceSimpleCall=*/false,
8134 /*CheckCancelFlag=*/false);
8135 if (!AfterIP)
8136 return AfterIP.takeError();
8137 }
8138 return Builder.saveIP();
8139}
8140
8142 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8143 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8144
8145 if (!updateToLocation(Loc))
8146 return Loc.IP;
8147
8148 Directive OMPD = Directive::OMPD_critical;
8149 uint32_t SrcLocStrSize;
8150 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8151 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8152 Value *ThreadId = getOrCreateThreadID(Ident);
8153 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8154 Value *Args[] = {Ident, ThreadId, LockVar};
8155
8156 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8157 Function *RTFn = nullptr;
8158 if (HintInst) {
8159 // Add Hint to entry Args and create call
8160 EnterArgs.push_back(HintInst);
8161 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8162 } else {
8163 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8164 }
8165 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8166
8167 Function *ExitRTLFn =
8168 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8169 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8170
8171 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8172 /*Conditional*/ false, /*hasFinalize*/ true);
8173}
8174
8177 InsertPointTy AllocaIP, unsigned NumLoops,
8178 ArrayRef<llvm::Value *> StoreValues,
8179 const Twine &Name, bool IsDependSource) {
8180 assert(
8181 llvm::all_of(StoreValues,
8182 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8183 "OpenMP runtime requires depend vec with i64 type");
8184
8185 if (!updateToLocation(Loc))
8186 return Loc.IP;
8187
8188 // Allocate space for vector and generate alloc instruction.
8189 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8190 Builder.restoreIP(AllocaIP);
8191 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8192 ArgsBase->setAlignment(Align(8));
8194
8195 // Store the index value with offset in depend vector.
8196 for (unsigned I = 0; I < NumLoops; ++I) {
8197 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8198 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8199 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8200 STInst->setAlignment(Align(8));
8201 }
8202
8203 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8204 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8205
8206 uint32_t SrcLocStrSize;
8207 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8208 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8209 Value *ThreadId = getOrCreateThreadID(Ident);
8210 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8211
8212 Function *RTLFn = nullptr;
8213 if (IsDependSource)
8214 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8215 else
8216 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8217 createRuntimeFunctionCall(RTLFn, Args);
8218
8219 return Builder.saveIP();
8220}
8221
8223 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8224 FinalizeCallbackTy FiniCB, bool IsThreads) {
8225 if (!updateToLocation(Loc))
8226 return Loc.IP;
8227
8228 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8229 Instruction *EntryCall = nullptr;
8230 Instruction *ExitCall = nullptr;
8231
8232 if (IsThreads) {
8233 uint32_t SrcLocStrSize;
8234 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8235 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8236 Value *ThreadId = getOrCreateThreadID(Ident);
8237 Value *Args[] = {Ident, ThreadId};
8238
8239 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8240 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8241
8242 Function *ExitRTLFn =
8243 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8244 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8245 }
8246
8247 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8248 /*Conditional*/ false, /*hasFinalize*/ true);
8249}
8250
8251OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8252 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8253 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8254 bool HasFinalize, bool IsCancellable) {
8255
8256 if (HasFinalize)
8257 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8258
8259 // Create inlined region's entry and body blocks, in preparation
8260 // for conditional creation
8261 BasicBlock *EntryBB = Builder.GetInsertBlock();
8262 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8264 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8265 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8266 BasicBlock *FiniBB =
8267 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8268
8269 Builder.SetInsertPoint(EntryBB->getTerminator());
8270 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8271
8272 // generate body
8273 if (Error Err =
8274 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8275 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8276 return Err;
8277
8278 // emit exit call and do any needed finalization.
8279 auto FinIP = FiniBB->getFirstInsertionPt();
8280 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8281 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8282 "Unexpected control flow graph state!!");
8283 InsertPointOrErrorTy AfterIP =
8284 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8285 if (!AfterIP)
8286 return AfterIP.takeError();
8287
8288 // If we are skipping the region of a non conditional, remove the exit
8289 // block, and clear the builder's insertion point.
8290 assert(SplitPos->getParent() == ExitBB &&
8291 "Unexpected Insertion point location!");
8292 auto merged = MergeBlockIntoPredecessor(ExitBB);
8293 BasicBlock *ExitPredBB = SplitPos->getParent();
8294 auto InsertBB = merged ? ExitPredBB : ExitBB;
8296 SplitPos->eraseFromParent();
8297 Builder.SetInsertPoint(InsertBB);
8298
8299 return Builder.saveIP();
8300}
8301
8302OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8303 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8304 // if nothing to do, Return current insertion point.
8305 if (!Conditional || !EntryCall)
8306 return Builder.saveIP();
8307
8308 BasicBlock *EntryBB = Builder.GetInsertBlock();
8309 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8310 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8311 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8312
8313 // Emit thenBB and set the Builder's insertion point there for
8314 // body generation next. Place the block after the current block.
8315 Function *CurFn = EntryBB->getParent();
8316 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8317
8318 // Move Entry branch to end of ThenBB, and replace with conditional
8319 // branch (If-stmt)
8320 Instruction *EntryBBTI = EntryBB->getTerminator();
8321 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8322 EntryBBTI->removeFromParent();
8323 Builder.SetInsertPoint(UI);
8324 Builder.Insert(EntryBBTI);
8325 UI->eraseFromParent();
8326 Builder.SetInsertPoint(ThenBB->getTerminator());
8327
8328 // return an insertion point to ExitBB.
8329 return ExitBB->getFirstInsertionPt();
8330}
8331
8332OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8333 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8334 bool HasFinalize) {
8335
8336 Builder.restoreIP(FinIP);
8337
8338 // If there is finalization to do, emit it before the exit call
8339 if (HasFinalize) {
8340 assert(!FinalizationStack.empty() &&
8341 "Unexpected finalization stack state!");
8342
8343 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8344 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8345
8346 BasicBlock *FinBB = FinIP.getNodeParent();
8347 if (Error Err = Fi.mergeFiniBB(Builder, FinBB))
8348 return std::move(Err);
8349
8350 // Exit condition: insertion point is before the terminator of the new Fini
8351 // block
8352 Builder.SetInsertPoint(FinBB->getTerminator());
8353 }
8354
8355 if (!ExitCall)
8356 return Builder.saveIP();
8357
8358 // place the Exitcall as last instruction before Finalization block terminator
8359 ExitCall->removeFromParent();
8360 Builder.Insert(ExitCall);
8361
8362 return ExitCall->getIterator();
8363}
8364
8366 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8367 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8368 if (!IP.isValid())
8369 return IP;
8370
8372
8373 // creates the following CFG structure
8374 // OMP_Entry : (MasterAddr != PrivateAddr)?
8375 // F T
8376 // | \
8377 // | copin.not.master
8378 // | /
8379 // v /
8380 // copyin.not.master.end
8381 // |
8382 // v
8383 // OMP.Entry.Next
8384
8385 BasicBlock *OMP_Entry = IP.getNodeParent();
8386 Function *CurFn = OMP_Entry->getParent();
8387 BasicBlock *CopyBegin =
8388 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8389 BasicBlock *CopyEnd = nullptr;
8390
8391 // If entry block is terminated, split to preserve the branch to following
8392 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8394 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8395 "copyin.not.master.end");
8396 OMP_Entry->getTerminator()->eraseFromParent();
8397 } else {
8398 CopyEnd =
8399 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8400 }
8401
8402 Builder.SetInsertPoint(OMP_Entry);
8403 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8404 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8405 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8406 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8407
8408 Builder.SetInsertPoint(CopyBegin);
8409 if (BranchtoEnd)
8410 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8411
8412 return Builder.saveIP();
8413}
8414
8416 Value *Size, Value *Allocator,
8417 std::string Name) {
8419 if (!updateToLocation(Loc))
8420 return nullptr;
8421
8422 uint32_t SrcLocStrSize;
8423 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8424 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8425 Value *ThreadId = getOrCreateThreadID(Ident);
8426 Value *Args[] = {ThreadId, Size, Allocator};
8427
8428 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8429
8430 return createRuntimeFunctionCall(Fn, Args, Name);
8431}
8432
8434 Value *Align, Value *Size,
8435 Value *Allocator,
8436 std::string Name) {
8438 if (!updateToLocation(Loc))
8439 return nullptr;
8440
8441 uint32_t SrcLocStrSize;
8442 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8443 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8444 Value *ThreadId = getOrCreateThreadID(Ident);
8445 Value *Args[] = {ThreadId, Align, Size, Allocator};
8446
8447 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8448
8449 return Builder.CreateCall(Fn, Args, Name);
8450}
8451
8453 Value *Addr, Value *Allocator,
8454 std::string Name) {
8456 if (!updateToLocation(Loc))
8457 return nullptr;
8458
8459 uint32_t SrcLocStrSize;
8460 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8461 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8462 Value *ThreadId = getOrCreateThreadID(Ident);
8463 Value *Args[] = {ThreadId, Addr, Allocator};
8464 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8465 return createRuntimeFunctionCall(Fn, Args, Name);
8466}
8467
8469 Value *Size,
8470 const Twine &Name) {
8473
8474 Value *Args[] = {Size};
8475 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8476 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8478 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8479 return Call;
8480}
8481
8483 Type *VarType,
8484 const Twine &Name) {
8485 return createOMPAllocShared(
8486 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8487}
8488
8490 Value *Addr, Value *Size,
8491 const Twine &Name) {
8494
8495 Value *Args[] = {Addr, Size};
8496 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8497 return Builder.CreateCall(Fn, Args, Name);
8498}
8499
8501 Value *Addr, Type *VarType,
8502 const Twine &Name) {
8503 return createOMPFreeShared(
8504 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8505 Name);
8506}
8507
8509 const LocationDescription &Loc, Value *InteropVar,
8511 Value *DependenceAddress, bool HaveNowaitClause) {
8514
8515 uint32_t SrcLocStrSize;
8516 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8517 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8518 Value *ThreadId = getOrCreateThreadID(Ident);
8519 if (Device == nullptr)
8521 else if (Device->getType() != Int32)
8522 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8523 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8524 if (NumDependences == nullptr) {
8525 NumDependences = ConstantInt::get(Int32, 0);
8526 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8527 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8528 }
8529 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8530 Value *Args[] = {
8531 Ident, ThreadId, InteropVar, InteropTypeVal,
8532 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8533
8534 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8535
8536 return createRuntimeFunctionCall(Fn, Args);
8537}
8538
8540 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8541 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8544
8545 uint32_t SrcLocStrSize;
8546 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8547 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8548 Value *ThreadId = getOrCreateThreadID(Ident);
8549 if (Device == nullptr)
8551 else if (Device->getType() != Int32)
8552 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8553 if (NumDependences == nullptr) {
8554 NumDependences = ConstantInt::get(Int32, 0);
8555 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8556 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8557 }
8558 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8559 Value *Args[] = {
8560 Ident, ThreadId, InteropVar, Device,
8561 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8562
8563 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8564
8565 return createRuntimeFunctionCall(Fn, Args);
8566}
8567
8569 Value *InteropVar, Value *Device,
8570 Value *NumDependences,
8571 Value *DependenceAddress,
8572 bool HaveNowaitClause) {
8575 uint32_t SrcLocStrSize;
8576 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8577 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8578 Value *ThreadId = getOrCreateThreadID(Ident);
8579 if (Device == nullptr)
8581 else if (Device->getType() != Int32)
8582 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8583 if (NumDependences == nullptr) {
8584 NumDependences = ConstantInt::get(Int32, 0);
8585 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8586 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8587 }
8588 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8589 Value *Args[] = {
8590 Ident, ThreadId, InteropVar, Device,
8591 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8592
8593 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8594
8595 return createRuntimeFunctionCall(Fn, Args);
8596}
8597
8600 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8603
8604 uint32_t SrcLocStrSize;
8605 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8606 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8607 Value *ThreadId = getOrCreateThreadID(Ident);
8608 Constant *ThreadPrivateCache =
8609 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8610 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8611
8612 Function *Fn =
8613 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8614
8615 return createRuntimeFunctionCall(Fn, Args);
8616}
8617
8619 const LocationDescription &Loc,
8621 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8622 "expected num_threads and num_teams to be specified");
8623
8624 if (!updateToLocation(Loc))
8625 return nullptr;
8626
8627 uint32_t SrcLocStrSize;
8628 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8629 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8630 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8631 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8632 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8633 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8634 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8635 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8636
8637 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8638 Function *Kernel = DebugKernelWrapper;
8639
8640 // We need to strip the debug prefix to get the correct kernel name.
8641 StringRef KernelName = Kernel->getName();
8642 const std::string DebugPrefix = "_debug__";
8643 if (KernelName.ends_with(DebugPrefix)) {
8644 KernelName = KernelName.drop_back(DebugPrefix.length());
8645 Kernel = M.getFunction(KernelName);
8646 assert(Kernel && "Expected the real kernel to exist");
8647 }
8648
8649 // Manifest the launch configuration in the metadata matching the kernel
8650 // environment.
8651 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8652 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8653 Attrs.MaxTeams.front());
8654
8655 // Don't derive or write thread bounds for Bare kernels.
8656 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8657 if (Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_BARE) {
8658 // If MaxThreads is not set and needs adjustment, select the maximum
8659 // between the default workgroup size and the MinThreads value. This is
8660 // only meaningful for targets with a known grid value (i.e. GPUs); for
8661 // other targets (e.g. host kernels) leave it unset so the runtime falls
8662 // back to its own device-specific default.
8663 if (MaxThreadsVal < 0 && UseDefaultMaxThreads && hasGridValue(T))
8664 MaxThreadsVal =
8665 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8666 Attrs.MinThreads.front());
8667
8668 // Generic mode runs the main thread on a warp of its own, past
8669 // thread_limit. Reserve the widest warp any target has. Not on SPIR-V,
8670 // causes problems with Level Zero.
8671 if (MaxThreadsVal > 0 &&
8672 Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC && hasGridValue(T) &&
8673 !T.isSPIRV())
8674 MaxThreadsVal = int32_t(
8675 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8676 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8677
8678 if (MaxThreadsVal > 0)
8679 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8680 MaxThreadsVal);
8681 }
8682
8683 Constant *MinThreads =
8684 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8685 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8686 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8687 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8688 Constant *ReductionDataSize =
8689 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8690
8691 const DataLayout &DL = M.getDataLayout();
8692
8693 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8694 Constant *DynamicEnvironmentInitializer =
8695 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8696 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8697 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8698 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8699 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8700 DL.getDefaultGlobalsAddressSpace());
8701 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8702
8703 Constant *DynamicEnvironment =
8704 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8705 ? DynamicEnvironmentGV
8706 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8707 DynamicEnvironmentPtr);
8708
8709 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8710 ConfigurationEnvironment, {
8711 UseGenericStateMachineVal,
8712 MayUseNestedParallelismVal,
8713 IsSPMDVal,
8714 MinThreads,
8715 MaxThreads,
8716 MinTeams,
8717 MaxTeams,
8718 ReductionDataSize,
8719 });
8720 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8721 KernelEnvironment, {
8722 ConfigurationEnvironmentInitializer,
8723 Ident,
8724 DynamicEnvironment,
8725 });
8726 std::string KernelEnvironmentName =
8727 (KernelName + "_kernel_environment").str();
8728 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8729 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8730 KernelEnvironmentInitializer, KernelEnvironmentName,
8731 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8732 DL.getDefaultGlobalsAddressSpace());
8733 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8734
8735 return KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8736 ? KernelEnvironmentGV
8737 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8738 KernelEnvironmentPtr);
8739}
8740
8742 const LocationDescription &Loc,
8744 Constant *KernelEnvironment = emitKernelEnvironment(Loc, Attrs);
8745 if (!KernelEnvironment)
8746 return Loc.IP;
8747
8748 if (!updateToLocation(Loc))
8749 return Loc.IP;
8750
8751 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8753 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8754
8755 Value *KernelLaunchEnvironment =
8756 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8757 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8758 KernelLaunchEnvironment =
8759 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8760 ? KernelLaunchEnvironment
8761 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8762 KernelLaunchEnvParamTy);
8763 CallInst *ThreadKind = createRuntimeFunctionCall(
8764 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8765
8766 Value *ExecUserCode = Builder.CreateICmpEQ(
8767 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8768 "exec_user_code");
8769
8770 // ThreadKind = __kmpc_target_init(...)
8771 // if (ThreadKind == -1)
8772 // user_code
8773 // else
8774 // return;
8775
8776 auto *UI = Builder.CreateUnreachable();
8777 BasicBlock *CheckBB = UI->getParent();
8778 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8779
8780 BasicBlock *WorkerExitBB = BasicBlock::Create(
8781 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8782 Builder.SetInsertPoint(WorkerExitBB);
8783 Builder.CreateRetVoid();
8784
8785 auto *CheckBBTI = CheckBB->getTerminator();
8786 Builder.SetInsertPoint(CheckBBTI);
8787 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8788
8789 CheckBBTI->eraseFromParent();
8790 UI->eraseFromParent();
8791
8792 // Continue in the "user_code" block, see diagram above and in
8793 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8794 return UserCodeEntryBB->getFirstInsertionPt();
8795}
8796
8798 int32_t TeamsReductionDataSize) {
8799 if (!updateToLocation(Loc))
8800 return;
8801
8803 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8804
8806
8807 if (!TeamsReductionDataSize)
8808 return;
8809
8810 Function *Kernel = Builder.GetInsertBlock()->getParent();
8811 // We need to strip the debug prefix to get the correct kernel name.
8812 StringRef KernelName = Kernel->getName();
8813 const std::string DebugPrefix = "_debug__";
8814 if (KernelName.ends_with(DebugPrefix))
8815 KernelName = KernelName.drop_back(DebugPrefix.length());
8816 auto *KernelEnvironmentGV =
8817 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8818 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8819 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8820 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8821 KernelEnvironmentInitializer,
8822 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8823 KernelEnvironmentGV->setInitializer(NewInitializer);
8824}
8825
8826static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8827 bool Min) {
8828 if (Kernel.hasFnAttribute(Name)) {
8829 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8830 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8831 }
8832 Kernel.addFnAttr(Name, llvm::utostr(Value));
8833}
8834
8835std::pair<int32_t, int32_t>
8837 int32_t ThreadLimit =
8838 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8839
8840 if (T.isAMDGPU()) {
8841 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8842 if (!Attr.isValid() || !Attr.isStringAttribute())
8843 return {0, ThreadLimit};
8844 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8845 int32_t LB, UB;
8846 if (!llvm::to_integer(UBStr, UB, 10))
8847 return {0, ThreadLimit};
8848 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8849 if (!llvm::to_integer(LBStr, LB, 10))
8850 return {0, UB};
8851 return {LB, UB};
8852 }
8853
8854 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8855 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8856 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8857 }
8858 return {0, ThreadLimit};
8859}
8860
8862 Function &Kernel, int32_t LB,
8863 int32_t UB) {
8864 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8865
8866 if (T.isAMDGPU()) {
8867 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8868 llvm::utostr(LB) + "," + llvm::utostr(UB));
8869 return;
8870 }
8871
8873}
8874
8875std::pair<int32_t, int32_t>
8877 // TODO: Read from backend annotations if available.
8878 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8879}
8880
8882 int32_t LB, int32_t UB) {
8883 if (UB > 0) {
8884 if (T.isNVPTX())
8886 if (T.isAMDGPU())
8887 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8888 }
8889
8890 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8891}
8892
8893void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8894 Function *OutlinedFn) {
8895 if (Config.isTargetDevice()) {
8897 // TODO: Determine if DSO local can be set to true.
8898 OutlinedFn->setDSOLocal(false);
8900 if (T.isAMDGCN())
8902 else if (T.isNVPTX())
8904 else if (T.isSPIRV())
8906 }
8907}
8908
8909Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8910 StringRef EntryFnIDName) {
8911 if (Config.isTargetDevice()) {
8912 assert(OutlinedFn && "The outlined function must exist if embedded");
8913 return OutlinedFn;
8914 }
8915
8916 return new GlobalVariable(
8917 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8918 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8919}
8920
8921Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8922 StringRef EntryFnName) {
8923 if (OutlinedFn)
8924 return OutlinedFn;
8925
8926 assert(!M.getGlobalVariable(EntryFnName, true) &&
8927 "Named kernel already exists?");
8928 return new GlobalVariable(
8929 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8930 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8931}
8932
8934 TargetRegionEntryInfo &EntryInfo,
8935 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8936 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8937
8938 SmallString<64> EntryFnName;
8939 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8940
8941 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8942 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8943 if (!CBResult)
8944 return CBResult.takeError();
8945 OutlinedFn = *CBResult;
8946 } else {
8947 OutlinedFn = nullptr;
8948 }
8949
8950 // If this target outline function is not an offload entry, we don't need to
8951 // register it. This may be in the case of a false if clause, or if there are
8952 // no OpenMP targets.
8953 if (!IsOffloadEntry)
8954 return Error::success();
8955
8956 std::string EntryFnIDName =
8957 Config.isTargetDevice()
8958 ? std::string(EntryFnName)
8959 : createPlatformSpecificName({EntryFnName, "region_id"});
8960
8961 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8962 EntryFnName, EntryFnIDName);
8963 return Error::success();
8964}
8965
8967 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8968 StringRef EntryFnName, StringRef EntryFnIDName) {
8969 if (OutlinedFn)
8970 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8971 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8972 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8973 OffloadInfoManager.registerTargetRegionEntryInfo(
8974 EntryInfo, EntryAddr, OutlinedFnID,
8976 return OutlinedFnID;
8977}
8978
8980 const LocationDescription &Loc, InsertPointTy AllocaIP,
8981 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8982 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8983 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8984 omp::RuntimeFunction *MapperFunc,
8986 BodyGenTy BodyGenType)>
8987 BodyGenCB,
8988 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8989 if (!updateToLocation(Loc))
8990 return InsertPointTy();
8991
8992 Builder.restoreIP(CodeGenIP);
8993
8994 bool IsStandAlone = !BodyGenCB;
8995 MapInfosTy *MapInfo;
8996 // Generate the code for the opening of the data environment. Capture all the
8997 // arguments of the runtime call by reference because they are used in the
8998 // closing of the region.
8999 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9000 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9001 MapInfo = &GenMapInfoCB(Builder.saveIP());
9002 if (Error Err = emitOffloadingArrays(
9003 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
9004 /*IsNonContiguous=*/true, DeviceAddrCB))
9005 return Err;
9006
9007 TargetDataRTArgs RTArgs;
9009
9010 // Emit the number of elements in the offloading arrays.
9011 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9012
9013 // Source location for the ident struct
9014 if (!SrcLocInfo) {
9015 uint32_t SrcLocStrSize;
9016 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9017 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9018 }
9019
9020 SmallVector<llvm::Value *, 13> OffloadingArgs = {
9021 SrcLocInfo, DeviceID,
9022 PointerNum, RTArgs.BasePointersArray,
9023 RTArgs.PointersArray, RTArgs.SizesArray,
9024 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9025 RTArgs.MappersArray};
9026
9027 if (IsStandAlone) {
9028 assert(MapperFunc && "MapperFunc missing for standalone target data");
9029
9030 auto TaskBodyCB = [&](Value *, Value *,
9032 if (Info.HasNoWait) {
9033 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
9037 }
9038
9040 OffloadingArgs);
9041
9042 if (Info.HasNoWait) {
9043 BasicBlock *OffloadContBlock =
9044 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
9045 Function *CurFn = Builder.GetInsertBlock()->getParent();
9046 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
9047 Builder.restoreIP(Builder.saveIP());
9048 }
9049 return Error::success();
9050 };
9051
9052 bool RequiresOuterTargetTask = Info.HasNoWait;
9053 if (!RequiresOuterTargetTask)
9054 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
9055 /*TargetTaskAllocaIP=*/{}));
9056 else
9057 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
9058 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
9059 } else {
9060 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
9061 omp::OMPRTL___tgt_target_data_begin_mapper);
9062
9063 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
9064
9065 for (auto DeviceMap : Info.DevicePtrInfoMap) {
9066 if (isa<AllocaInst>(DeviceMap.second.second)) {
9067 auto *LI =
9068 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
9069 Builder.CreateStore(LI, DeviceMap.second.second);
9070 }
9071 }
9072
9073 // If device pointer privatization is required, emit the body of the
9074 // region here. It will have to be duplicated: with and without
9075 // privatization.
9076 InsertPointOrErrorTy AfterIP =
9077 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9078 if (!AfterIP)
9079 return AfterIP.takeError();
9080 Builder.restoreIP(*AfterIP);
9081 }
9082 return Error::success();
9083 };
9084
9085 // If we need device pointer privatization, we need to emit the body of the
9086 // region with no privatization in the 'else' branch of the conditional.
9087 // Otherwise, we don't have to do anything.
9088 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9089 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9090 InsertPointOrErrorTy AfterIP =
9091 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9092 if (!AfterIP)
9093 return AfterIP.takeError();
9094 Builder.restoreIP(*AfterIP);
9095 return Error::success();
9096 };
9097
9098 // Generate code for the closing of the data region.
9099 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9100 ArrayRef<BasicBlock *> DeallocBlocks) {
9101 TargetDataRTArgs RTArgs;
9102 Info.EmitDebug = !MapInfo->Names.empty();
9103 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9104
9105 // Emit the number of elements in the offloading arrays.
9106 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9107
9108 // Source location for the ident struct
9109 if (!SrcLocInfo) {
9110 uint32_t SrcLocStrSize;
9111 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9112 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9113 }
9114
9115 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9116 PointerNum, RTArgs.BasePointersArray,
9117 RTArgs.PointersArray, RTArgs.SizesArray,
9118 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9119 RTArgs.MappersArray};
9120 Function *EndMapperFunc =
9121 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9122
9123 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9124 return Error::success();
9125 };
9126
9127 // We don't have to do anything to close the region if the if clause evaluates
9128 // to false.
9129 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9130 ArrayRef<BasicBlock *> DeallocBlocks) {
9131 return Error::success();
9132 };
9133
9134 Error Err = [&]() -> Error {
9135 if (BodyGenCB) {
9136 Error Err = [&]() {
9137 if (IfCond)
9138 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9139 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9140 }();
9141
9142 if (Err)
9143 return Err;
9144
9145 // If we don't require privatization of device pointers, we emit the body
9146 // in between the runtime calls. This avoids duplicating the body code.
9147 InsertPointOrErrorTy AfterIP =
9148 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9149 if (!AfterIP)
9150 return AfterIP.takeError();
9151 restoreIPandDebugLoc(Builder, *AfterIP);
9152
9153 if (IfCond)
9154 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9155 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9156 }
9157 if (IfCond)
9158 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9159 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9160 }();
9161
9162 if (Err)
9163 return Err;
9164
9165 return Builder.saveIP();
9166}
9167
9170 bool IsGPUDistribute) {
9171 assert((IVSize == 32 || IVSize == 64) &&
9172 "IV size is not compatible with the omp runtime");
9173 RuntimeFunction Name;
9174 if (IsGPUDistribute)
9175 Name = IVSize == 32
9176 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9177 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9178 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9179 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9180 else
9181 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9182 : omp::OMPRTL___kmpc_for_static_init_4u)
9183 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9184 : omp::OMPRTL___kmpc_for_static_init_8u);
9185
9186 return getOrCreateRuntimeFunction(M, Name);
9187}
9188
9190 bool IVSigned) {
9191 assert((IVSize == 32 || IVSize == 64) &&
9192 "IV size is not compatible with the omp runtime");
9193 RuntimeFunction Name = IVSize == 32
9194 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9195 : omp::OMPRTL___kmpc_dispatch_init_4u)
9196 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9197 : omp::OMPRTL___kmpc_dispatch_init_8u);
9198
9199 return getOrCreateRuntimeFunction(M, Name);
9200}
9201
9203 bool IVSigned) {
9204 assert((IVSize == 32 || IVSize == 64) &&
9205 "IV size is not compatible with the omp runtime");
9206 RuntimeFunction Name = IVSize == 32
9207 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9208 : omp::OMPRTL___kmpc_dispatch_next_4u)
9209 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9210 : omp::OMPRTL___kmpc_dispatch_next_8u);
9211
9212 return getOrCreateRuntimeFunction(M, Name);
9213}
9214
9216 bool IVSigned) {
9217 assert((IVSize == 32 || IVSize == 64) &&
9218 "IV size is not compatible with the omp runtime");
9219 RuntimeFunction Name = IVSize == 32
9220 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9221 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9222 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9223 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9224
9225 return getOrCreateRuntimeFunction(M, Name);
9226}
9227
9229 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9230}
9231
9233 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9234 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9235
9236 DISubprogram *NewSP = Func->getSubprogram();
9237 if (!NewSP)
9238 return;
9239
9241
9242 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9243 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9244 // Only use cached variable if the arg number matches. This is important
9245 // so that DIVariable created for privatized variables are not discarded.
9246 if (NewVar && (arg == NewVar->getArg()))
9247 return NewVar;
9248
9250 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9251 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9252 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9253 return NewVar;
9254 };
9255
9256 auto UpdateDebugRecord = [&](auto *DR) {
9257 DILocalVariable *OldVar = DR->getVariable();
9258 unsigned ArgNo = 0;
9259 for (auto Loc : DR->location_ops()) {
9260 auto Iter = ValueReplacementMap.find(Loc);
9261 if (Iter != ValueReplacementMap.end()) {
9262 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9263 ArgNo = std::get<1>(Iter->second) + 1;
9264 }
9265 }
9266 if (ArgNo != 0)
9267 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9268 };
9269
9271 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9272 if (DVR->getNumVariableLocationOps() != 1u) {
9273 DVR->setKillLocation();
9274 return;
9275 }
9276 Value *Loc = DVR->getVariableLocationOp(0u);
9277 BasicBlock *CurBB = DVR->getParent();
9278 BasicBlock *RequiredBB = nullptr;
9279
9280 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9281 RequiredBB = LocInst->getParent();
9282 else if (isa<llvm::Argument>(Loc))
9283 RequiredBB = &DVR->getFunction()->getEntryBlock();
9284
9285 if (RequiredBB && RequiredBB != CurBB) {
9286 assert(!RequiredBB->empty());
9287 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9288 RequiredBB->back().getIterator());
9289 DVRsToDelete.push_back(DVR);
9290 }
9291 };
9292
9293 // The location and scope of variable records still point to
9294 // the parent function of the target region. Update them.
9295 for (Instruction &I : instructions(Func)) {
9296 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9297 UpdateDebugRecord(&DVR);
9298 MoveDebugRecordToCorrectBlock(&DVR);
9299 }
9300 }
9301 for (auto *DVR : DVRsToDelete)
9302 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9303 // An extra argument is passed to the device. Create the debug data for it.
9304 if (OMPBuilder.Config.isTargetDevice()) {
9305 DICompileUnit *CU = NewSP->getUnit();
9306 Module *M = Func->getParent();
9307 DIBuilder DB(*M, true, CU);
9308 DIType *VoidPtrTy =
9309 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9310 unsigned ArgNo = Func->arg_size();
9311 DILocalVariable *Var = DB.createParameterVariable(
9312 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9313 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9314 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9315 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9316 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9317 &(*Func->begin()));
9318 }
9319}
9320
9322 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9323 return cast<Operator>(V)->getOperand(0);
9324 return V;
9325}
9326
9328 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9330 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9333 DebugLoc OutlinedFnLoc) {
9334 SmallVector<Type *> ParameterTypes;
9335 if (OMPBuilder.Config.isTargetDevice()) {
9336 // All parameters to target devices are passed as pointers
9337 // or i64. This assumes 64-bit address spaces/pointers.
9338 for (auto &Arg : Inputs)
9339 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9340 ? Arg->getType()
9341 : Type::getInt64Ty(Builder.getContext()));
9342 } else {
9343 for (auto &Arg : Inputs)
9344 ParameterTypes.push_back(Arg->getType());
9345 }
9346
9347 // The implicit dyn_ptr argument is always the last parameter on both host
9348 // and device so the argument counts match without runtime manipulation.
9349 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9350 ParameterTypes.push_back(PtrTy);
9351
9352 auto BB = Builder.GetInsertBlock();
9353 auto M = BB->getModule();
9354 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9355 /*isVarArg*/ false);
9356 auto Func =
9357 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9358
9359 // Forward target-cpu and target-features function attributes from the
9360 // original function to the new outlined function.
9361 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9362
9363 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9364 if (TargetCpuAttr.isStringAttribute())
9365 Func->addFnAttr(TargetCpuAttr);
9366
9367 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9368 if (TargetFeaturesAttr.isStringAttribute())
9369 Func->addFnAttr(TargetFeaturesAttr);
9370
9371 if (OMPBuilder.Config.isTargetDevice()) {
9372 Value *ExecMode =
9373 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9374 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9375 }
9376
9377 // Save insert point.
9378 IRBuilder<>::InsertPointGuard IPG(Builder);
9379 // We will generate the entries in the outlined function but the debug
9380 // location is still pointing to the parent function, which is the wrong
9381 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9382 // position scoped to the subprogram that will be attached to the outlined
9383 // function, so it is what everything emitted below needs.
9384 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9385
9386 // Generate the region into the function.
9387 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9388 Builder.SetInsertPoint(EntryBB);
9389
9390 // Insert target init call in the device compilation pass. On the host
9391 // (e.g. a non-GPU offload target), there is no runtime init/deinit
9392 // sequence, but the runtime still needs a '<kernel>_kernel_environment'
9393 // global to know how the kernel was configured, so emit it directly.
9394 if (OMPBuilder.Config.isTargetDevice())
9395 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9396 else
9397 OMPBuilder.emitKernelEnvironment(Builder, DefaultAttrs);
9398
9399 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9400
9401 // As we embed the user code in the middle of our target region after we
9402 // generate entry code, we must move what allocas we can into the entry
9403 // block to avoid possible breaking optimisations for device
9404 if (OMPBuilder.Config.isTargetDevice())
9406
9407 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9408 BasicBlock *OutlinedBodyBB =
9409 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9411 CBFunc(Builder.saveIP(), OutlinedBodyBB->begin(), ExitBB);
9412 if (!AfterIP)
9413 return AfterIP.takeError();
9414 Builder.SetInsertPoint(ExitBB);
9415 // The body callback builds the body with its own IRBuilder and cannot reach
9416 // this one directly. But a body holding another OpenMP construct, a nested
9417 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9418 // this Builder pointing at the wrong debug location, or at none at all. The
9419 // epilogue below belongs to the target construct rather than to whatever the
9420 // body emitted last, so re-establish the location the prologue was emitted
9421 // with.
9422 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9423
9424 // Insert target deinit call in the device compilation pass.
9425 if (OMPBuilder.Config.isTargetDevice())
9426 OMPBuilder.createTargetDeinit(Builder);
9427
9428 // Insert return instruction.
9429 Builder.CreateRetVoid();
9430
9431 // New Alloca IP at entry point of created device function.
9432 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9433 auto AllocaIP = Builder.saveIP();
9434
9435 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9436
9437 // Do not include the artificial dyn_ptr argument.
9438 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9439
9441
9442 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9443 // Things like GEP's can come in the form of Constants. Constants and
9444 // ConstantExpr's do not have access to the knowledge of what they're
9445 // contained in, so we must dig a little to find an instruction so we
9446 // can tell if they're used inside of the function we're outlining. We
9447 // also replace the original constant expression with a new instruction
9448 // equivalent; an instruction as it allows easy modification in the
9449 // following loop, as we can now know the constant (instruction) is
9450 // owned by our target function and replaceUsesOfWith can now be invoked
9451 // on it (cannot do this with constants it seems). A brand new one also
9452 // allows us to be cautious as it is perhaps possible the old expression
9453 // was used inside of the function but exists and is used externally
9454 // (unlikely by the nature of a Constant, but still).
9455 // NOTE: We cannot remove dead constants that have been rewritten to
9456 // instructions at this stage, we run the risk of breaking later lowering
9457 // by doing so as we could still be in the process of lowering the module
9458 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9459 // constants we have created rewritten versions of.
9460 if (auto *Const = dyn_cast<Constant>(Input))
9461 convertUsersOfConstantsToInstructions(Const, Func, false);
9462
9463 // Collect users before iterating over them to avoid invalidating the
9464 // iteration in case a user uses Input more than once (e.g. a call
9465 // instruction).
9466 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9467 // Collect all the instructions
9469 if (auto *Instr = dyn_cast<Instruction>(User))
9470 if (Instr->getFunction() == Func)
9471 Instr->replaceUsesOfWith(Input, InputCopy);
9472 };
9473
9474 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9475
9476 // Rewrite uses of input valus to parameters.
9477 for (auto InArg : zip(Inputs, ArgRange)) {
9478 Value *Input = std::get<0>(InArg);
9479 Argument &Arg = std::get<1>(InArg);
9480 Value *InputCopy = nullptr;
9481
9482 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9483 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(), ExitBB->begin());
9484 if (!AfterIP)
9485 return AfterIP.takeError();
9486 Builder.restoreIP(*AfterIP);
9487 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9488
9489 // In certain cases a Global may be set up for replacement, however, this
9490 // Global may be used in multiple arguments to the kernel, just segmented
9491 // apart, for example, if we have a global array, that is sectioned into
9492 // multiple mappings (technically not legal in OpenMP, but there is a case
9493 // in Fortran for Common Blocks where this is neccesary), we will end up
9494 // with GEP's into this array inside the kernel, that refer to the Global
9495 // but are technically separate arguments to the kernel for all intents and
9496 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9497 // index, it will fold into an referal to the Global, if we then encounter
9498 // this folded GEP during replacement all of the references to the
9499 // Global in the kernel will be replaced with the argument we have generated
9500 // that corresponds to it, including any other GEP's that refer to the
9501 // Global that may be other arguments. This will invalidate all of the other
9502 // preceding mapped arguments that refer to the same global that may be
9503 // separate segments. To prevent this, we defer global processing until all
9504 // other processing has been performed.
9507 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9508 continue;
9509 }
9510
9512 continue;
9513
9514 ReplaceValue(Input, InputCopy, Func);
9515 }
9516
9517 // Replace all of our deferred Input values, currently just Globals.
9518 for (auto Deferred : DeferredReplacement)
9519 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9520
9521 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9522 ValueReplacementMap);
9523 return Func;
9524}
9525/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9526/// of pointers containing shared data between the parent task and the created
9527/// task.
9529 IRBuilderBase &Builder,
9530 Value *TaskWithPrivates,
9531 Type *TaskWithPrivatesTy) {
9532
9533 Type *TaskTy = OMPIRBuilder.Task;
9534 LLVMContext &Ctx = Builder.getContext();
9535 Value *TaskT =
9536 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9537 Value *Shareds = TaskT;
9538 // TaskWithPrivatesTy can be one of the following
9539 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9540 // %struct.privates }
9541 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9542 //
9543 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9544 // its first member has to be the task descriptor. TaskTy is the type of the
9545 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9546 // first member of TaskT, gives us the pointer to shared data.
9547 if (TaskWithPrivatesTy != TaskTy)
9548 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9549 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9550}
9551/// Create an entry point for a target task with the following.
9552/// It'll have the following signature
9553/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9554/// This function is called from emitTargetTask once the
9555/// code to launch the target kernel has been outlined already.
9556/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9557/// into the task structure so that the deferred target task can access this
9558/// data even after the stack frame of the generating task has been rolled
9559/// back. Offloading arrays contain base pointers, pointers, sizes etc
9560/// of the data that the target kernel will access. These in effect are the
9561/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9563 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9564 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9565 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9566
9567 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9568 // This is because PrivatesTy is the type of the structure in which
9569 // we pass the offloading arrays to the deferred target task.
9570 assert((!NumOffloadingArrays || PrivatesTy) &&
9571 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9572 "to privatize");
9573
9574 Module &M = OMPBuilder.M;
9575 // KernelLaunchFunction is the target launch function, i.e.
9576 // the function that sets up kernel arguments and calls
9577 // __tgt_target_kernel to launch the kernel on the device.
9578 //
9579 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9580
9581 // StaleCI is the CallInst which is the call to the outlined
9582 // target kernel launch function. If there are local live-in values
9583 // that the outlined function uses then these are aggregated into a structure
9584 // which is passed as the second argument. If there are no local live-in
9585 // values or if all values used by the outlined kernel are global variables,
9586 // then there's only one argument, the threadID. So, StaleCI can be
9587 //
9588 // %structArg = alloca { ptr, ptr }, align 8
9589 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9590 // store ptr %20, ptr %gep_, align 8
9591 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9592 // store ptr %21, ptr %gep_8, align 8
9593 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9594 //
9595 // OR
9596 //
9597 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9598 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9599
9600 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9601 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9602 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9603
9604 auto ProxyFnTy =
9605 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9606 /* isVarArg */ false);
9607 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9608 ".omp_target_task_proxy_func", M);
9609 Value *ThreadId = ProxyFn->getArg(0);
9610 Value *TaskWithPrivates = ProxyFn->getArg(1);
9611 ThreadId->setName("thread.id");
9612 TaskWithPrivates->setName("task");
9613
9614 bool HasShareds = SharedArgsOperandNo > 0;
9615 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9616 IRBuilder<>::InsertPointGuard IPG(Builder);
9617 BasicBlock *EntryBB =
9618 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9619 Builder.SetInsertPoint(EntryBB);
9620 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9621
9622 SmallVector<Value *> KernelLaunchArgs;
9623 KernelLaunchArgs.reserve(StaleCI->arg_size());
9624 KernelLaunchArgs.push_back(ThreadId);
9625
9626 if (HasOffloadingArrays) {
9627 assert(TaskTy != TaskWithPrivatesTy &&
9628 "If there are offloading arrays to pass to the target"
9629 "TaskTy cannot be the same as TaskWithPrivatesTy");
9630 (void)TaskTy;
9631 Value *Privates =
9632 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9633 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9634 KernelLaunchArgs.push_back(
9635 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9636 }
9637
9638 if (HasShareds) {
9639 auto *ArgStructAlloca =
9640 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9641 assert(ArgStructAlloca &&
9642 "Unable to find the alloca instruction corresponding to arguments "
9643 "for extracted function");
9644 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9645 std::optional<TypeSize> ArgAllocSize =
9646 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9647 assert(ArgStructType && ArgAllocSize &&
9648 "Unable to determine size of arguments for extracted function");
9649 uint64_t StructSize = ArgAllocSize->getFixedValue();
9650
9651 AllocaInst *NewArgStructAlloca =
9652 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9653
9654 Value *SharedsSize = Builder.getInt64(StructSize);
9655
9657 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9658
9659 Builder.CreateMemCpy(
9660 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9661 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9662 KernelLaunchArgs.push_back(NewArgStructAlloca);
9663 }
9664 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9665 Builder.CreateRetVoid();
9666 return ProxyFn;
9667}
9669
9670 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9671 return GEP->getSourceElementType();
9672 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9673 return Alloca->getAllocatedType();
9674
9675 llvm_unreachable("Unhandled Instruction type");
9676 return nullptr;
9677}
9678// This function returns a struct that has at most two members.
9679// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9680// descriptor. The second member, if needed, is a struct containing arrays
9681// that need to be passed to the offloaded target kernel. For example,
9682// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9683// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9684// respectively, then the types created by this function are
9685//
9686// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9687// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9688// %struct.privates }
9689// %struct.task_with_privates is returned by this function.
9690// If there aren't any offloading arrays to pass to the target kernel,
9691// %struct.kmp_task_ompbuilder_t is returned.
9692static StructType *
9694 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9695
9696 if (OffloadingArraysToPrivatize.empty())
9697 return OMPIRBuilder.Task;
9698
9699 SmallVector<Type *, 4> StructFieldTypes;
9700 for (Value *V : OffloadingArraysToPrivatize) {
9701 assert(V->getType()->isPointerTy() &&
9702 "Expected pointer to array to privatize. Got a non-pointer value "
9703 "instead");
9704 Type *ArrayTy = getOffloadingArrayType(V);
9705 assert(ArrayTy && "ArrayType cannot be nullptr");
9706 StructFieldTypes.push_back(ArrayTy);
9707 }
9708 StructType *PrivatesStructTy =
9709 StructType::create(StructFieldTypes, "struct.privates");
9710 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9711 "struct.task_with_privates");
9712}
9714 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9715 TargetRegionEntryInfo &EntryInfo,
9717 Function *&OutlinedFn, Constant *&OutlinedFnID,
9721 DebugLoc OutlinedFnLoc) {
9722
9723 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9724 [&](StringRef EntryFnName) {
9725 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9726 EntryFnName, Inputs, CBFunc,
9727 ArgAccessorFuncCB, OutlinedFnLoc);
9728 };
9729
9730 return OMPBuilder.emitTargetRegionFunction(
9731 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9732 OutlinedFnID);
9733}
9734
9736 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9738 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9739 bool HasNoWait) {
9740
9741 // The following explains the code-gen scenario for the `target` directive. A
9742 // similar scneario is followed for other device-related directives (e.g.
9743 // `target enter data`) but in similar fashion since we only need to emit task
9744 // that encapsulates the proper runtime call.
9745 //
9746 // When we arrive at this function, the target region itself has been
9747 // outlined into the function OutlinedFn.
9748 // So at ths point, for
9749 // --------------------------------------------------------------
9750 // void user_code_that_offloads(...) {
9751 // omp target depend(..) map(from:a) map(to:b) private(i)
9752 // do i = 1, 10
9753 // a(i) = b(i) + n
9754 // }
9755 //
9756 // --------------------------------------------------------------
9757 //
9758 // we have
9759 //
9760 // --------------------------------------------------------------
9761 //
9762 // void user_code_that_offloads(...) {
9763 // %.offload_baseptrs = alloca [2 x ptr], align 8
9764 // %.offload_ptrs = alloca [2 x ptr], align 8
9765 // %.offload_mappers = alloca [2 x ptr], align 8
9766 // ;; target region has been outlined and now we need to
9767 // ;; offload to it via a target task.
9768 // }
9769 // void outlined_device_function(ptr a, ptr b, ptr n) {
9770 // n = *n_ptr;
9771 // do i = 1, 10
9772 // a(i) = b(i) + n
9773 // }
9774 //
9775 // We have to now do the following
9776 // (i) Make an offloading call to outlined_device_function using the OpenMP
9777 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9778 // emitted by emitKernelLaunch
9779 // (ii) Create a task entry point function that calls kernel_launch_function
9780 // and is the entry point for the target task. See
9781 // '@.omp_target_task_proxy_func in the pseudocode below.
9782 // (iii) Create a task with the task entry point created in (ii)
9783 //
9784 // That is we create the following
9785 // struct task_with_privates {
9786 // struct kmp_task_ompbuilder_t task_struct;
9787 // struct privates {
9788 // [2 x ptr] ; baseptrs
9789 // [2 x ptr] ; ptrs
9790 // [2 x i64] ; sizes
9791 // }
9792 // }
9793 // void user_code_that_offloads(...) {
9794 // %.offload_baseptrs = alloca [2 x ptr], align 8
9795 // %.offload_ptrs = alloca [2 x ptr], align 8
9796 // %.offload_sizes = alloca [2 x i64], align 8
9797 //
9798 // %structArg = alloca { ptr, ptr, ptr }, align 8
9799 // %strucArg[0] = a
9800 // %strucArg[1] = b
9801 // %strucArg[2] = &n
9802 //
9803 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9804 // sizeof(kmp_task_ompbuilder_t),
9805 // sizeof(structArg),
9806 // @.omp_target_task_proxy_func,
9807 // ...)
9808 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9809 // sizeof(structArg))
9810 // memcpy(target_task_with_privates->privates->baseptrs,
9811 // offload_baseptrs, sizeof(offload_baseptrs)
9812 // memcpy(target_task_with_privates->privates->ptrs,
9813 // offload_ptrs, sizeof(offload_ptrs)
9814 // memcpy(target_task_with_privates->privates->sizes,
9815 // offload_sizes, sizeof(offload_sizes)
9816 // dependencies_array = ...
9817 // ;; if nowait not present
9818 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9819 // call @__kmpc_omp_task_begin_if0(...)
9820 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9821 // %target_task_with_privates)
9822 // call @__kmpc_omp_task_complete_if0(...)
9823 // }
9824 //
9825 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9826 // ptr %task) {
9827 // %structArg = alloca {ptr, ptr, ptr}
9828 // %task_ptr = getelementptr(%task, 0, 0)
9829 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9830 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9831 //
9832 // %offloading_arrays = getelementptr(%task, 0, 1)
9833 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9834 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9835 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9836 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9837 // %offload_sizes, %structArg)
9838 // }
9839 //
9840 // We need the proxy function because the signature of the task entry point
9841 // expected by kmpc_omp_task is always the same and will be different from
9842 // that of the kernel_launch function.
9843 //
9844 // kernel_launch_function is generated by emitKernelLaunch and has the
9845 // always_inline attribute. For this example, it'll look like so:
9846 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9847 // %offload_sizes, %structArg) alwaysinline {
9848 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9849 // ; load aggregated data from %structArg
9850 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9851 // ; offload_sizes
9852 // call i32 @__tgt_target_kernel(...,
9853 // outlined_device_function,
9854 // ptr %kernel_args)
9855 // }
9856 // void outlined_device_function(ptr a, ptr b, ptr n) {
9857 // n = *n_ptr;
9858 // do i = 1, 10
9859 // a(i) = b(i) + n
9860 // }
9861 //
9862 BasicBlock *TargetTaskBodyBB =
9863 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9864 BasicBlock *TargetTaskAllocaBB =
9865 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9866
9867 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB->begin());
9868 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB->begin());
9869
9870 auto OI = std::make_unique<OutlineInfo>();
9871 OI->EntryBB = TargetTaskAllocaBB;
9872 OI->OuterAllocBB = AllocaIP.getNodeParent();
9873
9874 // Add the thread ID argument.
9876 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9877 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9878
9879 // Generate the task body which will subsequently be outlined.
9880 Builder.restoreIP(TargetTaskBodyIP);
9881 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9882 return Err;
9883
9884 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9885 // it is given. These blocks are enumerated by
9886 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9887 // to be outside the region. In other words, OI.ExitBlock is expected to be
9888 // the start of the region after the outlining. We used to set OI.ExitBlock
9889 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9890 // except when the task body is a single basic block. In that case,
9891 // OI.ExitBlock is set to the single task body block and will get left out of
9892 // the outlining process. So, simply create a new empty block to which we
9893 // uncoditionally branch from where TaskBodyCB left off
9894 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9895 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9896 /*IsFinished=*/true);
9897
9898 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9899 bool NeedsTargetTask = HasNoWait && DeviceID;
9900 if (NeedsTargetTask) {
9901 for (auto *V :
9902 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9903 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9904 RTArgs.SizesArray}) {
9906 OffloadingArraysToPrivatize.push_back(V);
9907 OI->ExcludeArgsFromAggregate.push_back(V);
9908 }
9909 }
9910 }
9911 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9912 DeviceID, OffloadingArraysToPrivatize](
9913 Function &OutlinedFn) mutable {
9914 assert(OutlinedFn.hasOneUse() &&
9915 "there must be a single user for the outlined function");
9916
9917 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9918
9919 // The first argument of StaleCI is always the thread id.
9920 // The next few arguments are the pointers to offloading arrays
9921 // if any. (see OffloadingArraysToPrivatize)
9922 // Finally, all other local values that are live-in into the outlined region
9923 // end up in a structure whose pointer is passed as the last argument. This
9924 // piece of data is passed in the "shared" field of the task structure. So,
9925 // we know we have to pass shareds to the task if the number of arguments is
9926 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9927 // thread id. Further, for safety, we assert that the number of arguments of
9928 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9929 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9930 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9931 assert((!HasShareds ||
9932 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9933 "Wrong number of arguments for StaleCI when shareds are present");
9934 int SharedArgOperandNo =
9935 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9936
9937 StructType *TaskWithPrivatesTy =
9938 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9939 StructType *PrivatesTy = nullptr;
9940
9941 if (!OffloadingArraysToPrivatize.empty())
9942 PrivatesTy =
9943 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9944
9946 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9947 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9948
9949 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9950 << "\n");
9951
9952 Builder.SetInsertPoint(StaleCI);
9953
9954 // Gather the arguments for emitting the runtime call.
9955 uint32_t SrcLocStrSize;
9956 Constant *SrcLocStr =
9958 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9959
9960 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9961 //
9962 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9963 // the DeviceID to the deferred task and also since
9964 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9965 Function *TaskAllocFn =
9966 !NeedsTargetTask
9967 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9969 OMPRTL___kmpc_omp_target_task_alloc);
9970
9971 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9972 // call.
9973 Value *ThreadID = getOrCreateThreadID(Ident);
9974
9975 // Argument - `sizeof_kmp_task_t` (TaskSize)
9976 // Tasksize refers to the size in bytes of kmp_task_t data structure
9977 // plus any other data to be passed to the target task, if any, which
9978 // is packed into a struct. kmp_task_t and the struct so created are
9979 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9980 Value *TaskSize = Builder.getInt64(
9981 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9982
9983 // Argument - `sizeof_shareds` (SharedsSize)
9984 // SharedsSize refers to the shareds array size in the kmp_task_t data
9985 // structure.
9986 Value *SharedsSize = Builder.getInt64(0);
9987 if (HasShareds) {
9988 auto *ArgStructAlloca =
9989 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9990 assert(ArgStructAlloca &&
9991 "Unable to find the alloca instruction corresponding to arguments "
9992 "for extracted function");
9993 std::optional<TypeSize> ArgAllocSize =
9994 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9995 assert(ArgAllocSize &&
9996 "Unable to determine size of arguments for extracted function");
9997 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9998 }
9999
10000 // Argument - `flags`
10001 // Task is tied iff (Flags & 1) == 1.
10002 // Task is untied iff (Flags & 1) == 0.
10003 // Task is final iff (Flags & 2) == 2.
10004 // Task is not final iff (Flags & 2) == 0.
10005 // A target task is not final and is untied.
10006 Value *Flags = Builder.getInt32(0);
10007
10008 // Emit the @__kmpc_omp_task_alloc runtime call
10009 // The runtime call returns a pointer to an area where the task captured
10010 // variables must be copied before the task is run (TaskData)
10011 CallInst *TaskData = nullptr;
10012
10013 SmallVector<llvm::Value *> TaskAllocArgs = {
10014 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
10015 /*flags=*/Flags,
10016 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
10017 /*task_func=*/ProxyFn};
10018
10019 if (NeedsTargetTask) {
10020 assert(DeviceID && "Expected non-empty device ID.");
10021 TaskAllocArgs.push_back(DeviceID);
10022 }
10023
10024 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
10025
10026 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
10027 if (HasShareds) {
10028 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
10030 *this, Builder, TaskData, TaskWithPrivatesTy);
10031 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
10032 SharedsSize);
10033 }
10034 if (!OffloadingArraysToPrivatize.empty()) {
10035 Value *Privates =
10036 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
10037 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
10038 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
10039 [[maybe_unused]] Type *ArrayType =
10040 getOffloadingArrayType(PtrToPrivatize);
10041 assert(ArrayType && "ArrayType cannot be nullptr");
10042
10043 Type *ElementType = PrivatesTy->getElementType(i);
10044 assert(ElementType == ArrayType &&
10045 "ElementType should match ArrayType");
10046 (void)ArrayType;
10047
10048 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
10049 Builder.CreateMemCpy(
10050 Dst, Alignment, PtrToPrivatize, Alignment,
10051 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
10052 }
10053 }
10054
10055 Value *DepArray = nullptr;
10056 Value *NumDeps = nullptr;
10057 if (Dependencies.DepArray) {
10058 DepArray = Dependencies.DepArray;
10059 NumDeps = Dependencies.NumDeps;
10060 } else if (!Dependencies.Deps.empty()) {
10061 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
10062 NumDeps = Builder.getInt32(Dependencies.Deps.size());
10063 }
10064
10065 // ---------------------------------------------------------------
10066 // V5.2 13.8 target construct
10067 // If the nowait clause is present, execution of the target task
10068 // may be deferred. If the nowait clause is not present, the target task is
10069 // an included task.
10070 // ---------------------------------------------------------------
10071 // The above means that the lack of a nowait on the target construct
10072 // translates to '#pragma omp task if(0)'
10073 if (!NeedsTargetTask) {
10074 if (DepArray) {
10075 Function *TaskWaitFn =
10076 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10078 TaskWaitFn,
10079 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10080 /*ndeps=*/NumDeps,
10081 /*dep_list=*/DepArray,
10082 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10083 /*noalias_dep_list=*/
10085 }
10086 // Included task.
10087 Function *TaskBeginFn =
10088 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10089 Function *TaskCompleteFn =
10090 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10091 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10092 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10093 CI->setDebugLoc(StaleCI->getDebugLoc());
10094 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10095 } else if (DepArray) {
10096 // HasNoWait - meaning the task may be deferred. Call
10097 // __kmpc_omp_task_with_deps if there are dependencies,
10098 // else call __kmpc_omp_task
10099 Function *TaskFn =
10100 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10102 TaskFn,
10103 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10104 ConstantInt::get(Builder.getInt32Ty(), 0),
10106 } else {
10107 // Emit the @__kmpc_omp_task runtime call to spawn the task
10108 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10109 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10110 }
10111
10112 Builder.ClearInsertionPoint();
10113 StaleCI->eraseFromParent();
10114 for (Instruction *I : llvm::reverse(ToBeDeleted))
10115 I->eraseFromParent();
10116 };
10117 addOutlineInfo(std::move(OI));
10118
10119 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10120 << *(Builder.GetInsertBlock()) << "\n");
10121 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10122 << *(Builder.getModule()) << "\n");
10123 return Builder.saveIP();
10124}
10125
10127 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10128 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10129 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10130 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10131 if (Error Err =
10132 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10133 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10134 return Err;
10135 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10136 return Error::success();
10137}
10138
10139static void emitTargetCall(
10140 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride,
10145 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10149 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10150 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10151 // Generate a function call to the host fallback implementation of the target
10152 // region. This is called by the host when no offload entry was generated for
10153 // the target region and when the offloading call fails at runtime.
10154 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10156 Builder.restoreIP(IP);
10157 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10158 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10159 FallbackArgs.push_back(
10160 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10161 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10162 return Builder.saveIP();
10163 };
10164
10165 bool HasDependencies = !Dependencies.empty();
10166 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10167
10169
10170 auto TaskBodyCB =
10171 [&](Value *DeviceID, Value *RTLoc,
10172 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10173 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10174 // produce any.
10176 // emitKernelLaunch makes the necessary runtime call to offload the
10177 // kernel. We then outline all that code into a separate function
10178 // ('kernel_launch_function' in the pseudo code above). This function is
10179 // then called by the target task proxy function (see
10180 // '@.omp_target_task_proxy_func' in the pseudo code above)
10181 // "@.omp_target_task_proxy_func' is generated by
10182 // emitTargetTaskProxyFunction.
10183 if (OutlinedFnID && DeviceID)
10184 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10185 EmitTargetCallFallbackCB, KArgs,
10186 DeviceID, RTLoc, TargetTaskAllocaIP);
10187
10188 // We only need to do the outlining if `DeviceID` is set to avoid calling
10189 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10190 // generating the `else` branch of an `if` clause.
10191 //
10192 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10193 // In this case, we execute the host implementation directly.
10194 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10195 }());
10196
10197 OMPBuilder.Builder.restoreIP(AfterIP);
10198 return Error::success();
10199 };
10200
10201 auto &&EmitTargetCallElse =
10202 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10204 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10205 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10206 // produce any.
10208 if (RequiresOuterTargetTask) {
10209 // Arguments that are intended to be directly forwarded to an
10210 // emitKernelLaunch call are pased as nullptr, since
10211 // OutlinedFnID=nullptr results in that call not being done.
10213 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10214 /*RTLoc=*/nullptr, AllocaIP,
10215 Dependencies, EmptyRTArgs, HasNoWait);
10216 }
10217 return EmitTargetCallFallbackCB(Builder.saveIP());
10218 }());
10219
10220 Builder.restoreIP(AfterIP);
10221 return Error::success();
10222 };
10223
10224 auto &&EmitTargetCallThen =
10225 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10227 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10228 Info.HasNoWait = HasNoWait;
10229 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10230
10232 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10233 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10234 /*IsNonContiguous=*/true,
10235 /*ForEndCall=*/false))
10236 return Err;
10237
10238 SmallVector<Value *, 3> NumTeamsC;
10239 for (auto [DefaultVal, RuntimeVal] :
10240 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10241 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10242 : Builder.getInt32(DefaultVal));
10243
10244 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10245 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10246 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10247 if (Clause)
10248 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10249 /*isSigned=*/false);
10250 return Clause;
10251 };
10252 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10253 if (Clause)
10254 Result =
10255 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10256 Result, Clause)
10257 : Clause;
10258 };
10259
10260 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10261 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10262 SmallVector<Value *, 3> NumThreadsC;
10263 Value *MaxThreadsClause =
10264 RuntimeAttrs.TeamsThreadLimit.size() == 1
10265 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10266 : nullptr;
10267
10268 for (auto [TeamsVal, TargetVal] : zip_equal(
10269 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10270 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10271 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10272
10273 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10274 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10275
10276 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10277 }
10278
10279 unsigned NumTargetItems = Info.NumberOfPtrs;
10280 Value *RTLoc = RTLocOverride;
10281 if (!RTLoc) {
10282 uint32_t SrcLocStrSize;
10283 Constant *SrcLocStr =
10284 OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10285 RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10286 llvm::omp::IdentFlag(0), 0);
10287 }
10288
10289 Value *TripCount = RuntimeAttrs.LoopTripCount
10290 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10291 Builder.getInt64Ty(),
10292 /*isSigned=*/false)
10293 : Builder.getInt64(0);
10294
10295 // Request zero groupprivate bytes by default.
10296 if (!DynCGroupMem)
10297 DynCGroupMem = Builder.getInt32(0);
10298
10300 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10301 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10302 DynCGroupMemFallback);
10303
10304 // Assume no error was returned because TaskBodyCB and
10305 // EmitTargetCallFallbackCB don't produce any.
10307 // The presence of certain clauses on the target directive require the
10308 // explicit generation of the target task.
10309 if (RequiresOuterTargetTask)
10310 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10311 RTLoc, AllocaIP, Dependencies,
10312 KArgs.RTArgs, Info.HasNoWait);
10313
10314 return OMPBuilder.emitKernelLaunch(
10315 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10316 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10317 }());
10318
10319 Builder.restoreIP(AfterIP);
10320 return Error::success();
10321 };
10322
10323 // If we don't have an ID for the target region, it means an offload entry
10324 // wasn't created. In this case we just run the host fallback directly and
10325 // ignore any potential 'if' clauses.
10326 if (!OutlinedFnID) {
10327 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10328 return;
10329 }
10330
10331 // If there's no 'if' clause, only generate the kernel launch code path.
10332 if (!IfCond) {
10333 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10334 return;
10335 }
10336
10337 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10338 EmitTargetCallElse, AllocaIP));
10339}
10340
10342 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10343 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10344 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10345 const TargetKernelDefaultAttrs &DefaultAttrs,
10346 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10347 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10350 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10351 bool HasNowait, Value *DynCGroupMem,
10352 OMPDynGroupprivateFallbackType DynCGroupMemFallback, DebugLoc OutlinedFnLoc,
10353 Value *RTLocOverride) {
10354
10355 if (!updateToLocation(Loc))
10356 return InsertPointTy();
10357
10358 Builder.restoreIP(CodeGenIP);
10359
10360 Function *OutlinedFn;
10361 Constant *OutlinedFnID = nullptr;
10362 // The target region is outlined into its own function. The LLVM IR for
10363 // the target region itself is generated using the callbacks CBFunc
10364 // and ArgAccessorFuncCB
10366 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10367 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10368 return Err;
10369
10370 // If we are not on the target device, then we need to generate code
10371 // to make a remote call (offload) to the previously outlined function
10372 // that represents the target region. Do that now.
10373 if (!Config.isTargetDevice())
10374 emitTargetCall(*this, Builder, RTLocOverride, AllocaIP, DeallocBlocks, Info,
10375 DefaultAttrs, RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID,
10376 Inputs, GenMapInfoCB, CustomMapperCB, Dependencies,
10377 HasNowait, DynCGroupMem, DynCGroupMemFallback);
10378 return Builder.saveIP();
10379}
10380
10381std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10382 StringRef FirstSeparator,
10383 StringRef Separator) {
10384 SmallString<128> Buffer;
10385 llvm::raw_svector_ostream OS(Buffer);
10386 StringRef Sep = FirstSeparator;
10387 for (StringRef Part : Parts) {
10388 OS << Sep << Part;
10389 Sep = Separator;
10390 }
10391 return OS.str().str();
10392}
10393
10394std::string
10396 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10397 Config.separator());
10398}
10399
10401 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10402 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10403 if (Elem.second) {
10404 assert(Elem.second->getValueType() == Ty &&
10405 "OMP internal variable has different type than requested");
10406 } else {
10407 // TODO: investigate the appropriate linkage type used for the global
10408 // variable for possibly changing that to internal or private, or maybe
10409 // create different versions of the function for different OMP internal
10410 // variables.
10411 const DataLayout &DL = M.getDataLayout();
10412 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10413 // default global AS is 1.
10414 // See double-target-call-with-declare-target.f90 and
10415 // declare-target-vars-in-target-region.f90 libomptarget
10416 // tests.
10417 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10418 : M.getTargetTriple().isAMDGPU()
10419 ? 0
10420 : DL.getDefaultGlobalsAddressSpace();
10421 auto Linkage = this->M.getTargetTriple().isWasm()
10424 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10425 Constant::getNullValue(Ty), Elem.first(),
10426 /*InsertBefore=*/nullptr,
10427 GlobalValue::NotThreadLocal, AddressSpaceVal);
10428 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10429 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10430 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10431 Elem.second = GV;
10432 }
10433
10434 return Elem.second;
10435}
10436
10437Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10438 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10439 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10440 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10441}
10442
10444 LLVMContext &Ctx = Builder.getContext();
10445 Value *Null =
10446 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10447 Value *SizeGep =
10448 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10449 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10450 return SizePtrToInt;
10451}
10452
10455 std::string VarName) {
10456 llvm::Constant *MaptypesArrayInit =
10457 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10458 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10459 M, MaptypesArrayInit->getType(),
10460 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10461 VarName);
10462 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10463 return MaptypesArrayGlobal;
10464}
10465
10467 InsertPointTy AllocaIP,
10468 unsigned NumOperands,
10469 struct MapperAllocas &MapperAllocas) {
10470 if (!updateToLocation(Loc))
10471 return;
10472
10473 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10474 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10475 Builder.restoreIP(AllocaIP);
10476 AllocaInst *ArgsBase = Builder.CreateAlloca(
10477 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10478 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10479 ".offload_ptrs");
10480 AllocaInst *ArgSizes = Builder.CreateAlloca(
10481 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10483 MapperAllocas.ArgsBase = ArgsBase;
10484 MapperAllocas.Args = Args;
10485 MapperAllocas.ArgSizes = ArgSizes;
10486}
10487
10489 Function *MapperFunc, Value *SrcLocInfo,
10490 Value *MaptypesArg, Value *MapnamesArg,
10492 int64_t DeviceID, unsigned NumOperands) {
10493 if (!updateToLocation(Loc))
10494 return;
10495
10496 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10497 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10498 Value *ArgsBaseGEP =
10499 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10500 {Builder.getInt32(0), Builder.getInt32(0)});
10501 Value *ArgsGEP =
10502 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10503 {Builder.getInt32(0), Builder.getInt32(0)});
10504 Value *ArgSizesGEP =
10505 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10506 {Builder.getInt32(0), Builder.getInt32(0)});
10507 Value *NullPtr =
10508 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10509 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10510 Builder.getInt32(NumOperands),
10511 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10512 MaptypesArg, MapnamesArg, NullPtr});
10513}
10514
10516 TargetDataRTArgs &RTArgs,
10517 TargetDataInfo &Info,
10518 bool ForEndCall) {
10519 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10520 "expected region end call to runtime only when end call is separate");
10521 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10522 auto VoidPtrTy = UnqualPtrTy;
10523 auto VoidPtrPtrTy = UnqualPtrTy;
10524 auto Int64Ty = Type::getInt64Ty(M.getContext());
10525 auto Int64PtrTy = UnqualPtrTy;
10526
10527 if (!Info.NumberOfPtrs) {
10528 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10529 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10530 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10531 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10532 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10533 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10534 return;
10535 }
10536
10537 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10538 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10539 Info.RTArgs.BasePointersArray,
10540 /*Idx0=*/0, /*Idx1=*/0);
10541 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10542 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10543 /*Idx0=*/0,
10544 /*Idx1=*/0);
10545 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10546 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10547 /*Idx0=*/0, /*Idx1=*/0);
10548 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10549 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10550 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10551 : Info.RTArgs.MapTypesArray,
10552 /*Idx0=*/0,
10553 /*Idx1=*/0);
10554
10555 // Only emit the mapper information arrays if debug information is
10556 // requested.
10557 if (!Info.EmitDebug)
10558 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10559 else
10560 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10561 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10562 /*Idx0=*/0,
10563 /*Idx1=*/0);
10564 // If there is no user-defined mapper, set the mapper array to nullptr to
10565 // avoid an unnecessary data privatization
10566 if (!Info.HasMapper)
10567 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10568 else
10569 RTArgs.MappersArray =
10570 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10571}
10572
10574 InsertPointTy CodeGenIP,
10575 MapInfosTy &CombinedInfo,
10576 TargetDataInfo &Info) {
10578 CombinedInfo.NonContigInfo;
10579
10580 // Build an array of struct descriptor_dim and then assign it to
10581 // offload_args.
10582 //
10583 // struct descriptor_dim {
10584 // uint64_t offset;
10585 // uint64_t count;
10586 // uint64_t stride
10587 // };
10588 Type *Int64Ty = Builder.getInt64Ty();
10590 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10591 "struct.descriptor_dim");
10592
10593 enum { OffsetFD = 0, CountFD, StrideFD };
10594 // We need two index variable here since the size of "Dims" is the same as
10595 // the size of Components, however, the size of offset, count, and stride is
10596 // equal to the size of base declaration that is non-contiguous.
10597 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10598 // Skip emitting ir if dimension size is 1 since it cannot be
10599 // non-contiguous.
10600 if (NonContigInfo.Dims[I] == 1)
10601 continue;
10602 Builder.restoreIP(AllocaIP);
10603 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10604 AllocaInst *DimsAddr =
10605 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10606 Builder.restoreIP(CodeGenIP);
10607 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10608 unsigned RevIdx = EE - II - 1;
10609 Value *DimsLVal = Builder.CreateInBoundsGEP(
10610 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10611 // Offset
10612 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10613 Builder.CreateAlignedStore(
10614 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10615 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10616 // Count
10617 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10618 Builder.CreateAlignedStore(
10619 NonContigInfo.Counts[L][RevIdx], CountLVal,
10620 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10621 // Stride
10622 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10623 Builder.CreateAlignedStore(
10624 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10625 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10626 }
10627 // args[I] = &dims
10628 Builder.restoreIP(CodeGenIP);
10629 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10630 DimsAddr, Builder.getPtrTy());
10631 Value *P = Builder.CreateConstInBoundsGEP2_32(
10632 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10633 Info.RTArgs.PointersArray, 0, I);
10634 Builder.CreateAlignedStore(
10635 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10636 ++L;
10637 }
10638}
10639
10640void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10641 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10642 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10643 BasicBlock *ExitBB, bool IsInit) {
10644 StringRef Prefix = IsInit ? ".init" : ".del";
10645
10646 // Evaluate if this is an array section.
10648 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10649 Value *IsArray =
10650 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10651 Value *DeleteBit = Builder.CreateAnd(
10652 MapType,
10653 Builder.getInt64(
10654 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10655 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10656 Value *DeleteCond;
10657 Value *Cond;
10658 if (IsInit) {
10659 // base != begin?
10660 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10661 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10662 DeleteCond = Builder.CreateIsNull(
10663 DeleteBit,
10664 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10665 } else {
10666 Cond = IsArray;
10667 DeleteCond = Builder.CreateIsNotNull(
10668 DeleteBit,
10669 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10670 }
10671 Cond = Builder.CreateAnd(Cond, DeleteCond);
10672 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10673
10674 emitBlock(BodyBB, MapperFn);
10675 // Get the array size by multiplying element size and element number (i.e., \p
10676 // Size).
10677 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10678 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10679 // memory allocation/deletion purpose only.
10680 Value *MapTypeArg = Builder.CreateAnd(
10681 MapType,
10682 Builder.getInt64(
10683 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10684 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10685 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10686 MapTypeArg = Builder.CreateOr(
10687 MapTypeArg,
10688 Builder.getInt64(
10689 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10690 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10691
10692 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10693 // data structure.
10694 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10695 ArraySize, MapTypeArg, MapName};
10697 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10698 OffloadingArgs);
10699}
10700
10703 llvm::Value *BeginArg)>
10704 GenMapInfoCB,
10705 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10706 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10707 SmallVector<Type *> Params;
10708 Params.emplace_back(Builder.getPtrTy());
10709 Params.emplace_back(Builder.getPtrTy());
10710 Params.emplace_back(Builder.getPtrTy());
10711 Params.emplace_back(Builder.getInt64Ty());
10712 Params.emplace_back(Builder.getInt64Ty());
10713 Params.emplace_back(Builder.getPtrTy());
10714
10715 auto *FnTy =
10716 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10717
10718 SmallString<64> TyStr;
10719 raw_svector_ostream Out(TyStr);
10720 Function *MapperFn =
10722 MapperFn->addFnAttr(Attribute::NoInline);
10723 MapperFn->addFnAttr(Attribute::NoUnwind);
10724 MapperFn->addParamAttr(0, Attribute::NoUndef);
10725 MapperFn->addParamAttr(1, Attribute::NoUndef);
10726 MapperFn->addParamAttr(2, Attribute::NoUndef);
10727 MapperFn->addParamAttr(3, Attribute::NoUndef);
10728 MapperFn->addParamAttr(4, Attribute::NoUndef);
10729 MapperFn->addParamAttr(5, Attribute::NoUndef);
10730
10731 // Start the mapper function code generation.
10732 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10734 Builder.SetInsertPoint(EntryBB);
10735 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10736
10737 Value *MapperHandle = MapperFn->getArg(0);
10738 Value *BaseIn = MapperFn->getArg(1);
10739 Value *BeginIn = MapperFn->getArg(2);
10740 Value *Size = MapperFn->getArg(3);
10741 Value *MapType = MapperFn->getArg(4);
10742 Value *MapName = MapperFn->getArg(5);
10743
10744 // Compute the starting and end addresses of array elements.
10745 // Prepare common arguments for array initiation and deletion.
10746 // Convert the size in bytes into the number of array elements.
10747 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10748 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10749 Value *PtrBegin = BeginIn;
10750 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10751
10752 // Emit array initiation if this is an array section and \p MapType indicates
10753 // that memory allocation is required.
10754 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10755 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10756 MapType, MapName, ElementSize, HeadBB,
10757 /*IsInit=*/true);
10758
10759 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10760
10761 // Emit the loop header block.
10762 emitBlock(HeadBB, MapperFn);
10763 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10764 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10765 // Evaluate whether the initial condition is satisfied.
10766 Value *IsEmpty =
10767 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10768 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10769
10770 // Emit the loop body block.
10771 emitBlock(BodyBB, MapperFn);
10772 BasicBlock *LastBB = BodyBB;
10773 PHINode *PtrPHI =
10774 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10775 PtrPHI->addIncoming(PtrBegin, HeadBB);
10776
10777 // Get map clause information. Fill up the arrays with all mapped variables.
10778 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10779 if (!Info)
10780 return Info.takeError();
10781
10782 // Call the runtime API __tgt_mapper_num_components to get the number of
10783 // pre-existing components.
10784 Value *OffloadingArgs[] = {MapperHandle};
10785 Value *PreviousSize = createRuntimeFunctionCall(
10786 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10787 OffloadingArgs);
10788 Value *ShiftedPreviousSize =
10789 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10790
10791 // Fill up the runtime mapper handle for all components.
10792 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10793 Value *CurBaseArg = Info->BasePointers[I];
10794 Value *CurBeginArg = Info->Pointers[I];
10795 Value *CurSizeArg = Info->Sizes[I];
10796 Value *CurNameArg = Info->Names.size()
10797 ? Info->Names[I]
10798 : Constant::getNullValue(Builder.getPtrTy());
10799
10800 Value *OriMapType = Builder.getInt64(
10801 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10802 Info->Types[I]));
10803 auto RawType =
10804 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10805 Info->Types[I]);
10806 constexpr uint64_t MemberOfMask =
10807 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10808 constexpr uint64_t AttachBit =
10809 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10810 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10811
10812 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10813 // current array element (N = __tgt_mapper_num_components() at loop body
10814 // start).
10815 //
10816 // Example 1:
10817 // struct S { int x; int *p; };
10818 //
10819 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10820 // use: S arr[2]; ... map(arr)
10821 // entries per element:
10822 //
10823 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10824 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10825 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10826 //
10827 // Example 2:
10828 // struct S1 { int x; int y; };
10829 // struct S2 { int z; S1 *s1p; };
10830 //
10831 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10832 // s2.s1p->y)
10833 // use: S2 arr[2]; ... map(arr)
10834 // entries per element:
10835 //
10836 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10837 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10838 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10839 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10840 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10841 //
10842 // x/y carry inner MEMBER_OF(2)
10843 // which is shifted by N to become MEMBER_OF(N+2).
10844 //
10845 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10846 // the combined ALLOC entry for the s1p->x..y block, and the individual
10847 // x/y entries that are MEMBER_OF that block, all describe storage
10848 // reached through the attach ptr arr[i].s1p.
10849 //
10850 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10851 // linking them to the parent struct:
10852 //
10853 // * (*) Entries with HasAttachPtr: they represent pointee data that
10854 // occupies a different storage block than the struct being mapped, so
10855 // they are not a member of it. They may still be MEMBER_OF an entry
10856 // within that pointee block, in which case those pre-existing bits are
10857 // shifted -- see (***).
10858 // * (**) ATTACH entries: they are not a member of anything — they just
10859 // link a ptr to its ptee.
10860 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10861 // its pre-shaped entries already carry their final MEMBER_OF bits.
10862 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10863 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10864 // it.
10865 //
10866 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10867 // s1p->x/y entries above), those bits are still shifted by N.
10868 Value *MemberMapType;
10869 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10870 Info->HasAttachPtr[I]) {
10871 if (RawType & MemberOfMask)
10872 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10873 else
10874 MemberMapType = OriMapType;
10875 } else {
10876 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10877 }
10878
10879 // Combine the map type inherited from user-defined mapper with that
10880 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10881 // bits of the \a MapType, which is the input argument of the mapper
10882 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10883 // bits of MemberMapType.
10884 // [OpenMP 5.0], 1.2.6. map-type decay.
10885 // | alloc | to | from | tofrom | release | delete
10886 // ----------------------------------------------------------
10887 // alloc | alloc | alloc | alloc | alloc | release | delete
10888 // to | alloc | to | alloc | to | release | delete
10889 // from | alloc | alloc | from | from | release | delete
10890 // tofrom | alloc | to | from | tofrom | release | delete
10891 Value *LeftToFrom = Builder.CreateAnd(
10892 MapType,
10893 Builder.getInt64(
10894 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10895 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10896 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10897 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10898 BasicBlock *AllocElseBB =
10899 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10900 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10901 BasicBlock *ToElseBB =
10902 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10903 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10904 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10905 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10906 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10907 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10908 emitBlock(AllocBB, MapperFn);
10909 Value *AllocMapType = Builder.CreateAnd(
10910 MemberMapType,
10911 Builder.getInt64(
10912 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10913 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10914 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10915 Builder.CreateBr(EndBB);
10916 emitBlock(AllocElseBB, MapperFn);
10917 Value *IsTo = Builder.CreateICmpEQ(
10918 LeftToFrom,
10919 Builder.getInt64(
10920 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10921 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10922 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10923 // In case of to, clear OMP_MAP_FROM.
10924 emitBlock(ToBB, MapperFn);
10925 Value *ToMapType = Builder.CreateAnd(
10926 MemberMapType,
10927 Builder.getInt64(
10928 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10929 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10930 Builder.CreateBr(EndBB);
10931 emitBlock(ToElseBB, MapperFn);
10932 Value *IsFrom = Builder.CreateICmpEQ(
10933 LeftToFrom,
10934 Builder.getInt64(
10935 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10936 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10937 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10938 // In case of from, clear OMP_MAP_TO.
10939 emitBlock(FromBB, MapperFn);
10940 Value *FromMapType = Builder.CreateAnd(
10941 MemberMapType,
10942 Builder.getInt64(
10943 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10944 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10945 // In case of tofrom, do nothing.
10946 emitBlock(EndBB, MapperFn);
10947 LastBB = EndBB;
10948 PHINode *CurMapType =
10949 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10950 CurMapType->addIncoming(AllocMapType, AllocBB);
10951 CurMapType->addIncoming(ToMapType, ToBB);
10952 CurMapType->addIncoming(FromMapType, FromBB);
10953 CurMapType->addIncoming(MemberMapType, ToElseBB);
10954
10955 // Propagate map-type-modifying bits from the outer map clause to each map
10956 // inserted by the mapper.
10957 //
10958 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10959 // list item from the map clause and to apply the clauses specified in the
10960 // declared mapper to the construct on which the map clause appears...
10961 // If any modifier with the map-type-modifying property appears in the map
10962 // clause then the effect is as if that modifier appears in each map clause
10963 // specified in the declared mapper.
10964 //
10965 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10966 //
10967 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10968 //
10969 // PRESENT is propagated only to entries that have an attach ptr
10970 // (HasAttachPtr): the pointee data, which occupies a different storage
10971 // block than the struct being mapped and so is not covered by the
10972 // present-check on the struct's own storage. A present modifier on the
10973 // outer clause must still require that pointee to be present on the device.
10974 //
10975 // This is gated on \p PropagatePresentToPointee (set by callers only for
10976 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10977 // applying to the pointee: the spec committee confirmed the divergence
10978 // between the present "motion" modifier (to/from) and the present map-type
10979 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10980 // so for 5.2 present is ignored for the pointee for both map and to/from.
10981 //
10982 // TODO: PRESENT should also be propagated to the struct's own members
10983 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10984 // member triggers the present-check. We cannot do that yet: while pointer
10985 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10986 // the whole struct (including the pointer's storage), so propagating
10987 // PRESENT to it would wrongly require the pointer's pointee to be present.
10988 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10989 // attach-style maps throughout.
10990 uint64_t ModifierBits =
10991 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10992 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10993 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10994 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10995 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10996 ModifierBits |=
10997 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10998 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10999 Value *ImportedModifierBits =
11000 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
11001 Value *CurMapTypeWithModifiers = Builder.CreateOr(
11002 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
11003
11004 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
11005 // reserved for the attach(always) map-type modifier, and other modifier
11006 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
11007 Value *FinalMapType =
11008 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
11009
11010 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
11011 CurSizeArg, FinalMapType, CurNameArg};
11012
11013 auto ChildMapperFn = CustomMapperCB(I);
11014 if (!ChildMapperFn)
11015 return ChildMapperFn.takeError();
11016 if (*ChildMapperFn) {
11017 // Call the corresponding mapper function.
11018 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
11019 ->setDoesNotThrow();
11020 } else {
11021 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
11022 // data structure.
11024 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
11025 OffloadingArgs);
11026 }
11027 }
11028
11029 // Update the pointer to point to the next element that needs to be mapped,
11030 // and check whether we have mapped all elements.
11031 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
11032 "omp.arraymap.next");
11033 PtrPHI->addIncoming(PtrNext, LastBB);
11034 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
11035 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
11036 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
11037
11038 emitBlock(ExitBB, MapperFn);
11039 // Emit array deletion if this is an array section and \p MapType indicates
11040 // that deletion is required.
11041 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
11042 MapType, MapName, ElementSize, DoneBB,
11043 /*IsInit=*/false);
11044
11045 // Emit the function exit block.
11046 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
11047
11048 Builder.CreateRetVoid();
11049 return MapperFn;
11050}
11051
11053 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
11054 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
11055 bool IsNonContiguous,
11056 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
11057
11058 // Reset the array information.
11059 Info.clearArrayInfo();
11060 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
11061
11062 if (Info.NumberOfPtrs == 0)
11063 return Error::success();
11064
11065 Builder.restoreIP(AllocaIP);
11066 // Detect if we have any capture size requiring runtime evaluation of the
11067 // size so that a constant array could be eventually used.
11068 ArrayType *PointerArrayType =
11069 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
11070
11071 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
11072 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
11073
11074 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11075 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11076 AllocaInst *MappersArray = Builder.CreateAlloca(
11077 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11078 Info.RTArgs.MappersArray = MappersArray;
11079
11080 // If we don't have any VLA types or other types that require runtime
11081 // evaluation, we can use a constant array for the map sizes, otherwise we
11082 // need to fill up the arrays as we do for the pointers.
11083 Type *Int64Ty = Builder.getInt64Ty();
11084 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11085 ConstantInt::get(Int64Ty, 0));
11086 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11087 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11088 bool IsNonContigEntry =
11089 IsNonContiguous &&
11090 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11091 CombinedInfo.Types[I] &
11092 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11093 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11094 // descriptor_dim records), not the byte size.
11095 if (IsNonContigEntry) {
11096 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11097 "Index must be in-bounds for NON_CONTIG Dims array");
11098 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11099 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11100 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11101 continue;
11102 }
11103 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11104 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11105 ConstSizes[I] = CI;
11106 continue;
11107 }
11108 }
11109 RuntimeSizes.set(I);
11110 }
11111
11112 if (RuntimeSizes.all()) {
11113 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11114 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11115 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11116 restoreIPandDebugLoc(Builder, CodeGenIP);
11117 } else {
11118 auto *SizesArrayInit = ConstantArray::get(
11119 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11120 std::string Name = createPlatformSpecificName({"offload_sizes"});
11121 auto *SizesArrayGbl =
11122 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11123 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11124 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11125
11126 if (!RuntimeSizes.any()) {
11127 Info.RTArgs.SizesArray = SizesArrayGbl;
11128 } else {
11129 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11130 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11131 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11132 AllocaInst *Buffer = Builder.CreateAlloca(
11133 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11134 Buffer->setAlignment(OffloadSizeAlign);
11135 restoreIPandDebugLoc(Builder, CodeGenIP);
11136 Builder.CreateMemCpy(
11137 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11138 SizesArrayGbl, OffloadSizeAlign,
11139 Builder.getIntN(
11140 IndexSize,
11141 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11142
11143 Info.RTArgs.SizesArray = Buffer;
11144 }
11145 restoreIPandDebugLoc(Builder, CodeGenIP);
11146 }
11147
11148 // The map types are always constant so we don't need to generate code to
11149 // fill arrays. Instead, we create an array constant.
11151 for (auto mapFlag : CombinedInfo.Types)
11152 Mapping.push_back(
11153 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11154 mapFlag));
11155 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11156 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11157 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11158
11159 // The information types are only built if provided.
11160 if (!CombinedInfo.Names.empty()) {
11161 auto *MapNamesArrayGbl = createOffloadMapnames(
11162 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11163 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11164 Info.EmitDebug = true;
11165 } else {
11166 Info.RTArgs.MapNamesArray =
11168 Info.EmitDebug = false;
11169 }
11170
11171 // If there's a present map type modifier, it must not be applied to the end
11172 // of a region, so generate a separate map type array in that case.
11173 if (Info.separateBeginEndCalls()) {
11174 bool EndMapTypesDiffer = false;
11175 for (uint64_t &Type : Mapping) {
11176 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11177 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11178 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11179 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11180 EndMapTypesDiffer = true;
11181 }
11182 }
11183 if (EndMapTypesDiffer) {
11184 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11185 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11186 }
11187 }
11188
11189 PointerType *PtrTy = Builder.getPtrTy();
11190 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11191 Value *BPVal = CombinedInfo.BasePointers[I];
11192 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11193 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11194 0, I);
11195 Builder.CreateAlignedStore(BPVal, BP,
11196 M.getDataLayout().getPrefTypeAlign(PtrTy));
11197
11198 if (Info.requiresDevicePointerInfo()) {
11199 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11200 CodeGenIP = Builder.saveIP();
11201 Builder.restoreIP(AllocaIP);
11202 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11203 restoreIPandDebugLoc(Builder, CodeGenIP);
11204 if (DeviceAddrCB)
11205 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11206 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11207 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11208 if (DeviceAddrCB)
11209 DeviceAddrCB(I, BP);
11210 }
11211 }
11212
11213 Value *PVal = CombinedInfo.Pointers[I];
11214 Value *P = Builder.CreateConstInBoundsGEP2_32(
11215 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11216 I);
11217 // TODO: Check alignment correct.
11218 Builder.CreateAlignedStore(PVal, P,
11219 M.getDataLayout().getPrefTypeAlign(PtrTy));
11220
11221 if (RuntimeSizes.test(I)) {
11222 Value *S = Builder.CreateConstInBoundsGEP2_32(
11223 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11224 /*Idx0=*/0,
11225 /*Idx1=*/I);
11226 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11227 Int64Ty,
11228 /*isSigned=*/true),
11229 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11230 }
11231 // Fill up the mapper array.
11232 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11233 Value *MFunc = ConstantPointerNull::get(PtrTy);
11234
11235 auto CustomMFunc = CustomMapperCB(I);
11236 if (!CustomMFunc)
11237 return CustomMFunc.takeError();
11238 if (*CustomMFunc)
11239 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11240
11241 Value *MAddr = Builder.CreateInBoundsGEP(
11242 PointerArrayType, MappersArray,
11243 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11244 Builder.CreateAlignedStore(
11245 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11246 }
11247
11248 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11249 Info.NumberOfPtrs == 0)
11250 return Error::success();
11251 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11252 return Error::success();
11253}
11254
11256 BasicBlock *CurBB = Builder.GetInsertBlock();
11257
11258 if (!CurBB || CurBB->hasTerminator()) {
11259 // If there is no insert point or the previous block is already
11260 // terminated, don't touch it.
11261 } else {
11262 // Otherwise, create a fall-through branch.
11263 Builder.CreateBr(Target);
11264 }
11265
11266 Builder.ClearInsertionPoint();
11267}
11268
11270 bool IsFinished) {
11271 BasicBlock *CurBB = Builder.GetInsertBlock();
11272
11273 // Fall out of the current block (if necessary).
11274 emitBranch(BB);
11275
11276 if (IsFinished && BB->use_empty()) {
11277 BB->eraseFromParent();
11278 return;
11279 }
11280
11281 // Place the block after the current block, if possible, or else at
11282 // the end of the function.
11283 if (CurBB && CurBB->getParent())
11284 CurFn->insert(std::next(CurBB->getIterator()), BB);
11285 else
11286 CurFn->insert(CurFn->end(), BB);
11287 Builder.SetInsertPoint(BB);
11288}
11289
11291 BodyGenCallbackTy ElseGen,
11292 InsertPointTy AllocaIP,
11293 ArrayRef<BasicBlock *> DeallocBlocks) {
11294 // If the condition constant folds and can be elided, try to avoid emitting
11295 // the condition and the dead arm of the if/else.
11296 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11297 auto CondConstant = CI->getSExtValue();
11298 if (CondConstant)
11299 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11300
11301 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11302 }
11303
11304 Function *CurFn = Builder.GetInsertBlock()->getParent();
11305
11306 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11307 // emit the conditional branch.
11308 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11309 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11310 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11311 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11312 // Emit the 'then' code.
11313 emitBlock(ThenBlock, CurFn);
11314 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11315 return Err;
11316 emitBranch(ContBlock);
11317 // Emit the 'else' code if present.
11318 // There is no need to emit line number for unconditional branch.
11319 emitBlock(ElseBlock, CurFn);
11320 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11321 return Err;
11322 // There is no need to emit line number for unconditional branch.
11323 emitBranch(ContBlock);
11324 // Emit the continuation block for code after the if.
11325 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11326 return Error::success();
11327}
11328
11329bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11330 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11333 "Unexpected Atomic Ordering.");
11334
11335 bool Flush = false;
11337
11338 switch (AK) {
11339 case Read:
11342 FlushAO = AtomicOrdering::Acquire;
11343 Flush = true;
11344 }
11345 break;
11346 case Write:
11347 case Compare:
11348 case Update:
11351 FlushAO = AtomicOrdering::Release;
11352 Flush = true;
11353 }
11354 break;
11355 case Capture:
11356 switch (AO) {
11358 FlushAO = AtomicOrdering::Acquire;
11359 Flush = true;
11360 break;
11362 FlushAO = AtomicOrdering::Release;
11363 Flush = true;
11364 break;
11368 Flush = true;
11369 break;
11370 default:
11371 // do nothing - leave silently.
11372 break;
11373 }
11374 }
11375
11376 if (Flush) {
11377 // Currently Flush RT call still doesn't take memory_ordering, so for when
11378 // that happens, this tries to do the resolution of which atomic ordering
11379 // to use with but issue the flush call
11380 // TODO: pass `FlushAO` after memory ordering support is added
11381 (void)FlushAO;
11382 emitFlush(Loc);
11383 }
11384
11385 // for AO == AtomicOrdering::Monotonic and all other case combinations
11386 // do nothing
11387 return Flush;
11388}
11389
11393 AtomicOrdering AO, InsertPointTy AllocaIP) {
11394 if (!updateToLocation(Loc))
11395 return Loc.IP;
11396
11397 assert(X.Var->getType()->isPointerTy() &&
11398 "OMP Atomic expects a pointer to target memory");
11399 Type *XElemTy = X.ElemTy;
11400 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11401 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11402 "OMP atomic read expected a scalar type");
11403
11404 Value *XRead = nullptr;
11405
11406 if (XElemTy->isIntegerTy()) {
11407 LoadInst *XLD =
11408 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11409 XLD->setAtomic(AO);
11410 XRead = cast<Value>(XLD);
11411 } else if (XElemTy->isStructTy()) {
11412 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11413 // target does not support `atomicrmw` of the size of the struct
11414 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11415 OldVal->setAtomic(AO);
11416 const DataLayout &DL = OldVal->getDataLayout();
11417 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11418 OpenMPIRBuilder::AtomicInfo atomicInfo(
11419 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11420 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11421 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11422 XRead = AtomicLoadRes.first;
11423 OldVal->eraseFromParent();
11424 } else {
11425 // We need to perform atomic op as integer
11426 IntegerType *IntCastTy =
11427 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11428 LoadInst *XLoad =
11429 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11430 XLoad->setAtomic(AO);
11431 if (XElemTy->isFloatingPointTy()) {
11432 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11433 } else {
11434 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11435 }
11436 }
11437 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11438 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11439 return Builder.saveIP();
11440}
11441
11444 AtomicOpValue &X, Value *Expr,
11445 AtomicOrdering AO, InsertPointTy AllocaIP) {
11446 if (!updateToLocation(Loc))
11447 return Loc.IP;
11448
11449 assert(X.Var->getType()->isPointerTy() &&
11450 "OMP Atomic expects a pointer to target memory");
11451 Type *XElemTy = X.ElemTy;
11452 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11453 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11454 "OMP atomic write expected a scalar type");
11455
11456 if (XElemTy->isIntegerTy()) {
11457 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11458 XSt->setAtomic(AO);
11459 } else if (XElemTy->isStructTy()) {
11460 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11461 const DataLayout &DL = OldVal->getDataLayout();
11462 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11463 OpenMPIRBuilder::AtomicInfo atomicInfo(
11464 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11465 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11466 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11467 OldVal->eraseFromParent();
11468 } else {
11469 // We need to bitcast and perform atomic op as integers
11470 IntegerType *IntCastTy =
11471 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11472 Value *ExprCast =
11473 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11474 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11475 XSt->setAtomic(AO);
11476 }
11477
11478 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11479 return Builder.saveIP();
11480}
11481
11484 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11485 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11486 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11487 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11488 if (!updateToLocation(Loc))
11489 return Loc.IP;
11490
11491 LLVM_DEBUG({
11492 Type *XTy = X.Var->getType();
11493 assert(XTy->isPointerTy() &&
11494 "OMP Atomic expects a pointer to target memory");
11495 Type *XElemTy = X.ElemTy;
11496 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11497 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11498 "OMP atomic update expected a scalar or struct type");
11499 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11500 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11501 "OpenMP atomic does not support LT or GT operations");
11502 });
11503
11504 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11505 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11506 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11507 if (!AtomicResult)
11508 return AtomicResult.takeError();
11509 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11510 return Builder.saveIP();
11511}
11512
11513// FIXME: Duplicating AtomicExpand
11514Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11515 AtomicRMWInst::BinOp RMWOp) {
11516 switch (RMWOp) {
11517 case AtomicRMWInst::Add:
11518 return Builder.CreateAdd(Src1, Src2);
11519 case AtomicRMWInst::Sub:
11520 return Builder.CreateSub(Src1, Src2);
11521 case AtomicRMWInst::And:
11522 return Builder.CreateAnd(Src1, Src2);
11524 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11525 case AtomicRMWInst::Or:
11526 return Builder.CreateOr(Src1, Src2);
11527 case AtomicRMWInst::Xor:
11528 return Builder.CreateXor(Src1, Src2);
11533 case AtomicRMWInst::Max:
11534 case AtomicRMWInst::Min:
11547 llvm_unreachable("Unsupported atomic update operation");
11548 }
11549 llvm_unreachable("Unsupported atomic update operation");
11550}
11551
11553 // Loads cannot use Release or AcquireRelease ordering. This load is
11554 // just the initial value for the cmpxchg loop; the cmpxchg itself
11555 // retains the original ordering.
11556 AtomicOrdering LoadAO = AO;
11557
11558 if (AO == AtomicOrdering::Release) {
11560 } else if (AO == AtomicOrdering::AcquireRelease) {
11561 LoadAO = AtomicOrdering::Acquire;
11562 }
11563
11564 return LoadAO;
11565}
11566
11567Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11568 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11570 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11571 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11572 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11573 bool emitRMWOp = false;
11574 switch (RMWOp) {
11575 case AtomicRMWInst::Add:
11576 case AtomicRMWInst::And:
11578 case AtomicRMWInst::Or:
11579 case AtomicRMWInst::Xor:
11581 emitRMWOp = XElemTy;
11582 break;
11583 case AtomicRMWInst::Sub:
11584 emitRMWOp = (IsXBinopExpr && XElemTy);
11585 break;
11586 default:
11587 emitRMWOp = false;
11588 }
11589 emitRMWOp &= XElemTy->isIntegerTy();
11590
11591 std::pair<Value *, Value *> Res;
11592 if (emitRMWOp) {
11593 AtomicRMWInst *RMWInst =
11594 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11595 if (IsIgnoreDenormalMode)
11596 RMWInst->setMetadata(llvm::LLVMContext::MD_atomic_ignore_denormal_mode,
11597 llvm::MDNode::get(Builder.getContext(), {}));
11598 if (T.isAMDGPU()) {
11599 if (!IsFineGrainedMemory)
11600 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11601 llvm::MDNode::get(Builder.getContext(), {}));
11602 if (!IsRemoteMemory)
11603 RMWInst->setMetadata("amdgpu.no.remote.memory",
11604 llvm::MDNode::get(Builder.getContext(), {}));
11605 }
11606 Res.first = RMWInst;
11607 // not needed except in case of postfix captures. Generate anyway for
11608 // consistency with the else part. Will be removed with any DCE pass.
11609 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11610 if (RMWOp == AtomicRMWInst::Xchg)
11611 Res.second = Res.first;
11612 else
11613 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11614 } else if (XElemTy->isStructTy()) {
11615 LoadInst *OldVal =
11616 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11618 OldVal->setAtomic(LoadAO);
11619 const DataLayout &LoadDL = OldVal->getDataLayout();
11620 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11621
11622 OpenMPIRBuilder::AtomicInfo atomicInfo(
11623 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11624 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11625 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11626 BasicBlock *CurBB = Builder.GetInsertBlock();
11627 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11628 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11629 BasicBlock *ExitBB =
11630 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11631 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11632 X->getName() + ".atomic.cont");
11633 ContBB->getTerminator()->eraseFromParent();
11634 Builder.restoreIP(AllocaIP);
11635 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11636 NewAtomicAddr->setName(X->getName() + "x.new.val");
11637 Builder.SetInsertPoint(ContBB);
11638 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11639 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11640 Value *OldExprVal = PHI;
11641 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11642 if (!CBResult)
11643 return CBResult.takeError();
11644 Value *Upd = *CBResult;
11645 Builder.CreateStore(Upd, NewAtomicAddr);
11648 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11649 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11650 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11651 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11652 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11653 OldVal->eraseFromParent();
11654 Res.first = OldExprVal;
11655 Res.second = Upd;
11656
11657 if (UnreachableInst *ExitTI =
11659 CurBBTI->eraseFromParent();
11660 Builder.SetInsertPoint(ExitBB);
11661 } else {
11662 Builder.SetInsertPoint(ExitTI);
11663 }
11664 } else {
11665 IntegerType *IntCastTy =
11666 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11667 LoadInst *OldVal =
11668 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11670 OldVal->setAtomic(LoadAO);
11671 // CurBB
11672 // | /---\
11673 // ContBB |
11674 // | \---/
11675 // ExitBB
11676 BasicBlock *CurBB = Builder.GetInsertBlock();
11677 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11678 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11679 BasicBlock *ExitBB =
11680 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11681 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11682 X->getName() + ".atomic.cont");
11683 ContBB->getTerminator()->eraseFromParent();
11684 Builder.restoreIP(AllocaIP);
11685 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11686 NewAtomicAddr->setName(X->getName() + "x.new.val");
11687 Builder.SetInsertPoint(ContBB);
11688 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11689 PHI->addIncoming(OldVal, CurBB);
11690 bool IsIntTy = XElemTy->isIntegerTy();
11691 Value *OldExprVal = PHI;
11692 if (!IsIntTy) {
11693 if (XElemTy->isFloatingPointTy()) {
11694 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11695 X->getName() + ".atomic.fltCast");
11696 } else {
11697 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11698 X->getName() + ".atomic.ptrCast");
11699 }
11700 }
11701
11702 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11703 if (!CBResult)
11704 return CBResult.takeError();
11705 Value *Upd = *CBResult;
11706 Builder.CreateStore(Upd, NewAtomicAddr);
11707 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11710 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11711 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11712 Result->setVolatile(VolatileX);
11713 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11714 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11715 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11716 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11717
11718 Res.first = OldExprVal;
11719 Res.second = Upd;
11720
11721 // set Insertion point in exit block
11722 if (UnreachableInst *ExitTI =
11724 CurBBTI->eraseFromParent();
11725 Builder.SetInsertPoint(ExitBB);
11726 } else {
11727 Builder.SetInsertPoint(ExitTI);
11728 }
11729 }
11730
11731 return Res;
11732}
11733
11736 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11737 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11738 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11739 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11740 if (!updateToLocation(Loc))
11741 return Loc.IP;
11742
11743 LLVM_DEBUG({
11744 Type *XTy = X.Var->getType();
11745 assert(XTy->isPointerTy() &&
11746 "OMP Atomic expects a pointer to target memory");
11747 Type *XElemTy = X.ElemTy;
11748 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11749 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11750 "OMP atomic capture expected a scalar or struct type");
11751 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11752 "OpenMP atomic does not support LT or GT operations");
11753 });
11754
11755 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11756 // 'x' is simply atomically rewritten with 'expr'.
11757 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11758 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11759 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11760 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11761 if (!AtomicResult)
11762 return AtomicResult.takeError();
11763 Value *CapturedVal =
11764 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11765 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11766
11767 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11768 return Builder.saveIP();
11769}
11770
11774 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11775 bool IsFailOnly, bool IsWeak) {
11776
11778 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11779 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11780}
11781
11785 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11786 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11787
11788 if (!updateToLocation(Loc))
11789 return Loc.IP;
11790
11791 assert(X.Var->getType()->isPointerTy() &&
11792 "OMP atomic expects a pointer to target memory");
11793 // compare capture
11794 if (V.Var) {
11795 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11796 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11797 }
11798
11799 bool IsInteger = E->getType()->isIntegerTy();
11800
11801 if (Op == OMPAtomicCompareOp::EQ) {
11802 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11803 // R.Var handling.
11804 Value *OldValue = nullptr;
11805 Value *SuccessOrFail = nullptr;
11806
11807 if (!IsInteger && HandleFPNegZero) {
11808 // IEEE 754 special cases for cmpxchg (which is bitwise):
11809 // 1. -0.0 == +0.0 but they have different bit patterns.
11810 // 2. NaN != NaN but identical NaN bit patterns would match.
11811 //
11812 // CurBB:
11813 // %e_int = bitcast E to intN
11814 // %d_int = bitcast D to intN
11815 // %x_curr = load atomic intN, X
11816 // %x_fp = bitcast %x_curr to FP
11817 // %e_is_nan = fcmp uno E, E
11818 // %x_is_nan = fcmp uno %x_fp, %x_fp
11819 // %either_nan = or %e_is_nan, %x_is_nan
11820 // br %either_nan, NaNBB, NotNaNBB
11821 // NaNBB: ; NaN == anything is always false
11822 // br ExitBB
11823 // NotNaNBB:
11824 // %x_is_zero = fcmp oeq %x_fp, 0.0
11825 // %e_is_zero = fcmp oeq E, 0.0
11826 // %both_zero = and %x_is_zero, %e_is_zero
11827 // br %both_zero, ZeroBB, NormalBB
11828 // ZeroBB: ; both ±0.0 → x = d
11829 // cmpxchg X, %x_curr, %d_int
11830 // br ExitBB
11831 // NormalBB: ; original path
11832 // cmpxchg X, %e_int, %d_int
11833 // br ExitBB
11834 // ExitBB:
11835 // phi merge
11836 IntegerType *IntCastTy =
11837 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11838 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11839 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11840
11841 // Load X atomically.
11842 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11843 X.Var->getName() + ".atomic.load");
11845 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11846
11847 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11848 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11849 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11850 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11851 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11852
11853 BasicBlock *CurBB = Builder.GetInsertBlock();
11854 Function *F = CurBB->getParent();
11855 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11856 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11857 BasicBlock *ExitBB =
11858 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11860 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11861 BasicBlock *NotNaNBB = BasicBlock::Create(
11862 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11864 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11865 BasicBlock *NormalBB = BasicBlock::Create(
11866 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11867
11868 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11869 CurBB->getTerminator()->eraseFromParent();
11870 Builder.SetInsertPoint(CurBB);
11871 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11872
11873 // NaNBB: NaN == anything is always false; skip cmpxchg.
11874 Builder.SetInsertPoint(NaNBB);
11875 Builder.CreateBr(ExitBB);
11876
11877 // NotNaNBB: check both X and E for ±0.0.
11878 Builder.SetInsertPoint(NotNaNBB);
11879 Value *XIsZero =
11880 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11881 X.Var->getName() + ".atomic.xiszero");
11882 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11883 "atomic.e.iszero");
11884 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11885 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11886
11887 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11888 Builder.SetInsertPoint(ZeroBB);
11889 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11890 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11891 ResZero->setWeak(IsWeak);
11892 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11893 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11894 Builder.CreateBr(ExitBB);
11895
11896 // NormalBB: original bitwise cmpxchg.
11897 Builder.SetInsertPoint(NormalBB);
11898 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11899 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11900 ResNormal->setWeak(IsWeak);
11901 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11902 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11903 Builder.CreateBr(ExitBB);
11904
11905 // ExitBB: merge results from NaN, Zero, and Normal paths.
11906 Builder.SetInsertPoint(ExitBB->begin());
11907 PHINode *OldIntPHI =
11908 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11909 OldIntPHI->addIncoming(XCurr, NaNBB);
11910 OldIntPHI->addIncoming(OldZero, ZeroBB);
11911 OldIntPHI->addIncoming(OldNormal, NormalBB);
11912 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11913 X.Var->getName() + ".atomic.ok");
11914 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11915 SuccessPHI->addIncoming(OkZero, ZeroBB);
11916 SuccessPHI->addIncoming(OkNormal, NormalBB);
11917
11918 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11919 CurBBTI->eraseFromParent();
11920 Builder.SetInsertPoint(ExitBB);
11921 } else {
11922 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11923 }
11924
11925 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11926 X.Var->getName() + ".atomic.old.fp");
11927 SuccessOrFail = SuccessPHI;
11928 } else {
11929 AtomicCmpXchgInst *Result = nullptr;
11930 if (!IsInteger) {
11931 IntegerType *IntCastTy =
11932 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11933 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11934 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11935 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11936 MaybeAlign(), AO, Failure);
11937 } else {
11938 Result =
11939 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11940 }
11941 Result->setWeak(IsWeak);
11942
11943 if (V.Var) {
11944 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11945 if (!IsInteger)
11946 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11947 assert(OldValue->getType() == V.ElemTy &&
11948 "OldValue and V must be of same type");
11949 if (IsPostfixUpdate) {
11950 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11951 } else {
11952 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11953 if (IsFailOnly) {
11954 BasicBlock *CurBB = Builder.GetInsertBlock();
11955 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11956 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11957 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11958 CurBBTI, X.Var->getName() + ".atomic.exit");
11959 BasicBlock *ContBB = CurBB->splitBasicBlock(
11960 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11961 ContBB->getTerminator()->eraseFromParent();
11962 CurBB->getTerminator()->eraseFromParent();
11963
11964 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11965
11966 Builder.SetInsertPoint(ContBB);
11967 Builder.CreateStore(OldValue, V.Var);
11968 Builder.CreateBr(ExitBB);
11969
11970 if (UnreachableInst *ExitTI =
11972 CurBBTI->eraseFromParent();
11973 Builder.SetInsertPoint(ExitBB);
11974 } else {
11975 Builder.SetInsertPoint(ExitTI);
11976 }
11977 } else {
11978 Value *CapturedValue =
11979 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11980 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11981 }
11982 }
11983 }
11984 // The comparison result has to be stored.
11985 if (R.Var) {
11986 assert(R.Var->getType()->isPointerTy() &&
11987 "r.var must be of pointer type");
11988 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11989
11990 Value *SuccessFailureVal =
11991 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11992 Value *ResultCast =
11993 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11994 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11995 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11996 }
11997 }
11998
11999 // For the HandleFPNegZero path, handle V.Var and R.Var using the
12000 // pre-computed OldValue and SuccessOrFail.
12001 if (HandleFPNegZero && !IsInteger) {
12002 if (V.Var) {
12003 assert(OldValue->getType() == V.ElemTy &&
12004 "OldValue and V must be of same type");
12005 if (IsPostfixUpdate) {
12006 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
12007 } else {
12008 if (IsFailOnly) {
12009 BasicBlock *CurBB = Builder.GetInsertBlock();
12010 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
12011 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
12012 BasicBlock *ExitBB = CurBB->splitBasicBlock(
12013 CurBBTI, X.Var->getName() + ".atomic.exit");
12014 BasicBlock *ContBB = CurBB->splitBasicBlock(
12015 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
12016 ContBB->getTerminator()->eraseFromParent();
12017 CurBB->getTerminator()->eraseFromParent();
12018
12019 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
12020
12021 Builder.SetInsertPoint(ContBB);
12022 Builder.CreateStore(OldValue, V.Var);
12023 Builder.CreateBr(ExitBB);
12024
12025 if (UnreachableInst *ExitTI =
12027 CurBBTI->eraseFromParent();
12028 Builder.SetInsertPoint(ExitBB);
12029 } else {
12030 Builder.SetInsertPoint(ExitTI);
12031 }
12032 } else {
12033 Value *CapturedValue =
12034 Builder.CreateSelect(SuccessOrFail, E, OldValue);
12035 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12036 }
12037 }
12038 }
12039 // The comparison result has to be stored.
12040 if (R.Var) {
12041 assert(R.Var->getType()->isPointerTy() &&
12042 "r.var must be of pointer type");
12043 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12044
12045 Value *ResultCast = R.IsSigned
12046 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
12047 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
12048 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12049 }
12050 }
12051 } else {
12052 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
12053 "Op should be either max or min at this point");
12054 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
12055
12056 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
12057 // Let's take max as example.
12058 // OpenMP form:
12059 // x = x > expr ? expr : x;
12060 // LLVM form:
12061 // *ptr = *ptr > val ? *ptr : val;
12062 // We need to transform to LLVM form.
12063 // x = x <= expr ? x : expr;
12065 if (IsXBinopExpr) {
12066 if (IsInteger) {
12067 if (X.IsSigned)
12068 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
12070 else
12071 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
12073 } else {
12074 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12076 }
12077 } else {
12078 if (IsInteger) {
12079 if (X.IsSigned)
12080 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12082 else
12083 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12085 } else {
12086 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12088 }
12089 }
12090
12091 AtomicRMWInst *OldValue =
12092 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12093 if (V.Var) {
12094 Value *CapturedValue = nullptr;
12095 if (IsPostfixUpdate) {
12096 CapturedValue = OldValue;
12097 } else {
12098 CmpInst::Predicate Pred;
12099 switch (NewOp) {
12100 case AtomicRMWInst::Max:
12101 Pred = CmpInst::ICMP_SGT;
12102 break;
12104 Pred = CmpInst::ICMP_UGT;
12105 break;
12107 Pred = CmpInst::FCMP_OGT;
12108 break;
12109 case AtomicRMWInst::Min:
12110 Pred = CmpInst::ICMP_SLT;
12111 break;
12113 Pred = CmpInst::ICMP_ULT;
12114 break;
12116 Pred = CmpInst::FCMP_OLT;
12117 break;
12118 default:
12119 llvm_unreachable("unexpected comparison op");
12120 }
12121 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12122 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12123 }
12124 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12125 }
12126 }
12127
12128 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12129
12130 return Builder.saveIP();
12131}
12132
12135 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12136 Value *NumTeamsUpper, Value *ThreadLimit,
12137 Value *IfExpr) {
12138 if (!updateToLocation(Loc))
12139 return InsertPointTy();
12140
12141 uint32_t SrcLocStrSize;
12142 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12143 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12144 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12145
12146 // Outer allocation basicblock is the entry block of the current function.
12147 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12148 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12149 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12150 Builder.SetInsertPoint(BodyBB->begin());
12151 }
12152
12153 // The current basic block is split into four basic blocks. After outlining,
12154 // they will be mapped as follows:
12155 // ```
12156 // def current_fn() {
12157 // current_basic_block:
12158 // br label %teams.exit
12159 // teams.exit:
12160 // ; instructions after teams
12161 // }
12162 //
12163 // def outlined_fn() {
12164 // teams.alloca:
12165 // br label %teams.body
12166 // teams.body:
12167 // ; instructions within teams body
12168 // }
12169 // ```
12170 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12171 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12172 BasicBlock *AllocaBB =
12173 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12174
12175 bool SubClausesPresent =
12176 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12177 // Push num_teams
12178 if (!Config.isTargetDevice() && SubClausesPresent) {
12179 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12180 "if lowerbound is non-null, then upperbound must also be non-null "
12181 "for bounds on num_teams");
12182
12183 if (NumTeamsUpper == nullptr)
12184 NumTeamsUpper = Builder.getInt32(0);
12185
12186 if (NumTeamsLower == nullptr)
12187 NumTeamsLower = NumTeamsUpper;
12188
12189 if (IfExpr) {
12190 assert(IfExpr->getType()->isIntegerTy() &&
12191 "argument to if clause must be an integer value");
12192
12193 // upper = ifexpr ? upper : 1
12194 if (IfExpr->getType() != Int1)
12195 IfExpr = Builder.CreateICmpNE(IfExpr,
12196 ConstantInt::get(IfExpr->getType(), 0));
12197 NumTeamsUpper = Builder.CreateSelect(
12198 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12199
12200 // lower = ifexpr ? lower : 1
12201 NumTeamsLower = Builder.CreateSelect(
12202 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12203 }
12204
12205 if (ThreadLimit == nullptr)
12206 ThreadLimit = Builder.getInt32(0);
12207
12208 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12209 // truncate or sign extend the passed values to match the int32 parameters.
12210 Value *NumTeamsLowerInt32 =
12211 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12212 Value *NumTeamsUpperInt32 =
12213 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12214 Value *ThreadLimitInt32 =
12215 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12216
12217 Value *ThreadNum = getOrCreateThreadID(Ident);
12218
12220 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12221 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12222 ThreadLimitInt32});
12223 }
12224 // Generate the body of teams.
12225 InsertPointTy AllocaIP(AllocaBB->begin());
12226 InsertPointTy CodeGenIP(BodyBB->begin());
12227 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12228 return Err;
12229
12230 auto OI = std::make_unique<OutlineInfo>();
12231 OI->EntryBB = AllocaBB;
12232 OI->ExitBB = ExitBB;
12233 OI->OuterAllocBB = &OuterAllocaBB;
12234
12235 // Insert fake values for global tid and bound tid.
12237 InsertPointTy OuterAllocaIP(OuterAllocaBB.begin());
12238 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12239 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12240 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12241 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12242
12243 auto HostPostOutlineCB = [this, Ident,
12244 ToBeDeleted](Function &OutlinedFn) mutable {
12245 // The stale call instruction will be replaced with a new call instruction
12246 // for runtime call with the outlined function.
12247
12248 assert(OutlinedFn.hasOneUse() &&
12249 "there must be a single user for the outlined function");
12250 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12251 ToBeDeleted.push_back(StaleCI);
12252
12253 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12254 "Outlined function must have two or three arguments only");
12255
12256 bool HasShared = OutlinedFn.arg_size() == 3;
12257
12258 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12259 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12260 if (HasShared)
12261 OutlinedFn.getArg(2)->setName("data");
12262
12263 // Call to the runtime function for teams in the current function.
12264 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12265 "outlined function.");
12266 Builder.SetInsertPoint(StaleCI);
12267 SmallVector<Value *> Args = {
12268 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12269 if (HasShared)
12270 Args.push_back(StaleCI->getArgOperand(2));
12273 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12274 Args);
12275
12276 Builder.ClearInsertionPoint();
12277 for (Instruction *I : llvm::reverse(ToBeDeleted))
12278 I->eraseFromParent();
12279 };
12280
12281 if (!Config.isTargetDevice())
12282 OI->PostOutlineCB = HostPostOutlineCB;
12283
12284 addOutlineInfo(std::move(OI));
12285
12286 Builder.SetInsertPoint(ExitBB);
12287
12288 return Builder.saveIP();
12289}
12290
12292 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12293 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12294 if (!updateToLocation(Loc))
12295 return InsertPointTy();
12296
12297 BasicBlock *OuterAllocaBB = OuterAllocIP.getNodeParent();
12298
12299 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12300 BasicBlock *BodyBB =
12301 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12302 Builder.SetInsertPoint(BodyBB->begin());
12303 }
12304 BasicBlock *ExitBB =
12305 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12306 BasicBlock *BodyBB =
12307 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12308 BasicBlock *AllocaBB =
12309 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12310
12311 // Generate the body of distribute clause
12312 InsertPointTy AllocaIP(AllocaBB->begin());
12313 InsertPointTy CodeGenIP(BodyBB->begin());
12314 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12315 return Err;
12316
12317 // When using target we use different runtime functions which require a
12318 // callback.
12319 if (Config.isTargetDevice()) {
12320 auto OI = std::make_unique<OutlineInfo>();
12321 OI->OuterAllocBB = OuterAllocIP.getNodeParent();
12322 OI->EntryBB = AllocaBB;
12323 OI->ExitBB = ExitBB;
12324 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12325 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12326
12327 addOutlineInfo(std::move(OI));
12328 }
12329 Builder.SetInsertPoint(ExitBB);
12330
12331 return Builder.saveIP();
12332}
12333
12336 std::string VarName) {
12337 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12339 Names.size()),
12340 Names);
12341 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12342 M, MapNamesArrayInit->getType(),
12343 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12344 VarName);
12345 return MapNamesArrayGlobal;
12346}
12347
12348// Create all simple and struct types exposed by the runtime and remember
12349// the llvm::PointerTypes of them for easy access later.
12350void OpenMPIRBuilder::initializeTypes(Module &M) {
12351 LLVMContext &Ctx = M.getContext();
12352 StructType *T;
12353 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12354 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12355#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12356#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12357 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12358 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12359#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12360 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12361 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12362#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12363 T = StructType::getTypeByName(Ctx, StructName); \
12364 if (!T) \
12365 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12366 VarName = T; \
12367 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12368#include "llvm/Frontend/OpenMP/OMPKinds.def"
12369}
12370
12373 SmallVectorImpl<BasicBlock *> &BlockVector) {
12375 BlockSet.insert(EntryBB);
12376 BlockSet.insert(ExitBB);
12377
12378 Worklist.push_back(EntryBB);
12379 while (!Worklist.empty()) {
12380 BasicBlock *BB = Worklist.pop_back_val();
12381 BlockVector.push_back(BB);
12382 for (BasicBlock *SuccBB : successors(BB))
12383 if (BlockSet.insert(SuccBB).second)
12384 Worklist.push_back(SuccBB);
12385 }
12386}
12387
12388std::unique_ptr<CodeExtractor>
12390 bool ArgsInZeroAddressSpace,
12391 Twine Suffix) {
12392 return std::make_unique<CodeExtractor>(
12393 Blocks, /* DominatorTree */ nullptr,
12394 /* AggregateArgs */ true,
12395 /* BlockFrequencyInfo */ nullptr,
12396 /* BranchProbabilityInfo */ nullptr,
12397 /* AssumptionCache */ nullptr,
12398 /* AllowVarArgs */ true,
12399 /* AllowAlloca */ true,
12400 /* AllocationBlock*/ OuterAllocBB,
12401 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12402 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12403}
12404
12405std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12406 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12407 return std::make_unique<DeviceSharedMemCodeExtractor>(
12408 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12409 /* AggregateArgs */ true,
12410 /* BlockFrequencyInfo */ nullptr,
12411 /* BranchProbabilityInfo */ nullptr,
12412 /* AssumptionCache */ nullptr,
12413 /* AllowVarArgs */ true,
12414 /* AllowAlloca */ true,
12415 /* AllocationBlock*/ OuterAllocBB,
12416 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12418 : OuterDeallocBBs,
12419 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12420}
12421
12423 uint64_t Size, int32_t Flags,
12425 StringRef Name) {
12426 if (!Config.isGPU()) {
12429 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12430 return;
12431 }
12432 // TODO: Add support for global variables on the device after declare target
12433 // support.
12434 Function *Fn = dyn_cast<Function>(Addr);
12435 if (!Fn)
12436 return;
12437
12438 // Add a function attribute for the kernel.
12439 Fn->addFnAttr("kernel");
12440 if (T.isAMDGCN())
12441 Fn->addFnAttr("uniform-work-group-size");
12442 Fn->addFnAttr(Attribute::MustProgress);
12443}
12444
12445// We only generate metadata for function that contain target regions.
12448
12449 // If there are no entries, we don't need to do anything.
12450 if (OffloadInfoManager.empty())
12451 return;
12452
12453 LLVMContext &C = M.getContext();
12456 16>
12457 OrderedEntries(OffloadInfoManager.size());
12458
12459 // Auxiliary methods to create metadata values and strings.
12460 auto &&GetMDInt = [this](unsigned V) {
12461 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12462 };
12463
12464 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12465
12466 // Create the offloading info metadata node.
12467 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12468 auto &&TargetRegionMetadataEmitter =
12469 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12470 const TargetRegionEntryInfo &EntryInfo,
12472 // Generate metadata for target regions. Each entry of this metadata
12473 // contains:
12474 // - Entry 0 -> Kind of this type of metadata (0).
12475 // - Entry 1 -> Device ID of the file where the entry was identified.
12476 // - Entry 2 -> File ID of the file where the entry was identified.
12477 // - Entry 3 -> Mangled name of the function where the entry was
12478 // identified.
12479 // - Entry 4 -> Line in the file where the entry was identified.
12480 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12481 // - Entry 6 -> Order the entry was created.
12482 // The first element of the metadata node is the kind.
12483 Metadata *Ops[] = {
12484 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12485 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12486 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12487 GetMDInt(E.getOrder())};
12488
12489 // Save this entry in the right position of the ordered entries array.
12490 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12491
12492 // Add metadata to the named metadata node.
12493 MD->addOperand(MDNode::get(C, Ops));
12494 };
12495
12496 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12497
12498 // Create function that emits metadata for each device global variable entry;
12499 auto &&DeviceGlobalVarMetadataEmitter =
12500 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12501 StringRef MangledName,
12503 // Generate metadata for global variables. Each entry of this metadata
12504 // contains:
12505 // - Entry 0 -> Kind of this type of metadata (1).
12506 // - Entry 1 -> Mangled name of the variable.
12507 // - Entry 2 -> Declare target kind.
12508 // - Entry 3 -> Order the entry was created.
12509 // The first element of the metadata node is the kind.
12510 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12511 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12512
12513 // Save this entry in the right position of the ordered entries array.
12514 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12515 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12516
12517 // Add metadata to the named metadata node.
12518 MD->addOperand(MDNode::get(C, Ops));
12519 };
12520
12521 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12522 DeviceGlobalVarMetadataEmitter);
12523
12524 for (const auto &E : OrderedEntries) {
12525 assert(E.first && "All ordered entries must exist!");
12526 if (const auto *CE =
12528 E.first)) {
12529 if (!CE->getID() || !CE->getAddress()) {
12530 // Do not blame the entry if the parent funtion is not emitted.
12531 TargetRegionEntryInfo EntryInfo = E.second;
12532 StringRef FnName = EntryInfo.ParentName;
12533 if (!M.getNamedValue(FnName))
12534 continue;
12535 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12536 continue;
12537 }
12538 createOffloadEntry(CE->getID(), CE->getAddress(),
12539 /*Size=*/0, CE->getFlags(),
12541 } else if (const auto *CE = dyn_cast<
12543 E.first)) {
12546 CE->getFlags());
12547 switch (Flags) {
12550 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12551 continue;
12552 if (!CE->getAddress()) {
12553 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12554 continue;
12555 }
12556 // The vaiable has no definition - no need to add the entry.
12557 if (CE->getVarSize() == 0)
12558 continue;
12559 break;
12561 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12562 (!Config.isTargetDevice() && CE->getAddress())) &&
12563 "Declaret target link address is set.");
12564 if (Config.isTargetDevice())
12565 continue;
12566 if (!CE->getAddress()) {
12568 continue;
12569 }
12570 break;
12573 if (!CE->getAddress()) {
12574 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12575 continue;
12576 }
12577 break;
12578 default:
12579 break;
12580 }
12581
12582 // Hidden or internal symbols on the device are not externally visible.
12583 // We should not attempt to register them by creating an offloading
12584 // entry. Indirect variables are handled separately on the device.
12585 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12586 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12587 (Flags !=
12589 Flags != OffloadEntriesInfoManager::
12590 OMPTargetGlobalVarEntryIndirectVTable))
12591 continue;
12592
12593 // Indirect globals need to use a special name that doesn't match the name
12594 // of the associated host global.
12596 Flags ==
12598 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12599 Flags, CE->getLinkage(), CE->getVarName());
12600 else
12601 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12602 Flags, CE->getLinkage());
12603
12604 } else {
12605 llvm_unreachable("Unsupported entry kind.");
12606 }
12607 }
12608
12609 // Emit requires directive globals to a special entry so the runtime can
12610 // register them when the device image is loaded.
12611 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12612 // entries should be redesigned to better suit this use-case.
12613 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12617 ".requires", /*Size=*/0,
12619 Config.getRequiresFlags());
12620}
12621
12624 unsigned FileID, unsigned Line, unsigned Count) {
12625 raw_svector_ostream OS(Name);
12626 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12627 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12628 if (Count)
12629 OS << "_" << Count;
12630}
12631
12633 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12634 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12636 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12637 EntryInfo.Line, NewCount);
12638}
12639
12642 vfs::FileSystem &VFS,
12643 StringRef ParentName) {
12644 sys::fs::UniqueID ID(0xdeadf17e, 0);
12645 auto FileIDInfo = CallBack();
12646 uint64_t FileID = 0;
12647 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12648 ID = Status->getUniqueID();
12649 FileID = Status->getUniqueID().getFile();
12650 } else {
12651 // If the inode ID could not be determined, create a hash value
12652 // the current file name and use that as an ID.
12653 FileID = hash_value(std::get<0>(FileIDInfo));
12654 }
12655
12656 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12657 std::get<1>(FileIDInfo));
12658}
12659
12661 unsigned Offset = 0;
12662 for (uint64_t Remain =
12663 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12665 !(Remain & 1); Remain = Remain >> 1)
12666 Offset++;
12667 return Offset;
12668}
12669
12672 // Rotate by getFlagMemberOffset() bits.
12673 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12674 << getFlagMemberOffset());
12675}
12676
12679 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12680 // If the entry is PTR_AND_OBJ but has not been marked with the special
12681 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12682 // marked as MEMBER_OF.
12683 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12685 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12688 return;
12689
12690 // Entries with ATTACH are not members-of anything. They are handled
12691 // separately by the runtime after other maps have been handled.
12692 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12694 return;
12695
12696 // Reset the placeholder value to prepare the flag for the assignment of the
12697 // proper MEMBER_OF value.
12698 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12699 Flags |= MemberOfFlag;
12700}
12701
12705 bool IsDeclaration, bool IsExternallyVisible,
12706 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12707 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12708 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12709 std::function<Constant *()> GlobalInitializer,
12710 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12711 // TODO: convert this to utilise the IRBuilder Config rather than
12712 // a passed down argument.
12713 if (OpenMPSIMD)
12714 return nullptr;
12715
12718 CaptureClause ==
12720 Config.hasRequiresUnifiedSharedMemory())) {
12721 SmallString<64> PtrName;
12722 {
12723 raw_svector_ostream OS(PtrName);
12724 OS << MangledName;
12725 if (!IsExternallyVisible)
12726 OS << format("_%x", EntryInfo.FileID);
12727 OS << "_decl_tgt_ref_ptr";
12728 }
12729
12730 Value *Ptr = M.getNamedValue(PtrName);
12731
12732 if (!Ptr) {
12733 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12734 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12735
12736 auto *GV = cast<GlobalVariable>(Ptr);
12737 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12738
12739 if (!Config.isTargetDevice()) {
12740 if (GlobalInitializer)
12741 GV->setInitializer(GlobalInitializer());
12742 else
12743 GV->setInitializer(GlobalValue);
12744 }
12745
12747 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12748 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12749 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12750 }
12751
12752 return cast<Constant>(Ptr);
12753 }
12754
12755 return nullptr;
12756}
12757
12761 bool IsDeclaration, bool IsExternallyVisible,
12762 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12763 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12764 std::vector<Triple> TargetTriple,
12765 std::function<Constant *()> GlobalInitializer,
12766 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12767 Constant *Addr) {
12769 (TargetTriple.empty() && !Config.isTargetDevice()))
12770 return;
12771
12773 StringRef VarName;
12774 int64_t VarSize;
12776
12778 CaptureClause ==
12780 !Config.hasRequiresUnifiedSharedMemory()) {
12782 VarName = MangledName;
12783 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12784
12785 if (!IsDeclaration)
12786 VarSize = divideCeil(
12787 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12788 else
12789 VarSize = 0;
12790 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12791
12792 // This is a workaround carried over from Clang which prevents undesired
12793 // optimisation of internal variables.
12794 if (Config.isTargetDevice() &&
12795 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12796 // Do not create a "ref-variable" if the original is not also available
12797 // on the host.
12798 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12799 return;
12800
12801 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12802
12803 if (!M.getNamedValue(RefName)) {
12804 Constant *AddrRef =
12805 getOrCreateInternalVariable(Addr->getType(), RefName);
12806 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12807 GvAddrRef->setConstant(true);
12808 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12809 GvAddrRef->setInitializer(Addr);
12810 GeneratedRefs.push_back(GvAddrRef);
12811 }
12812 }
12813 } else {
12816 else
12818
12819 if (Config.isTargetDevice()) {
12820 VarName = (Addr) ? Addr->getName() : "";
12821 Addr = nullptr;
12822 } else {
12824 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12825 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12826 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12827 VarName = (Addr) ? Addr->getName() : "";
12828 }
12829 VarSize = M.getDataLayout().getPointerSize();
12831 }
12832
12833 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12834 Flags, Linkage);
12835}
12836
12837/// Loads all the offload entries information from the host IR
12838/// metadata.
12840 // If we are in target mode, load the metadata from the host IR. This code has
12841 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12842
12843 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12844 if (!MD)
12845 return;
12846
12847 for (MDNode *MN : MD->operands()) {
12848 auto &&GetMDInt = [MN](unsigned Idx) {
12849 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12850 return cast<ConstantInt>(V->getValue())->getZExtValue();
12851 };
12852
12853 auto &&GetMDString = [MN](unsigned Idx) {
12854 auto *V = cast<MDString>(MN->getOperand(Idx));
12855 return V->getString();
12856 };
12857
12858 switch (GetMDInt(0)) {
12859 default:
12860 llvm_unreachable("Unexpected metadata!");
12861 break;
12862 case OffloadEntriesInfoManager::OffloadEntryInfo::
12863 OffloadingEntryInfoTargetRegion: {
12864 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12865 /*DeviceID=*/GetMDInt(1),
12866 /*FileID=*/GetMDInt(2),
12867 /*Line=*/GetMDInt(4),
12868 /*Count=*/GetMDInt(5));
12869 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12870 /*Order=*/GetMDInt(6));
12871 break;
12872 }
12873 case OffloadEntriesInfoManager::OffloadEntryInfo::
12874 OffloadingEntryInfoDeviceGlobalVar:
12875 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12876 /*MangledName=*/GetMDString(1),
12878 /*Flags=*/GetMDInt(2)),
12879 /*Order=*/GetMDInt(3));
12880 break;
12881 }
12882 }
12883}
12884
12886 StringRef HostFilePath) {
12887 if (HostFilePath.empty())
12888 return;
12889
12890 auto Buf = VFS.getBufferForFile(HostFilePath);
12891 if (std::error_code Err = Buf.getError()) {
12892 report_fatal_error(("error opening host file from host file path inside of "
12893 "OpenMPIRBuilder: " +
12894 Err.message())
12895 .c_str());
12896 }
12897
12898 LLVMContext Ctx;
12900 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12901 if (std::error_code Err = M.getError()) {
12903 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12904 .c_str());
12905 }
12906
12907 loadOffloadInfoMetadata(*M.get());
12908}
12909
12912 llvm::StringRef Name) {
12913 Builder.restoreIP(Loc.IP);
12914
12915 BasicBlock *CurBB = Builder.GetInsertBlock();
12916 assert(CurBB &&
12917 "expected a valid insertion block for creating an iterator loop");
12918 Function *F = CurBB->getParent();
12919
12920 InsertPointTy SplitIP = Builder.saveIP();
12921 if (SplitIP == CurBB->end())
12922 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12923 SplitIP = Terminator->getIterator();
12924
12925 BasicBlock *ContBB =
12926 splitBB(SplitIP, /*CreateBranch=*/false,
12927 Builder.getCurrentDebugLocation(), "omp.it.cont");
12928
12929 CanonicalLoopInfo *CLI =
12930 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12931 /*PreInsertBefore=*/ContBB,
12932 /*PostInsertBefore=*/ContBB, Name);
12933
12934 // Enter loop from original block.
12935 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12936
12937 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12938 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12939 T->eraseFromParent();
12940
12941 InsertPointTy BodyIP = CLI->getBodyIP();
12942 // Blocks that exist before BodyGen, other than the body and latch, are
12943 // outside the loop body.
12944 SmallPtrSet<BasicBlock *, 32> ExistingBlocks;
12945 for (BasicBlock &Block : *F)
12946 ExistingBlocks.insert(&Block);
12947 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12948 return Err;
12949
12950 // The body may span several blocks. Branch its single unterminated block to
12951 // the latch; otherwise some block must already branch there.
12952 BasicBlock *Latch = CLI->getLatch();
12953 BasicBlock *OpenBB = nullptr;
12954 bool ReachesLatch = false;
12955 SmallVector<BasicBlock *> Worklist{CLI->getBody()};
12956 SmallPtrSet<BasicBlock *, 8> Visited{CLI->getBody()};
12957 while (!Worklist.empty()) {
12958 BasicBlock *BB = Worklist.pop_back_val();
12959 if (!BB->hasTerminator()) {
12960 if (OpenBB)
12962 "iterator bodygen must leave at most one unterminated block",
12964 OpenBB = BB;
12965 continue;
12966 }
12967 for (BasicBlock *Succ : successors(BB)) {
12968 if (Succ == Latch) {
12969 ReachesLatch = true;
12970 continue;
12971 }
12972 if (Succ != CLI->getBody() && ExistingBlocks.contains(Succ))
12974 "iterator bodygen must not branch out of the loop body",
12976 if (Visited.insert(Succ).second)
12977 Worklist.push_back(Succ);
12978 }
12979 }
12980
12981 if (OpenBB) {
12982 Builder.SetInsertPoint(OpenBB);
12983 Builder.CreateBr(Latch);
12984 } else if (!ReachesLatch) {
12985 return make_error<StringError>("iterator bodygen must reach the loop latch",
12987 }
12988
12989 // Link After -> ContBB
12990 Builder.SetInsertPoint(CLI->getAfter()->begin());
12991 if (!CLI->getAfter()->hasTerminator())
12992 Builder.CreateBr(ContBB);
12993
12994 return ContBB->begin();
12995}
12996
12997/// Mangle the parameter part of the vector function name according to
12998/// their OpenMP classification. The mangling function is defined in
12999/// section 4.5 of the AAVFABI(2021Q1).
13000static std::string mangleVectorParameters(
13002 SmallString<256> Buffer;
13003 llvm::raw_svector_ostream Out(Buffer);
13004 for (const auto &ParamAttr : ParamAttrs) {
13005 switch (ParamAttr.Kind) {
13007 Out << 'l';
13008 break;
13010 Out << 'R';
13011 break;
13013 Out << 'U';
13014 break;
13016 Out << 'L';
13017 break;
13019 Out << 'u';
13020 break;
13022 Out << 'v';
13023 break;
13024 }
13025 if (ParamAttr.HasVarStride)
13026 Out << "s" << ParamAttr.StrideOrArg;
13027 else if (ParamAttr.Kind ==
13029 ParamAttr.Kind ==
13031 ParamAttr.Kind ==
13033 ParamAttr.Kind ==
13035 // Don't print the step value if it is not present or if it is
13036 // equal to 1.
13037 if (ParamAttr.StrideOrArg < 0)
13038 Out << 'n' << -ParamAttr.StrideOrArg;
13039 else if (ParamAttr.StrideOrArg != 1)
13040 Out << ParamAttr.StrideOrArg;
13041 }
13042
13043 if (!!ParamAttr.Alignment)
13044 Out << 'a' << ParamAttr.Alignment;
13045 }
13046
13047 return std::string(Out.str());
13048}
13049
13051 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
13053 struct ISADataTy {
13054 char ISA;
13055 unsigned VecRegSize;
13056 };
13057 ISADataTy ISAData[] = {
13058 {'b', 128}, // SSE
13059 {'c', 256}, // AVX
13060 {'d', 256}, // AVX2
13061 {'e', 512}, // AVX512
13062 };
13064 switch (Branch) {
13066 Masked.push_back('N');
13067 Masked.push_back('M');
13068 break;
13070 Masked.push_back('N');
13071 break;
13073 Masked.push_back('M');
13074 break;
13075 }
13076 for (char Mask : Masked) {
13077 for (const ISADataTy &Data : ISAData) {
13079 llvm::raw_svector_ostream Out(Buffer);
13080 Out << "_ZGV" << Data.ISA << Mask;
13081 if (!VLENVal) {
13082 assert(NumElts && "Non-zero simdlen/cdtsize expected");
13083 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
13084 } else {
13085 Out << VLENVal;
13086 }
13087 Out << mangleVectorParameters(ParamAttrs);
13088 Out << '_' << Fn->getName();
13089 Fn->addFnAttr(Out.str());
13090 }
13091 }
13092}
13093
13094// Function used to add the attribute. The parameter `VLEN` is templated to
13095// allow the use of `x` when targeting scalable functions for SVE.
13096template <typename T>
13097static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
13098 char ISA, StringRef ParSeq,
13099 StringRef MangledName, bool OutputBecomesInput,
13100 llvm::Function *Fn) {
13101 SmallString<256> Buffer;
13102 llvm::raw_svector_ostream Out(Buffer);
13103 Out << Prefix << ISA << LMask << VLEN;
13104 if (OutputBecomesInput)
13105 Out << 'v';
13106 Out << ParSeq << '_' << MangledName;
13107 Fn->addFnAttr(Out.str());
13108}
13109
13110// Helper function to generate the Advanced SIMD names depending on the value
13111// of the NDS when simdlen is not present.
13112static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13113 StringRef Prefix, char ISA,
13114 StringRef ParSeq, StringRef MangledName,
13115 bool OutputBecomesInput,
13116 llvm::Function *Fn) {
13117 switch (NDS) {
13118 case 8:
13119 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13120 OutputBecomesInput, Fn);
13121 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13122 OutputBecomesInput, Fn);
13123 break;
13124 case 16:
13125 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13126 OutputBecomesInput, Fn);
13127 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13128 OutputBecomesInput, Fn);
13129 break;
13130 case 32:
13131 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13132 OutputBecomesInput, Fn);
13133 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13134 OutputBecomesInput, Fn);
13135 break;
13136 case 64:
13137 case 128:
13138 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13139 OutputBecomesInput, Fn);
13140 break;
13141 default:
13142 llvm_unreachable("Scalar type is too wide.");
13143 }
13144}
13145
13146/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13148 llvm::Function *Fn, unsigned UserVLEN,
13150 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13151 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13152
13153 // Sort out parameter sequence.
13154 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13155 StringRef Prefix = "_ZGV";
13156 StringRef MangledName = Fn->getName();
13157
13158 // Generate simdlen from user input (if any).
13159 if (UserVLEN) {
13160 if (ISA == 's') {
13161 // SVE generates only a masked function.
13162 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13163 OutputBecomesInput, Fn);
13164 return;
13165 }
13166
13167 switch (Branch) {
13169 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13170 OutputBecomesInput, Fn);
13171 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13172 OutputBecomesInput, Fn);
13173 break;
13175 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13176 OutputBecomesInput, Fn);
13177 break;
13179 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13180 OutputBecomesInput, Fn);
13181 break;
13182 }
13183 return;
13184 }
13185
13186 if (ISA == 's') {
13187 // SVE, section 3.4.1, item 1.
13188 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13189 OutputBecomesInput, Fn);
13190 return;
13191 }
13192
13193 switch (Branch) {
13195 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13196 MangledName, OutputBecomesInput, Fn);
13197 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13198 MangledName, OutputBecomesInput, Fn);
13199 break;
13201 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13202 MangledName, OutputBecomesInput, Fn);
13203 break;
13205 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13206 MangledName, OutputBecomesInput, Fn);
13207 break;
13208 }
13209}
13210
13211//===----------------------------------------------------------------------===//
13212// OffloadEntriesInfoManager
13213//===----------------------------------------------------------------------===//
13214
13216 return OffloadEntriesTargetRegion.empty() &&
13217 OffloadEntriesDeviceGlobalVar.empty();
13218}
13219
13220unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13221 const TargetRegionEntryInfo &EntryInfo) const {
13222 auto It = OffloadEntriesTargetRegionCount.find(
13223 getTargetRegionEntryCountKey(EntryInfo));
13224 if (It == OffloadEntriesTargetRegionCount.end())
13225 return 0;
13226 return It->second;
13227}
13228
13229void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13230 const TargetRegionEntryInfo &EntryInfo) {
13231 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13232 EntryInfo.Count + 1;
13233}
13234
13235/// Initialize target region entry.
13237 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13238 OffloadEntriesTargetRegion[EntryInfo] =
13239 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13241 ++OffloadingEntriesNum;
13242}
13243
13245 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13247 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13248
13249 // Update the EntryInfo with the next available count for this location.
13250 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13251
13252 // If we are emitting code for a target, the entry is already initialized,
13253 // only has to be registered.
13254 if (OMPBuilder->Config.isTargetDevice()) {
13255 // This could happen if the device compilation is invoked standalone.
13256 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13257 return;
13258 }
13259 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13260 Entry.setAddress(Addr);
13261 Entry.setID(ID);
13262 Entry.setFlags(Flags);
13263 } else {
13265 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13266 return;
13267 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13268 "Target region entry already registered!");
13269 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13270 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13271 ++OffloadingEntriesNum;
13272 }
13273 incrementTargetRegionEntryInfoCount(EntryInfo);
13274}
13275
13277 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13278
13279 // Update the EntryInfo with the next available count for this location.
13280 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13281
13282 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13283 if (It == OffloadEntriesTargetRegion.end()) {
13284 return false;
13285 }
13286 // Fail if this entry is already registered.
13287 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13288 return false;
13289 return true;
13290}
13291
13293 const OffloadTargetRegionEntryInfoActTy &Action) {
13294 // Scan all target region entries and perform the provided action.
13295 for (const auto &It : OffloadEntriesTargetRegion) {
13296 Action(It.first, It.second);
13297 }
13298}
13299
13301 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13302 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13303 ++OffloadingEntriesNum;
13304}
13305
13307 StringRef VarName, Constant *Addr, int64_t VarSize,
13309 if (OMPBuilder->Config.isTargetDevice()) {
13310 // This could happen if the device compilation is invoked standalone.
13311 if (!hasDeviceGlobalVarEntryInfo(VarName))
13312 return;
13313 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13314 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13315 if (Entry.getVarSize() == 0) {
13316 Entry.setVarSize(VarSize);
13317 Entry.setLinkage(Linkage);
13318 }
13319 return;
13320 }
13321 Entry.setVarSize(VarSize);
13322 Entry.setLinkage(Linkage);
13323 Entry.setAddress(Addr);
13324 } else {
13325 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13326 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13327 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13328 "Entry not initialized!");
13329 if (Entry.getVarSize() == 0) {
13330 Entry.setVarSize(VarSize);
13331 Entry.setLinkage(Linkage);
13332 }
13333 return;
13334 }
13336 Flags ==
13338 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13339 Addr, VarSize, Flags, Linkage,
13340 VarName.str());
13341 else
13342 OffloadEntriesDeviceGlobalVar.try_emplace(
13343 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13344 ++OffloadingEntriesNum;
13345 }
13346}
13347
13350 // Scan all target region entries and perform the provided action.
13351 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13352 Action(E.getKey(), E.getValue());
13353}
13354
13355//===----------------------------------------------------------------------===//
13356// CanonicalLoopInfo
13357//===----------------------------------------------------------------------===//
13358
13359void CanonicalLoopInfo::collectControlBlocks(
13361 // We only count those BBs as control block for which we do not need to
13362 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13363 // flow. For consistency, this also means we do not add the Body block, which
13364 // is just the entry to the body code.
13365 BBs.reserve(BBs.size() + 6);
13366 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13367}
13368
13370 assert(isValid() && "Requires a valid canonical loop");
13371 for (BasicBlock *Pred : predecessors(Header)) {
13372 if (Pred != Latch)
13373 return Pred;
13374 }
13375 llvm_unreachable("Missing preheader");
13376}
13377
13378void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13379 assert(isValid() && "Requires a valid canonical loop");
13380
13381 Instruction *CmpI = &getCond()->front();
13382 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13383 CmpI->setOperand(1, TripCount);
13384
13385#ifndef NDEBUG
13386 assertOK();
13387#endif
13388}
13389
13390void CanonicalLoopInfo::mapIndVar(
13391 llvm::function_ref<Value *(Instruction *)> Updater) {
13392 assert(isValid() && "Requires a valid canonical loop");
13393
13394 Instruction *OldIV = getIndVar();
13395
13396 // Record all uses excluding those introduced by the updater. Uses by the
13397 // CanonicalLoopInfo itself to keep track of the number of iterations are
13398 // excluded.
13399 SmallVector<Use *> ReplacableUses;
13400 for (Use &U : OldIV->uses()) {
13401 auto *User = dyn_cast<Instruction>(U.getUser());
13402 if (!User)
13403 continue;
13404 if (User->getParent() == getCond())
13405 continue;
13406 if (User->getParent() == getLatch())
13407 continue;
13408 ReplacableUses.push_back(&U);
13409 }
13410
13411 // Run the updater that may introduce new uses
13412 Value *NewIV = Updater(OldIV);
13413
13414 // Replace the old uses with the value returned by the updater.
13415 for (Use *U : ReplacableUses)
13416 U->set(NewIV);
13417
13418#ifndef NDEBUG
13419 assertOK();
13420#endif
13421}
13422
13424#ifndef NDEBUG
13425 // No constraints if this object currently does not describe a loop.
13426 if (!isValid())
13427 return;
13428
13429 BasicBlock *Preheader = getPreheader();
13430 BasicBlock *Body = getBody();
13431 BasicBlock *After = getAfter();
13432
13433 // Verify standard control-flow we use for OpenMP loops.
13434 assert(Preheader);
13435 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13436 "Preheader must terminate with unconditional branch");
13437 assert(Preheader->getSingleSuccessor() == Header &&
13438 "Preheader must jump to header");
13439
13440 assert(Header);
13441 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13442 "Header must terminate with unconditional branch");
13443 assert(Header->getSingleSuccessor() == Cond &&
13444 "Header must jump to exiting block");
13445
13446 assert(Cond);
13447 assert(Cond->getSinglePredecessor() == Header &&
13448 "Exiting block only reachable from header");
13449
13450 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13451 "Exiting block must terminate with conditional branch");
13452 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13453 "Exiting block's first successor jump to the body");
13454 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13455 "Exiting block's second successor must exit the loop");
13456
13457 assert(Body);
13458 assert(Body->getSinglePredecessor() == Cond &&
13459 "Body only reachable from exiting block");
13460 assert(!isa<PHINode>(Body->front()));
13461
13462 assert(Latch);
13463 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13464 "Latch must terminate with unconditional branch");
13465 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13466 // TODO: To support simple redirecting of the end of the body code that has
13467 // multiple; introduce another auxiliary basic block like preheader and after.
13468 assert(Latch->getSinglePredecessor() != nullptr);
13469 assert(!isa<PHINode>(Latch->front()));
13470
13471 assert(Exit);
13472 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13473 "Exit block must terminate with unconditional branch");
13474 assert(Exit->getSingleSuccessor() == After &&
13475 "Exit block must jump to after block");
13476
13477 assert(After);
13478 assert(After->getSinglePredecessor() == Exit &&
13479 "After block only reachable from exit block");
13480 assert(After->empty() || !isa<PHINode>(After->front()));
13481
13482 Instruction *IndVar = getIndVar();
13483 assert(IndVar && "Canonical induction variable not found?");
13484 assert(isa<IntegerType>(IndVar->getType()) &&
13485 "Induction variable must be an integer");
13486 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13487 "Induction variable must be a PHI in the loop header");
13488 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13489 assert(
13490 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13491 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13492
13493 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13494 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13495 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13496 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13497 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13498 ->isOne());
13499
13500 Value *TripCount = getTripCount();
13501 assert(TripCount && "Loop trip count not found?");
13502 assert(IndVar->getType() == TripCount->getType() &&
13503 "Trip count and induction variable must have the same type");
13504
13505 auto *CmpI = cast<CmpInst>(&Cond->front());
13506 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13507 "Exit condition must be a signed less-than comparison");
13508 assert(CmpI->getOperand(0) == IndVar &&
13509 "Exit condition must compare the induction variable");
13510 assert(CmpI->getOperand(1) == TripCount &&
13511 "Exit condition must compare with the trip count");
13512#endif
13513}
13514
13516 Header = nullptr;
13517 Cond = nullptr;
13518 Latch = nullptr;
13519 Exit = nullptr;
13520}
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),...