LLVM 24.0.0git
OMPIRBuilder.cpp
Go to the documentation of this file.
1//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// This file implements the OpenMPIRBuilder class, which is used as a
11/// convenient way to create LLVM instructions for OpenMP directives.
12///
13//===----------------------------------------------------------------------===//
14
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/StringRef.h"
31#include "llvm/IR/Attributes.h"
32#include "llvm/IR/BasicBlock.h"
33#include "llvm/IR/CFG.h"
34#include "llvm/IR/CallingConv.h"
35#include "llvm/IR/Constant.h"
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/DIBuilder.h"
40#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
45#include "llvm/IR/LLVMContext.h"
46#include "llvm/IR/MDBuilder.h"
47#include "llvm/IR/Metadata.h"
49#include "llvm/IR/PassManager.h"
51#include "llvm/IR/Value.h"
54#include "llvm/Support/Error.h"
65
66#include <cstdint>
67#include <optional>
68
69#define DEBUG_TYPE "openmp-ir-builder"
70
71using namespace llvm;
72using namespace omp;
73
74static cl::opt<bool>
75 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
76 cl::desc("Use optimistic attributes describing "
77 "'as-if' properties of runtime calls."),
78 cl::init(false));
79
81 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
82 cl::desc("Factor for the unroll threshold to account for code "
83 "simplifications still taking place"),
84 cl::init(1.5));
85
87 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
88 cl::desc("Use a default max threads if none is provided."), cl::init(true));
89
90#ifndef NDEBUG
91/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
92/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
93/// an InsertPoint stores the instruction before something is inserted. For
94/// instance, if both point to the same instruction, two IRBuilders alternating
95/// creating instruction will cause the instructions to be interleaved.
98 if (!IP1.isValid() || !IP2.isValid())
99 return false;
100 return IP1 == IP2;
101}
102
104 // Valid ordered/unordered and base algorithm combinations.
105 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
106 case OMPScheduleType::UnorderedStaticChunked:
107 case OMPScheduleType::UnorderedStatic:
108 case OMPScheduleType::UnorderedDynamicChunked:
109 case OMPScheduleType::UnorderedGuidedChunked:
110 case OMPScheduleType::UnorderedRuntime:
111 case OMPScheduleType::UnorderedAuto:
112 case OMPScheduleType::UnorderedTrapezoidal:
113 case OMPScheduleType::UnorderedGreedy:
114 case OMPScheduleType::UnorderedBalanced:
115 case OMPScheduleType::UnorderedGuidedIterativeChunked:
116 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
117 case OMPScheduleType::UnorderedSteal:
118 case OMPScheduleType::UnorderedStaticBalancedChunked:
119 case OMPScheduleType::UnorderedGuidedSimd:
120 case OMPScheduleType::UnorderedRuntimeSimd:
121 case OMPScheduleType::OrderedStaticChunked:
122 case OMPScheduleType::OrderedStatic:
123 case OMPScheduleType::OrderedDynamicChunked:
124 case OMPScheduleType::OrderedGuidedChunked:
125 case OMPScheduleType::OrderedRuntime:
126 case OMPScheduleType::OrderedAuto:
127 case OMPScheduleType::OrderdTrapezoidal:
128 case OMPScheduleType::NomergeUnorderedStaticChunked:
129 case OMPScheduleType::NomergeUnorderedStatic:
130 case OMPScheduleType::NomergeUnorderedDynamicChunked:
131 case OMPScheduleType::NomergeUnorderedGuidedChunked:
132 case OMPScheduleType::NomergeUnorderedRuntime:
133 case OMPScheduleType::NomergeUnorderedAuto:
134 case OMPScheduleType::NomergeUnorderedTrapezoidal:
135 case OMPScheduleType::NomergeUnorderedGreedy:
136 case OMPScheduleType::NomergeUnorderedBalanced:
137 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
138 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
139 case OMPScheduleType::NomergeUnorderedSteal:
140 case OMPScheduleType::NomergeOrderedStaticChunked:
141 case OMPScheduleType::NomergeOrderedStatic:
142 case OMPScheduleType::NomergeOrderedDynamicChunked:
143 case OMPScheduleType::NomergeOrderedGuidedChunked:
144 case OMPScheduleType::NomergeOrderedRuntime:
145 case OMPScheduleType::NomergeOrderedAuto:
146 case OMPScheduleType::NomergeOrderedTrapezoidal:
147 case OMPScheduleType::OrderedDistributeChunked:
148 case OMPScheduleType::OrderedDistribute:
149 break;
150 default:
151 return false;
152 }
153
154 // Must not set both monotonicity modifiers at the same time.
155 OMPScheduleType MonotonicityFlags =
156 SchedType & OMPScheduleType::MonotonicityMask;
157 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
158 return false;
159
160 return true;
161}
162#endif
163
164/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
165/// debug location when the insert point is at the end of a block. It picks a
166/// location scoped to the current function: the block's last instruction
167/// location if the block is non-empty, otherwise a location synthesized from
168/// the function's subprogram (when the function has debug info).
171 Builder.restoreIP(IP);
172 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
173 // set the debug location from that instruction, so leave it alone.
174 llvm::BasicBlock *BB = Builder.GetInsertBlock();
175 if (Builder.GetInsertPoint() != BB->end())
176 return;
177
178 // At the end of a block, pick a location guaranteed to belong to the current
179 // insertion function's subprogram. Prefer the block's own last instruction;
180 // otherwise synthesize a location from the function's subprogram.
181 if (!BB->empty())
182 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());
183 else if (llvm::DISubprogram *FSP =
184 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
185 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
186 Builder.SetCurrentDebugLocation(
187 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
188 }
189}
190
191static bool hasGridValue(const Triple &T) {
192 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
193}
194
195static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
196 if (T.isAMDGPU()) {
197 StringRef Features =
198 Kernel->getFnAttribute("target-features").getValueAsString();
199 if (Features.count("+wavefrontsize64"))
202 }
203 if (T.isNVPTX())
205 if (T.isSPIRV())
207 llvm_unreachable("No grid value available for this architecture!");
208}
209
210/// Determine which scheduling algorithm to use, determined from schedule clause
211/// arguments.
212static OMPScheduleType
213getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
214 bool HasSimdModifier, bool HasDistScheduleChunks) {
215 // Currently, the default schedule it static.
216 switch (ClauseKind) {
217 case OMP_SCHEDULE_Default:
218 case OMP_SCHEDULE_Static:
219 return HasChunks ? OMPScheduleType::BaseStaticChunked
220 : OMPScheduleType::BaseStatic;
221 case OMP_SCHEDULE_Dynamic:
222 return OMPScheduleType::BaseDynamicChunked;
223 case OMP_SCHEDULE_Guided:
224 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
225 : OMPScheduleType::BaseGuidedChunked;
226 case OMP_SCHEDULE_Auto:
228 case OMP_SCHEDULE_Runtime:
229 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
230 : OMPScheduleType::BaseRuntime;
231 case OMP_SCHEDULE_Distribute:
232 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
233 : OMPScheduleType::BaseDistribute;
234 }
235 llvm_unreachable("unhandled schedule clause argument");
236}
237
238/// Adds ordering modifier flags to schedule type.
239static OMPScheduleType
241 bool HasOrderedClause) {
242 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
243 OMPScheduleType::None &&
244 "Must not have ordering nor monotonicity flags already set");
245
246 OMPScheduleType OrderingModifier = HasOrderedClause
247 ? OMPScheduleType::ModifierOrdered
248 : OMPScheduleType::ModifierUnordered;
249 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
250
251 // Unsupported combinations
252 if (OrderingScheduleType ==
253 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
254 return OMPScheduleType::OrderedGuidedChunked;
255 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
256 OMPScheduleType::ModifierOrdered))
257 return OMPScheduleType::OrderedRuntime;
258
259 return OrderingScheduleType;
260}
261
262/// Adds monotonicity modifier flags to schedule type.
263static OMPScheduleType
265 bool HasSimdModifier, bool HasMonotonic,
266 bool HasNonmonotonic, bool HasOrderedClause) {
267 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
268 OMPScheduleType::None &&
269 "Must not have monotonicity flags already set");
270 assert((!HasMonotonic || !HasNonmonotonic) &&
271 "Monotonic and Nonmonotonic are contradicting each other");
272
273 if (HasMonotonic) {
274 return ScheduleType | OMPScheduleType::ModifierMonotonic;
275 } else if (HasNonmonotonic) {
276 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
277 } else {
278 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
279 // If the static schedule kind is specified or if the ordered clause is
280 // specified, and if the nonmonotonic modifier is not specified, the
281 // effect is as if the monotonic modifier is specified. Otherwise, unless
282 // the monotonic modifier is specified, the effect is as if the
283 // nonmonotonic modifier is specified.
284 OMPScheduleType BaseScheduleType =
285 ScheduleType & ~OMPScheduleType::ModifierMask;
286 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
287 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
288 HasOrderedClause) {
289 // The monotonic is used by default in openmp runtime library, so no need
290 // to set it.
291 return ScheduleType;
292 } else {
293 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
294 }
295 }
296}
297
298/// Determine the schedule type using schedule and ordering clause arguments.
299static OMPScheduleType
300computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
301 bool HasSimdModifier, bool HasMonotonicModifier,
302 bool HasNonmonotonicModifier, bool HasOrderedClause,
303 bool HasDistScheduleChunks) {
305 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
306 OMPScheduleType OrderedSchedule =
307 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
309 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
310 HasNonmonotonicModifier, HasOrderedClause);
311
313 return Result;
314}
315
316/// Given a function, if it represents the entry point of a target kernel, this
317/// returns the execution mode flags associated with that kernel.
318static std::optional<omp::OMPTgtExecModeFlags>
320 CallInst *TargetInitCall = nullptr;
321 for (Instruction &Inst : Kernel.getEntryBlock()) {
322 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
323 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
324 TargetInitCall = Call;
325 break;
326 }
327 }
328 }
329
330 if (!TargetInitCall)
331 return std::nullopt;
332
333 // Get the kernel mode information from the global variable associated to the
334 // first argument to the call to __kmpc_target_init. Refer to
335 // createTargetInit() to see how this is initialized.
336 Value *InitOperand = TargetInitCall->getArgOperand(0);
337 GlobalVariable *KernelEnv = nullptr;
338 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
339 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
340 else
341 KernelEnv = cast<GlobalVariable>(InitOperand);
342 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
343 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
344 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
345 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
346}
347
348static bool isGenericKernel(Function &Fn) {
349 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
351 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
352}
353
354/// Make \p Source branch to \p Target.
355///
356/// Handles two situations:
357/// * \p Source already has an unconditional branch.
358/// * \p Source is a degenerate block (no terminator because the BB is
359/// the current head of the IR construction).
361 if (Instruction *Term = Source->getTerminatorOrNull()) {
362 auto *Br = cast<UncondBrInst>(Term);
363 BasicBlock *Succ = Br->getSuccessor();
364 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
365 Br->setSuccessor(Target);
366 return;
367 }
368
369 auto *NewBr = UncondBrInst::Create(Target, Source);
370 NewBr->setDebugLoc(DL);
371}
372
374 bool CreateBranch, DebugLoc DL) {
375 assert(New->getFirstInsertionPt() == New->begin() &&
376 "Target BB must not have PHI nodes");
377
378 // Move instructions to new block.
379 BasicBlock *Old = IP.getNodeParent();
380 // If the `Old` block is empty then there are no instructions to move. But in
381 // the new debug scheme, it could have trailing debug records which will be
382 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
383 // reasons:
384 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
385 // 2. Even if `New` is not empty, the rationale to move those records to `New`
386 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
387 // assumes that `Old` is optimized out and is going away. This is not the case
388 // here. The `Old` block is still being used e.g. a branch instruction is
389 // added to it later in this function.
390 // So we call `BasicBlock::splice` only when `Old` is not empty.
391 if (!Old->empty())
392 New->splice(New->begin(), Old, IP, Old->end());
393
394 if (CreateBranch) {
395 auto *NewBr = UncondBrInst::Create(New, Old);
396 NewBr->setDebugLoc(DL);
397 }
398}
399
400void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
401 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
402 BasicBlock *Old = Builder.GetInsertBlock();
403
404 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
405 if (CreateBranch)
406 Builder.SetInsertPoint(Old->getTerminator());
407 else
408 Builder.SetInsertPoint(Old);
409
410 // SetInsertPoint also updates the Builder's debug location, but we want to
411 // keep the one the Builder was configured to use.
412 Builder.SetCurrentDebugLocation(DebugLoc);
413}
414
416 DebugLoc DL, llvm::Twine Name) {
417 BasicBlock *Old = IP.getNodeParent();
419 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
420 Old->getParent(), Old->getNextNode());
421 spliceBB(IP, New, CreateBranch, DL);
422 New->replaceSuccessorsPhiUsesWith(Old, New);
423 return New;
424}
425
426BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
427 llvm::Twine Name) {
428 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
429 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
430 if (CreateBranch)
431 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
432 else
433 Builder.SetInsertPoint(Builder.GetInsertBlock());
434 // SetInsertPoint also updates the Builder's debug location, but we want to
435 // keep the one the Builder was configured to use.
436 Builder.SetCurrentDebugLocation(DebugLoc);
437 return New;
438}
439
440BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
441 llvm::Twine Name) {
442 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
443 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
444 if (CreateBranch)
445 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
446 else
447 Builder.SetInsertPoint(Builder.GetInsertBlock());
448 // SetInsertPoint also updates the Builder's debug location, but we want to
449 // keep the one the Builder was configured to use.
450 Builder.SetCurrentDebugLocation(DebugLoc);
451 return New;
452}
453
455 llvm::Twine Suffix) {
456 BasicBlock *Old = Builder.GetInsertBlock();
457 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
458}
459
460// This function creates a fake integer value and a fake use for the integer
461// value. It returns the fake value created. This is useful in modeling the
462// extra arguments to the outlined functions.
464 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
466 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
467 const Twine &Name = "", bool AsPtr = true,
468 bool Is64Bit = false) {
469 Builder.restoreIP(OuterAllocaIP);
470 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
471 Instruction *FakeVal;
472 AllocaInst *FakeValAddr =
473 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
474 ToBeDeleted.push_back(FakeValAddr);
475
476 if (AsPtr) {
477 FakeVal = FakeValAddr;
478 // The runtime passes these extra arguments to the outlined function as
479 // generic pointers, so cast away a non-zero alloca address space.
480 if (FakeValAddr->getAddressSpace() != 0) {
481 FakeVal = cast<Instruction>(Builder.CreateAddrSpaceCast(
482 FakeValAddr, Builder.getPtrTy(), Name + ".ascast"));
483 ToBeDeleted.push_back(FakeVal);
484 }
485 } else {
486 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
487 ToBeDeleted.push_back(FakeVal);
488 }
489
490 // Generate a fake use of this value
491 Builder.restoreIP(InnerAllocaIP);
492 Instruction *UseFakeVal;
493 if (AsPtr) {
494 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
495 } else {
496 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
497 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
498 }
499 ToBeDeleted.push_back(UseFakeVal);
500 return FakeVal;
501}
502
503//===----------------------------------------------------------------------===//
504// OpenMPIRBuilderConfig
505//===----------------------------------------------------------------------===//
506
507namespace {
509/// Values for bit flags for marking which requires clauses have been used.
510enum OpenMPOffloadingRequiresDirFlags {
511 /// flag undefined.
512 OMP_REQ_UNDEFINED = 0x000,
513 /// no requires directive present.
514 OMP_REQ_NONE = 0x001,
515 /// reverse_offload clause.
516 OMP_REQ_REVERSE_OFFLOAD = 0x002,
517 /// unified_address clause.
518 OMP_REQ_UNIFIED_ADDRESS = 0x004,
519 /// unified_shared_memory clause.
520 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
521 /// dynamic_allocators clause.
522 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
523 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
524};
525
526class OMPCodeExtractor : public CodeExtractor {
527public:
528 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
529 DominatorTree *DT = nullptr, bool AggregateArgs = false,
530 BlockFrequencyInfo *BFI = nullptr,
531 BranchProbabilityInfo *BPI = nullptr,
532 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
533 bool AllowAlloca = false,
534 BasicBlock *AllocationBlock = nullptr,
535 ArrayRef<BasicBlock *> DeallocationBlocks = {},
536 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
537 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
538 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
539 ArgsInZeroAddressSpace),
540 OMPBuilder(OMPBuilder) {}
541
542 virtual ~OMPCodeExtractor() = default;
543
544protected:
545 OpenMPIRBuilder &OMPBuilder;
546};
547
548class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
549public:
550 using OMPCodeExtractor::OMPCodeExtractor;
551 virtual ~DeviceSharedMemCodeExtractor() = default;
552
553protected:
554 virtual Instruction *
555 allocateVar(IRBuilder<>::InsertPoint AllocaIP, DebugLoc DL, Type *VarType,
556 const Twine &Name = Twine(""),
557 AddrSpaceCastInst **CastedAlloc = nullptr) override {
558 return OMPBuilder.createOMPAllocShared({AllocaIP, DL}, VarType, Name);
559 }
560
561 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
562 DebugLoc DL, Value *Var,
563 Type *VarType) override {
564 return OMPBuilder.createOMPFreeShared({DeallocIP, DL}, Var, VarType);
565 }
566};
567
568/// Helper storing information about regions to outline using device shared
569/// memory for intermediate allocations.
570struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
571 OpenMPIRBuilder &OMPBuilder;
572
573 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
574 : OMPBuilder(OMPBuilder) {}
575 virtual ~DeviceSharedMemOutlineInfo() = default;
576
577 virtual std::unique_ptr<CodeExtractor>
578 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
579 bool ArgsInZeroAddressSpace,
580 Twine Suffix = Twine("")) override;
581};
582
583} // anonymous namespace
584
586 : RequiresFlags(OMP_REQ_UNDEFINED) {}
587
590 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
591 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
594 RequiresFlags(OMP_REQ_UNDEFINED) {
595 if (HasRequiresReverseOffload)
596 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
597 if (HasRequiresUnifiedAddress)
598 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
599 if (HasRequiresUnifiedSharedMemory)
600 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
601 if (HasRequiresDynamicAllocators)
602 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
603}
604
606 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
607}
608
610 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
611}
612
614 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
615}
616
618 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
619}
620
622 return hasRequiresFlags() ? RequiresFlags
623 : static_cast<int64_t>(OMP_REQ_NONE);
624}
625
627 if (Value)
628 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
629 else
630 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
631}
632
634 if (Value)
635 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
636 else
637 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
638}
639
641 if (Value)
642 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
643 else
644 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
645}
646
648 if (Value)
649 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
650 else
651 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
652}
653
654//===----------------------------------------------------------------------===//
655// OpenMPIRBuilder
656//===----------------------------------------------------------------------===//
657
660 SmallVector<Value *> &ArgsVector) {
662 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
663 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
664 constexpr size_t MaxDim = 3;
665 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
666
667 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
668
669 Value *DynCGroupMemFallbackFlag =
670 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
671 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
672
673 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
674 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
675
676 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
677 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
678
679 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
680 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
681 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
682
683 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
684
685 Value *NumTeams3D =
686 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
687 Value *NumThreads3D =
688 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
689 for (unsigned I :
690 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
691 NumTeams3D =
692 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
693 for (unsigned I :
694 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
695 NumThreads3D =
696 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
697
698 ArgsVector = {Version,
699 PointerNum,
700 KernelArgs.RTArgs.BasePointersArray,
701 KernelArgs.RTArgs.PointersArray,
702 KernelArgs.RTArgs.SizesArray,
703 KernelArgs.RTArgs.MapTypesArray,
704 KernelArgs.RTArgs.MapNamesArray,
705 KernelArgs.RTArgs.MappersArray,
706 KernelArgs.NumIterations,
707 Flags,
708 NumTeams3D,
709 NumThreads3D,
710 KernelArgs.DynCGroupMem};
711}
712
714 LLVMContext &Ctx = Fn.getContext();
715
716 // Get the function's current attributes.
717 auto Attrs = Fn.getAttributes();
718 auto FnAttrs = Attrs.getFnAttrs();
719 auto RetAttrs = Attrs.getRetAttrs();
721 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
722 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
723
724 // Add AS to FnAS while taking special care with integer extensions.
725 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
726 bool Param = true) -> void {
727 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
728 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
729 if (HasSignExt || HasZeroExt) {
730 assert(AS.getNumAttributes() == 1 &&
731 "Currently not handling extension attr combined with others.");
732 if (Param) {
733 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
734 FnAS = FnAS.addAttribute(Ctx, AK);
735 } else if (auto AK =
736 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
737 FnAS = FnAS.addAttribute(Ctx, AK);
738 } else {
739 FnAS = FnAS.addAttributes(Ctx, AS);
740 }
741 };
742
743#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
744#include "llvm/Frontend/OpenMP/OMPKinds.def"
745
746 // Add attributes to the function declaration.
747 switch (FnID) {
748#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
749 case Enum: \
750 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
751 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
752 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
753 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
754 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
755 break;
756#include "llvm/Frontend/OpenMP/OMPKinds.def"
757 default:
758 // Attributes are optional.
759 break;
760 }
761}
762
765 FunctionType *FnTy = nullptr;
766 Function *Fn = nullptr;
767
768 // Try to find the declation in the module first.
769 switch (FnID) {
770#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
771 case Enum: \
772 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
773 IsVarArg); \
774 Fn = M.getFunction(Str); \
775 break;
776#include "llvm/Frontend/OpenMP/OMPKinds.def"
777 }
778
779 if (!Fn) {
780 // Create a new declaration if we need one.
781 switch (FnID) {
782#define OMP_RTL(Enum, Str, ...) \
783 case Enum: \
784 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
785 break;
786#include "llvm/Frontend/OpenMP/OMPKinds.def"
787 }
788 Fn->setCallingConv(Config.getRuntimeCC());
789 // Add information if the runtime function takes a callback function
790 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
791 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
792 LLVMContext &Ctx = Fn->getContext();
793 MDBuilder MDB(Ctx);
794 // Annotate the callback behavior of the runtime function:
795 // - The callback callee is argument number 2 (microtask).
796 // - The first two arguments of the callback callee are unknown (-1).
797 // - All variadic arguments to the runtime function are passed to the
798 // callback callee.
799 Fn->addMetadata(
800 LLVMContext::MD_callback,
802 2, {-1, -1}, /* VarArgsArePassed */ true)}));
803 }
804 }
805
806 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
807 << " with type " << *Fn->getFunctionType() << "\n");
808 addAttributes(FnID, *Fn);
809
810 } else {
811 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
812 << " with type " << *Fn->getFunctionType() << "\n");
813 }
814
815 assert(Fn && "Failed to create OpenMP runtime function");
816
817 return {FnTy, Fn};
818}
819
822 if (!FiniBB) {
823 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
825 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
826 Builder.SetInsertPoint(FiniBB);
827 // FiniCB adds the branch to the exit stub.
828 if (Error Err = FiniCB(Builder.saveIP()))
829 return Err;
830 }
831 return FiniBB;
832}
833
835 BasicBlock *OtherFiniBB) {
836 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
837 if (!FiniBB) {
838 FiniBB = OtherFiniBB;
839
840 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
841 if (Error Err = FiniCB(Builder.saveIP()))
842 return Err;
843
844 return Error::success();
845 }
846
847 // Move instructions from FiniBB to the start of OtherFiniBB.
848 auto EndIt = FiniBB->end();
849 if (FiniBB->size() >= 1)
850 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
851 EndIt = Prev;
852 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
853 EndIt);
854
855 FiniBB->replaceAllUsesWith(OtherFiniBB);
856 FiniBB->eraseFromParent();
857 FiniBB = OtherFiniBB;
858 return Error::success();
859}
860
863 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
864 assert(Fn && "Failed to create OpenMP runtime function pointer");
865 return Fn;
866}
867
870 StringRef Name) {
871 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
872 Call->setCallingConv(Config.getRuntimeCC());
873 return Call;
874}
875
876void OpenMPIRBuilder::initialize() { initializeTypes(M); }
877
880 BasicBlock &EntryBlock = Function->getEntryBlock();
881 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
882
883 // Loop over blocks looking for constant allocas, skipping the entry block
884 // as any allocas there are already in the desired location.
885 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
886 Block++) {
887 for (auto Inst = Block->getReverseIterator()->begin();
888 Inst != Block->getReverseIterator()->end();) {
890 Inst++;
892 continue;
893 AllocaInst->moveBeforePreserving(MoveLocInst);
894 } else {
895 Inst++;
896 }
897 }
898 }
899}
900
903
904 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
905 // TODO: For now, we support simple static allocations, we might need to
906 // move non-static ones as well. However, this will need further analysis to
907 // move the lenght arguments as well.
909 };
910
911 for (llvm::Instruction &Inst : Block)
913 if (ShouldHoistAlloca(*AllocaInst))
914 AllocasToMove.push_back(AllocaInst);
915
916 auto InsertPoint =
917 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
918
919 for (llvm::Instruction *AllocaInst : AllocasToMove)
921}
922
924 PostDominatorTree PostDomTree(*Func);
925 for (llvm::BasicBlock &BB : *Func)
926 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
928}
929
931 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
933 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
934 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
935 // Skip functions that have not finalized yet; may happen with nested
936 // function generation.
937 if (Fn && OI->getFunction() != Fn) {
938 DeferredOutlines.push_back(std::move(OI));
939 continue;
940 }
941
942 ParallelRegionBlockSet.clear();
943 Blocks.clear();
944 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
945
946 Function *OuterFn = OI->getFunction();
947 CodeExtractorAnalysisCache CEAC(*OuterFn);
948 // If we generate code for the target device, we need to allocate
949 // struct for aggregate params in the device default alloca address space.
950 // OpenMP runtime requires that the params of the extracted functions are
951 // passed as zero address space pointers. This flag ensures that
952 // CodeExtractor generates correct code for extracted functions
953 // which are used by OpenMP runtime.
954 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
955 std::unique_ptr<CodeExtractor> Extractor =
956 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
957
958 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
959 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
960 << " Exit: " << OI->ExitBB->getName() << "\n");
961 assert(Extractor->isEligible() &&
962 "Expected OpenMP outlining to be possible!");
963
964 for (auto *V : OI->ExcludeArgsFromAggregate)
965 Extractor->excludeArgFromAggregate(V);
966
967 Function *OutlinedFn =
968 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
969
970 // Forward target-cpu, target-features attributes to the outlined function.
971 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
972 if (TargetCpuAttr.isStringAttribute())
973 OutlinedFn->addFnAttr(TargetCpuAttr);
974
975 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
976 if (TargetFeaturesAttr.isStringAttribute())
977 OutlinedFn->addFnAttr(TargetFeaturesAttr);
978
979 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
980 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
981 assert(OutlinedFn->getReturnType()->isVoidTy() &&
982 "OpenMP outlined functions should not return a value!");
983
984 // For compability with the clang CG we move the outlined function after the
985 // one with the parallel region.
986 OutlinedFn->removeFromParent();
987 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
988
989 // Remove the artificial entry introduced by the extractor right away, we
990 // made our own entry block after all.
991 {
992 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
993 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
994 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
995 // Move instructions from the to-be-deleted ArtificialEntry to the entry
996 // basic block of the parallel region. CodeExtractor generates
997 // instructions to unwrap the aggregate argument and may sink
998 // allocas/bitcasts for values that are solely used in the outlined region
999 // and do not escape.
1000 assert(!ArtificialEntry.empty() &&
1001 "Expected instructions to add in the outlined region entry");
1002 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
1003 End = ArtificialEntry.rend();
1004 It != End;) {
1005 Instruction &I = *It;
1006 It++;
1007
1008 if (I.isTerminator()) {
1009 // Absorb any debug value that terminator may have
1010 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1011 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1012 continue;
1013 }
1014
1015 I.moveBeforePreserving(*OI->EntryBB,
1016 OI->EntryBB->getFirstInsertionPt());
1017 }
1018
1019 OI->EntryBB->moveBefore(&ArtificialEntry);
1020 ArtificialEntry.eraseFromParent();
1021 }
1022 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1023 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1024
1025 // Run a user callback, e.g. to add attributes.
1026 if (OI->PostOutlineCB)
1027 OI->PostOutlineCB(*OutlinedFn);
1028
1029 if (OI->FixUpNonEntryAllocas)
1031 }
1032
1033 // Remove work items that have been completed.
1034 OutlineInfos = std::move(DeferredOutlines);
1035
1036 // The createTarget functions embeds user written code into
1037 // the target region which may inject allocas which need to
1038 // be moved to the entry block of our target or risk malformed
1039 // optimisations by later passes, this is only relevant for
1040 // the device pass which appears to be a little more delicate
1041 // when it comes to optimisations (however, we do not block on
1042 // that here, it's up to the inserter to the list to do so).
1043 // This notbaly has to occur after the OutlinedInfo candidates
1044 // have been extracted so we have an end product that will not
1045 // be implicitly adversely affected by any raises unless
1046 // intentionally appended to the list.
1047 // NOTE: This only does so for ConstantData, it could be extended
1048 // to ConstantExpr's with further effort, however, they should
1049 // largely be folded when they get here. Extending it to runtime
1050 // defined/read+writeable allocation sizes would be non-trivial
1051 // (need to factor in movement of any stores to variables the
1052 // allocation size depends on, as well as the usual loads,
1053 // otherwise it'll yield the wrong result after movement) and
1054 // likely be more suitable as an LLVM optimisation pass.
1057
1058 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1059 [](EmitMetadataErrorKind Kind,
1060 const TargetRegionEntryInfo &EntryInfo) -> void {
1061 errs() << "Error of kind: " << Kind
1062 << " when emitting offload entries and metadata during "
1063 "OMPIRBuilder finalization \n";
1064 };
1065
1066 if (!OffloadInfoManager.empty())
1068
1069 // Rewrite uses of globals to their replacement declare target globals if
1070 // we are processing a device module.
1071 if (Config.isTargetDevice())
1072 applyDeclareTargetGlobalReplacements();
1073
1074 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1075 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1076 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1077 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1078 }
1079
1080 IsFinalized = true;
1081}
1082
1083bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1084
1086 GlobalValue *Original, GlobalValue *Replacement) {
1087 assert(Original && Replacement &&
1088 "Null values provided to registerDeclareTargetGlobalReplacement");
1089 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1090}
1091
1092void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1093 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1094 GlobalValue *OldGV = R.Original;
1095 GlobalValue *NewGV = R.Replacement;
1096
1097 assert(OldGV && NewGV &&
1098 "A null value was inserted into DeclareTargetGlobalReplacements");
1099
1100 // The assert above should catch this case, but this is kept to attempt
1101 // to proceed without issue when asserts are off.
1102 if (!OldGV || !NewGV)
1103 continue;
1104
1105 // The replacement global is a reference pointer that holds the
1106 // address of the device-resident storage. Every use must load the
1107 // reference pointer first and use the loaded address.
1108 //
1109 // Constant expression users (e.g. a constant GEP embedded in another
1110 // global's initializer or in an instruction) cannot have a load inserted
1111 // in place, so first expand any constant-expression users that live inside
1112 // functions into instructions. Any remaining constant users are handled
1113 // via a direct constant rewrite below as we cannot materialize a load
1114 // there.
1115 //
1116 // NOTE: We extend the constant rewrite to module scope, as we replace all
1117 // usages.
1118 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1120 /*RestrictToFunc=*/nullptr,
1121 /*RemoveDeadConstants=*/false);
1122
1123 IRBuilderBase::InsertPointGuard Guard(Builder);
1125 for (User *U : Users) {
1126 auto *Insn = dyn_cast<Instruction>(U);
1127 if (!Insn)
1128 continue;
1129
1130 // A PHI node cannot have a load inserted immediately before it, as PHIs
1131 // must remain grouped at the top of their basic block. So we need to
1132 // make sure any loads we emit are generated in the preceding edge, a
1133 // PHI may reference the global on more than one edge, so every matching
1134 // slot must be handled.
1135 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1136 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1137 if (PHI->getIncomingValue(I) != OldGV)
1138 continue;
1139
1140 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1141 Builder.SetInsertPoint(IncomingBB->getTerminator());
1142 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1143 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1144 PHI->setIncomingValue(I, EdgeLoad);
1145 }
1146 continue;
1147 }
1148
1149 Builder.SetInsertPoint(Insn);
1150 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1151 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1152
1153 // The replacement declare target global lives in the default address
1154 // space, whereas the original global may reside in a non-default
1155 // address space. In that case the initial lowering may have
1156 // emitted an addrspacecast that is no longer valid. Replace the
1157 // whole addrspacecast with the load and erase it rather than
1158 // feeding the load back into the (now pointless) cast.
1159 // NOTE: If we end up with replacement declare target globals in
1160 // non-zero AS's the below will need some minor extensions to have the
1161 // option to alter the address space cast to the new address space where
1162 // required rather than just replacing it.
1163 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1164 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1165 assert(NewGVAS == 0 &&
1166 "Non-default address space declare target global");
1167 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1168 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1169 if (DestAS == 0 && NewGVAS != OldGVAS) {
1170 ASC->replaceAllUsesWith(Load);
1171 ASC->eraseFromParent();
1172 continue;
1173 }
1174 }
1175
1176 Insn->replaceUsesOfWith(OldGV, Load);
1177 }
1178 }
1179
1181}
1182
1184 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1185}
1186
1188 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1189 auto *GV =
1190 new GlobalVariable(M, I32Ty,
1191 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1192 ConstantInt::get(I32Ty, Value), Name);
1193 GV->setVisibility(GlobalValue::HiddenVisibility);
1194
1195 return GV;
1196}
1197
1199 if (List.empty())
1200 return;
1201
1202 // Convert List to what ConstantArray needs.
1204 UsedArray.resize(List.size());
1205 for (unsigned I = 0, E = List.size(); I != E; ++I)
1207 cast<Constant>(&*List[I]), Builder.getPtrTy());
1208
1209 if (UsedArray.empty())
1210 return;
1211 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1212
1213 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1214 ConstantArray::get(ATy, UsedArray), Name);
1215
1216 GV->setSection("llvm.metadata");
1217}
1218
1221 OMPTgtExecModeFlags Mode) {
1222 auto *Int8Ty = Builder.getInt8Ty();
1223 auto *GVMode = new GlobalVariable(
1224 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1225 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1226 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1227 return GVMode;
1228}
1229
1231 uint32_t SrcLocStrSize,
1232 IdentFlag LocFlags,
1233 unsigned Reserve2Flags) {
1234 // Enable "C-mode".
1235 LocFlags |= OMP_IDENT_FLAG_KMPC;
1236
1237 Constant *&Ident =
1238 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1239 if (!Ident) {
1240 Constant *I32Null = ConstantInt::getNullValue(Int32);
1241 Constant *IdentData[] = {I32Null,
1242 ConstantInt::get(Int32, uint32_t(LocFlags)),
1243 ConstantInt::get(Int32, Reserve2Flags),
1244 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1245
1246 size_t SrcLocStrArgIdx = 4;
1247 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1249 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1250 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1251 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1252 Constant *Initializer =
1253 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1254
1255 // Look for existing encoding of the location + flags, not needed but
1256 // minimizes the difference to the existing solution while we transition.
1257 for (GlobalVariable &GV : M.globals())
1258 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1259 if (GV.getInitializer() == Initializer)
1260 Ident = &GV;
1261
1262 if (!Ident) {
1263 auto *GV = new GlobalVariable(
1264 M, OpenMPIRBuilder::Ident,
1265 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1267 M.getDataLayout().getDefaultGlobalsAddressSpace());
1268 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1269 GV->setAlignment(Align(8));
1270 Ident = GV;
1271 }
1272 }
1273
1274 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1275}
1276
1278 uint32_t &SrcLocStrSize) {
1279 SrcLocStrSize = LocStr.size();
1280 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1281 if (!SrcLocStr) {
1282 Constant *Initializer =
1283 ConstantDataArray::getString(M.getContext(), LocStr);
1284
1285 // Look for existing encoding of the location, not needed but minimizes the
1286 // difference to the existing solution while we transition.
1287 for (GlobalVariable &GV : M.globals())
1288 if (GV.isConstant() && GV.hasInitializer() &&
1289 GV.getInitializer() == Initializer)
1290 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1291
1292 SrcLocStr = Builder.CreateGlobalString(
1293 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1294 &M);
1295 }
1296 return SrcLocStr;
1297}
1298
1300 StringRef FileName,
1301 unsigned Line, unsigned Column,
1302 uint32_t &SrcLocStrSize) {
1303 SmallString<128> Buffer;
1304 Buffer.push_back(';');
1305 Buffer.append(FileName);
1306 Buffer.push_back(';');
1307 Buffer.append(FunctionName);
1308 Buffer.push_back(';');
1309 Buffer.append(std::to_string(Line));
1310 Buffer.push_back(';');
1311 Buffer.append(std::to_string(Column));
1312 Buffer.push_back(';');
1313 Buffer.push_back(';');
1314 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1315}
1316
1317Constant *
1319 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1320 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1321}
1322
1324 uint32_t &SrcLocStrSize,
1325 const Function *F) {
1326 DILocation *DIL = DL.get();
1327 if (!DIL)
1328 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1329 StringRef FileName =
1330 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1331 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1332 if (Function.empty() && F)
1333 Function = F->getName();
1334 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1335 DIL->getColumn(), SrcLocStrSize);
1336}
1337
1339 uint32_t &SrcLocStrSize) {
1340 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1341 Loc.IP.getNodeParent()->getParent());
1342}
1343
1346 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1347 "omp_global_thread_num");
1348}
1349
1350OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1351 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1352 ArrayRef<Type *> ResultPtrTys,
1353 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1354 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1355 "expected one result pointer type per in_reduction item");
1356 if (!updateToLocation(Loc))
1357 return Loc.IP;
1358 if (OrigPtrs.empty())
1359 return Builder.saveIP();
1360
1361 // Compute the executing thread's gtid once for the whole target body and
1362 // reuse it for every in_reduction lookup, so a target with several
1363 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1364 // item.
1365 uint32_t SrcLocStrSize;
1366 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1367 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1368 Value *Gtid = getOrCreateThreadID(Ident);
1369
1370 // The runtime entry point takes (and returns) a generic, default-address-
1371 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1372 // taskgroups to find the matching task_reduction registration for the item.
1373 Type *PtrTy = PointerType::getUnqual(M.getContext());
1374 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1375 FunctionCallee GetThData =
1376 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1377
1378 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1379 // Normalize a non-default-address-space original pointer to the generic
1380 // address space before the call.
1381 Value *OrigPtr = OrigPtrs[Idx];
1382 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1383 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1384 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1385
1386 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1387 "omp.inred.priv");
1388
1389 // Cast the returned private pointer back to the requested address space
1390 // when it differs.
1391 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1392 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1393 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1394
1395 MapPrivateCB(Idx, Priv);
1396 }
1397 return Builder.saveIP();
1398}
1399
1402 bool ForceSimpleCall, bool CheckCancelFlag) {
1403 if (!updateToLocation(Loc))
1404 return Loc.IP;
1405
1406 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1407 // __kmpc_barrier(loc, thread_id);
1408
1409 IdentFlag BarrierLocFlags;
1410 switch (Kind) {
1411 case OMPD_for:
1412 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1413 break;
1414 case OMPD_sections:
1415 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1416 break;
1417 case OMPD_single:
1418 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1419 break;
1420 case OMPD_barrier:
1421 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1422 break;
1423 default:
1424 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1425 break;
1426 }
1427
1428 uint32_t SrcLocStrSize;
1429 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1430 Value *Args[] = {
1431 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1432 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1433
1434 // If we are in a cancellable parallel region, barriers are cancellation
1435 // points.
1436 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1437 bool UseCancelBarrier =
1438 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1439
1441 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1442 ? OMPRTL___kmpc_cancel_barrier
1443 : OMPRTL___kmpc_barrier),
1444 Args);
1445
1446 if (UseCancelBarrier && CheckCancelFlag)
1447 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1448 return Err;
1449
1450 return Builder.saveIP();
1451}
1452
1455 Value *IfCondition,
1456 omp::Directive CanceledDirective) {
1457 if (!updateToLocation(Loc))
1458 return Loc.IP;
1459
1460 // LLVM utilities like blocks with terminators.
1461 auto *UI = Builder.CreateUnreachable();
1462
1463 Instruction *ThenTI = UI, *ElseTI = nullptr;
1464 if (IfCondition) {
1465 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1466
1467 // Even if the if condition evaluates to false, this should count as a
1468 // cancellation point
1469 Builder.SetInsertPoint(ElseTI);
1470 auto ElseIP = Builder.saveIP();
1471
1473 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1474 if (!IPOrErr)
1475 return IPOrErr;
1476 }
1477
1478 Builder.SetInsertPoint(ThenTI);
1479
1480 Value *CancelKind = nullptr;
1481 switch (CanceledDirective) {
1482#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1483 case DirectiveEnum: \
1484 CancelKind = Builder.getInt32(Value); \
1485 break;
1486#include "llvm/Frontend/OpenMP/OMPKinds.def"
1487 default:
1488 llvm_unreachable("Unknown cancel kind!");
1489 }
1490
1491 uint32_t SrcLocStrSize;
1492 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1493 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1494 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1496 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1497
1498 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1499 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1500 return Err;
1501
1502 // Update the insertion point and remove the terminator we introduced.
1503 Builder.SetInsertPoint(UI->getParent());
1504 UI->eraseFromParent();
1505
1506 return Builder.saveIP();
1507}
1508
1511 omp::Directive CanceledDirective) {
1512 if (!updateToLocation(Loc))
1513 return Loc.IP;
1514
1515 // LLVM utilities like blocks with terminators.
1516 auto *UI = Builder.CreateUnreachable();
1517 Builder.SetInsertPoint(UI);
1518
1519 Value *CancelKind = nullptr;
1520 switch (CanceledDirective) {
1521#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1522 case DirectiveEnum: \
1523 CancelKind = Builder.getInt32(Value); \
1524 break;
1525#include "llvm/Frontend/OpenMP/OMPKinds.def"
1526 default:
1527 llvm_unreachable("Unknown cancel kind!");
1528 }
1529
1530 uint32_t SrcLocStrSize;
1531 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1532 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1533 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1535 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1536
1537 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1538 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1539 return Err;
1540
1541 // Update the insertion point and remove the terminator we introduced.
1542 Builder.SetInsertPoint(UI->getParent());
1543 UI->eraseFromParent();
1544
1545 return Builder.saveIP();
1546}
1547
1549 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1550 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1551 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1552 if (!updateToLocation(Loc))
1553 return Loc.IP;
1554
1555 Builder.restoreIP(AllocaIP);
1556 auto *KernelArgsPtr =
1557 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1559
1560 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1561 llvm::Value *Arg =
1562 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1563 Builder.CreateAlignedStore(
1564 KernelArgs[I], Arg,
1565 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1566 }
1567
1568 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1569 NumThreads, HostPtr, KernelArgsPtr};
1570
1572 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1573 OffloadingArgs);
1574
1575 return Builder.saveIP();
1576}
1577
1579 const LocationDescription &Loc, Value *OutlinedFnID,
1580 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1581 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1582
1583 if (!updateToLocation(Loc))
1584 return Loc.IP;
1585
1586 // On top of the arrays that were filled up, the target offloading call
1587 // takes as arguments the device id as well as the host pointer. The host
1588 // pointer is used by the runtime library to identify the current target
1589 // region, so it only has to be unique and not necessarily point to
1590 // anything. It could be the pointer to the outlined function that
1591 // implements the target region, but we aren't using that so that the
1592 // compiler doesn't need to keep that, and could therefore inline the host
1593 // function if proven worthwhile during optimization.
1594
1595 // From this point on, we need to have an ID of the target region defined.
1596 assert(OutlinedFnID && "Invalid outlined function ID!");
1597 (void)OutlinedFnID;
1598
1599 // Return value of the runtime offloading call.
1600 Value *Return = nullptr;
1601
1602 // Arguments for the target kernel.
1603 SmallVector<Value *> ArgsVector;
1604 getKernelArgsVector(Args, Builder, ArgsVector);
1605
1606 // The target region is an outlined function launched by the runtime
1607 // via calls to __tgt_target_kernel().
1608 //
1609 // Note that on the host and CPU targets, the runtime implementation of
1610 // these calls simply call the outlined function without forking threads.
1611 // The outlined functions themselves have runtime calls to
1612 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1613 // the compiler in emitTeamsCall() and emitParallelCall().
1614 //
1615 // In contrast, on the NVPTX target, the implementation of
1616 // __tgt_target_teams() launches a GPU kernel with the requested number
1617 // of teams and threads so no additional calls to the runtime are required.
1618 // Check the error code and execute the host version if required.
1619 Builder.restoreIP(emitTargetKernel(
1620 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1621 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1622
1623 BasicBlock *OffloadFailedBlock =
1624 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1625 BasicBlock *OffloadContBlock =
1626 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1627 Value *Failed = Builder.CreateIsNotNull(Return);
1628 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1629
1630 auto CurFn = Builder.GetInsertBlock()->getParent();
1631 emitBlock(OffloadFailedBlock, CurFn);
1632 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1633 if (!AfterIP)
1634 return AfterIP.takeError();
1635 Builder.restoreIP(*AfterIP);
1636 emitBranch(OffloadContBlock);
1637 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1638 return Builder.saveIP();
1639}
1640
1642 Value *CancelFlag, omp::Directive CanceledDirective) {
1643 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1644 "Unexpected cancellation!");
1645
1646 // For a cancel barrier we create two new blocks.
1647 BasicBlock *BB = Builder.GetInsertBlock();
1648 BasicBlock *NonCancellationBlock;
1649 if (Builder.GetInsertPoint() == BB->end()) {
1650 // TODO: This branch will not be needed once we moved to the
1651 // OpenMPIRBuilder codegen completely.
1652 NonCancellationBlock = BasicBlock::Create(
1653 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1654 } else {
1655 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1657 Builder.SetInsertPoint(BB);
1658 }
1659 BasicBlock *CancellationBlock = BasicBlock::Create(
1660 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1661
1662 // Jump to them based on the return value.
1663 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1664 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1665 /* TODO weight */ nullptr, nullptr);
1666
1667 // From the cancellation block we finalize all variables and go to the
1668 // post finalization block that is known to the FiniCB callback.
1669 auto &FI = FinalizationStack.back();
1670 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1671 if (!FiniBBOrErr)
1672 return FiniBBOrErr.takeError();
1673 Builder.SetInsertPoint(CancellationBlock);
1674 Builder.CreateBr(*FiniBBOrErr);
1675
1676 // The continuation block is where code generation continues.
1677 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1678 return Error::success();
1679}
1680
1681/// Create wrapper function used to gather the outlined function's argument
1682/// structure from a shared buffer and to forward them to it when running in
1683/// Generic mode.
1684///
1685/// The outlined function is expected to receive 2 integer arguments followed by
1686/// an optional pointer argument to an argument structure holding the rest.
1688 Function &OutlinedFn) {
1689 size_t NumArgs = OutlinedFn.arg_size();
1690 assert((NumArgs == 2 || NumArgs == 3) &&
1691 "expected a 2-3 argument parallel outlined function");
1692 bool UseArgStruct = NumArgs == 3;
1693
1694 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1695 IRBuilder<>::InsertPointGuard IPG(Builder);
1696 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1697 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1698 /*isVarArg=*/false);
1699 auto *WrapperFn =
1701 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1702
1703 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1704 WrapperFn->addParamAttr(0, Attribute::ZExt);
1705 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1706
1707 BasicBlock *EntryBB =
1708 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1709 Builder.SetInsertPoint(EntryBB);
1710
1711 // Allocation.
1712 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1713 /*ArraySize=*/nullptr, "addr");
1714 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1715 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1716 AddrAlloca->getName() + ".ascast");
1717
1718 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1719 /*ArraySize=*/nullptr, "zero");
1720 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1721 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1722 ZeroAlloca->getName() + ".ascast");
1723
1724 Value *ArgsAlloca = nullptr;
1725 if (UseArgStruct) {
1726 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1727 /*ArraySize=*/nullptr, "global_args");
1728 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1729 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1730 ArgsAlloca->getName() + ".ascast");
1731 }
1732
1733 // Initialization.
1734 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1735 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1736 if (UseArgStruct) {
1737 Builder.CreateCall(
1738 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1739 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1740 {ArgsAlloca});
1741 }
1742
1743 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1744
1745 // Load structArg from global_args.
1746 if (UseArgStruct) {
1747 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1748 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1749 {Builder.getInt64(0)});
1750 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1751 Args.push_back(StructArg);
1752 }
1753
1754 // Call the outlined function holding the parallel body.
1755 Builder.CreateCall(&OutlinedFn, Args);
1756 Builder.CreateRetVoid();
1757
1758 return WrapperFn;
1759}
1760
1761// Callback used to create OpenMP runtime calls to support
1762// omp parallel clause for the device.
1763// We need to use this callback to replace call to the OutlinedFn in OuterFn
1764// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1766 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1767 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1768 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1769 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1770 assert(OutlinedFn.arg_size() >= 2 &&
1771 "Expected at least tid and bounded tid as arguments");
1772 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1773
1774 // Add some known attributes.
1775 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1776 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1777 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1778 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1779 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1780 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1781
1782 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1783 assert(CI && "Expected call instruction to outlined function");
1784 CI->getParent()->setName("omp_parallel");
1785
1786 Builder.SetInsertPoint(CI);
1787 Type *PtrTy = OMPIRBuilder->VoidPtr;
1788
1789 // Add alloca for kernel args
1790 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1791 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1792 AllocaInst *ArgsAlloca =
1793 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1794 Value *Args = ArgsAlloca;
1795 // Add address space cast if array for storing arguments is not allocated
1796 // in address space 0
1797 if (ArgsAlloca->getAddressSpace())
1798 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1799 Builder.restoreIP(CurrentIP);
1800
1801 // Store captured vars which are used by kmpc_parallel_60
1802 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1803 Value *V = *(CI->arg_begin() + 2 + Idx);
1804 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1805 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1806 Builder.CreateStore(V, StoreAddress);
1807 }
1808
1809 Value *Cond =
1810 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1811 : Builder.getInt32(1);
1812 Value *NumThreadsArg =
1813 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1814 : Builder.getInt32(-1);
1815
1816 // If this is not a Generic kernel, we can skip generating the wrapper.
1817 Value *WrapperFn;
1818 if (isGenericKernel(*OuterFn))
1819 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1820 else
1821 WrapperFn = Constant::getNullValue(PtrTy);
1822
1823 // Build kmpc_parallel_60 call
1824 Value *Parallel60CallArgs[] = {
1825 /* identifier*/ Ident,
1826 /* global thread num*/ ThreadID,
1827 /* if expression */ Cond,
1828 /* number of threads */ NumThreadsArg,
1829 /* Proc bind */ Builder.getInt32(-1),
1830 /* outlined function */ &OutlinedFn,
1831 /* wrapper function */ WrapperFn,
1832 /* arguments of the outlined funciton*/ Args,
1833 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1834 /* strict for number of threads */ Builder.getInt32(0)};
1835
1836 FunctionCallee RTLFn =
1837 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1838
1839 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1840
1841 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1842 << *Builder.GetInsertBlock()->getParent() << "\n");
1843
1844 // Initialize the local TID stack location with the argument value.
1845 Builder.SetInsertPoint(PrivTID);
1846 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1847 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1848 PrivTIDAddr);
1849
1850 // Remove redundant call to the outlined function.
1851 CI->eraseFromParent();
1852
1853 for (Instruction *I : ToBeDeleted) {
1854 I->eraseFromParent();
1855 }
1856}
1857
1858// Callback used to create OpenMP runtime calls to support
1859// omp parallel clause for the host.
1860// We need to use this callback to replace call to the OutlinedFn in OuterFn
1861// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1862static void
1864 Function *OuterFn, Value *Ident, Value *IfCondition,
1865 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1866 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1867 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1868 FunctionCallee RTLFn;
1869 if (IfCondition) {
1870 RTLFn =
1871 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1872 } else {
1873 RTLFn =
1874 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1875 }
1876 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1877 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1878 LLVMContext &Ctx = F->getContext();
1879 MDBuilder MDB(Ctx);
1880 // Annotate the callback behavior of the __kmpc_fork_call:
1881 // - The callback callee is argument number 2 (microtask).
1882 // - The first two arguments of the callback callee are unknown (-1).
1883 // - All variadic arguments to the __kmpc_fork_call are passed to the
1884 // callback callee.
1885 F->addMetadata(LLVMContext::MD_callback,
1887 2, {-1, -1},
1888 /* VarArgsArePassed */ true)}));
1889 }
1890 }
1891 // Add some known attributes.
1892 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1893 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1894 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1895
1896 assert(OutlinedFn.arg_size() >= 2 &&
1897 "Expected at least tid and bounded tid as arguments");
1898 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1899
1900 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1901 CI->getParent()->setName("omp_parallel");
1902 Builder.SetInsertPoint(CI);
1903
1904 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1905 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1906 &OutlinedFn};
1907
1908 SmallVector<Value *, 16> RealArgs;
1909 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1910 if (IfCondition) {
1911 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1912 RealArgs.push_back(Cond);
1913 }
1914 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1915
1916 // __kmpc_fork_call_if always expects a void ptr as the last argument
1917 // If there are no arguments, pass a null pointer.
1918 auto PtrTy = OMPIRBuilder->VoidPtr;
1919 if (IfCondition && NumCapturedVars == 0) {
1920 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1921 RealArgs.push_back(NullPtrValue);
1922 }
1923
1924 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1925
1926 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1927 << *Builder.GetInsertBlock()->getParent() << "\n");
1928
1929 // Initialize the local TID stack location with the argument value.
1930 Builder.SetInsertPoint(PrivTID);
1931 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1932 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1933 PrivTIDAddr);
1934
1935 // Remove redundant call to the outlined function.
1936 CI->eraseFromParent();
1937
1938 for (Instruction *I : ToBeDeleted) {
1939 I->eraseFromParent();
1940 }
1941}
1942
1944 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1945 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1946 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1947 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1948 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1949
1950 if (!updateToLocation(Loc))
1951 return Loc.IP;
1952
1953 uint32_t SrcLocStrSize;
1954 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1955 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1956 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1957 (ProcBind != OMP_PROC_BIND_default);
1958 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1959 // If we generate code for the target device, we need to allocate
1960 // struct for aggregate params in the device default alloca address space.
1961 // OpenMP runtime requires that the params of the extracted functions are
1962 // passed as zero address space pointers. This flag ensures that extracted
1963 // function arguments are declared in zero address space
1964 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1965
1966 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1967 // only if we compile for host side.
1968 if (NumThreads && !Config.isTargetDevice()) {
1969 Value *Args[] = {
1970 Ident, ThreadID,
1971 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1973 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1974 }
1975
1976 if (ProcBind != OMP_PROC_BIND_default) {
1977 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1978 Value *Args[] = {
1979 Ident, ThreadID,
1980 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1982 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1983 }
1984
1985 BasicBlock *InsertBB = Builder.GetInsertBlock();
1986 Function *OuterFn = InsertBB->getParent();
1987
1988 // Save the outer alloca block because the insertion iterator may get
1989 // invalidated and we still need this later.
1990 BasicBlock *OuterAllocaBlock = OuterAllocIP.getNodeParent();
1991
1992 // Vector to remember instructions we used only during the modeling but which
1993 // we want to delete at the end.
1995
1996 // Change the location to the outer alloca insertion point to create and
1997 // initialize the allocas we pass into the parallel region.
1998 InsertPointTy NewOuter(OuterAllocaBlock->begin());
1999 Builder.restoreIP(NewOuter);
2000 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
2001 AllocaInst *ZeroAddrAlloca =
2002 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
2003 Instruction *TIDAddr = TIDAddrAlloca;
2004 Instruction *ZeroAddr = ZeroAddrAlloca;
2005 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
2006 // Add additional casts to enforce pointers in zero address space
2007 TIDAddr = new AddrSpaceCastInst(
2008 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2009 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2010 ToBeDeleted.push_back(TIDAddr);
2011 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2012 PointerType ::get(M.getContext(), 0),
2013 "zero.addr.ascast");
2014 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2015 ToBeDeleted.push_back(ZeroAddr);
2016 }
2017
2018 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2019 // associated arguments in the outlined function, so we delete them later.
2020 ToBeDeleted.push_back(TIDAddrAlloca);
2021 ToBeDeleted.push_back(ZeroAddrAlloca);
2022
2023 // Create an artificial insertion point that will also ensure the blocks we
2024 // are about to split are not degenerated.
2025 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2026
2027 BasicBlock *EntryBB = UI->getParent();
2028 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2029 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2030 BasicBlock *PRegPreFiniBB =
2031 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2032 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2033
2034 auto FiniCBWrapper = [&](InsertPointTy IP) {
2035 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2036 // target to the region exit block.
2037 if (IP == IP.getNodeParent()->end()) {
2039 Builder.restoreIP(IP);
2040 Instruction *I = Builder.CreateBr(PRegExitBB);
2041 IP = I->getIterator();
2042 }
2043 assert(IP.getNodeParent()->getTerminator()->getNumSuccessors() == 1 &&
2044 IP.getNodeParent()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2045 "Unexpected insertion point for finalization call!");
2046 return FiniCB(IP);
2047 };
2048
2049 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2050
2051 // Generate the privatization allocas in the block that will become the entry
2052 // of the outlined function.
2053 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2054 InsertPointTy InnerAllocaIP = Builder.saveIP();
2055
2056 AllocaInst *PrivTIDAddr =
2057 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2058 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2059
2060 // Add some fake uses for OpenMP provided arguments.
2061 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2062 Instruction *ZeroAddrUse =
2063 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2064 ToBeDeleted.push_back(ZeroAddrUse);
2065
2066 // EntryBB
2067 // |
2068 // V
2069 // PRegionEntryBB <- Privatization allocas are placed here.
2070 // |
2071 // V
2072 // PRegionBodyBB <- BodeGen is invoked here.
2073 // |
2074 // V
2075 // PRegPreFiniBB <- The block we will start finalization from.
2076 // |
2077 // V
2078 // PRegionExitBB <- A common exit to simplify block collection.
2079 //
2080
2081 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2082
2083 // Let the caller create the body.
2084 assert(BodyGenCB && "Expected body generation callback!");
2085 InsertPointTy CodeGenIP(PRegBodyBB->begin());
2086 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2087 return Err;
2088
2089 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2090
2091 // If OuterFn is a Generic kernel, we need to use device shared memory to
2092 // allocate argument structures. Otherwise, we use stack allocations as usual.
2093 bool UsesDeviceSharedMemory =
2094 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2095 std::unique_ptr<OutlineInfo> OI =
2096 UsesDeviceSharedMemory
2097 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2098 : std::make_unique<OutlineInfo>();
2099
2100 if (Config.isTargetDevice()) {
2101 // Generate OpenMP target specific runtime call
2102 OI->PostOutlineCB = [=, ToBeDeletedVec =
2103 std::move(ToBeDeleted)](Function &OutlinedFn) {
2104 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2105 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2106 ThreadID, ToBeDeletedVec);
2107 };
2108 } else {
2109 // Generate OpenMP host runtime call
2110 OI->PostOutlineCB = [=, ToBeDeletedVec =
2111 std::move(ToBeDeleted)](Function &OutlinedFn) {
2112 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2113 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2114 };
2115 }
2116
2117 OI->FixUpNonEntryAllocas = true;
2118 OI->OuterAllocBB = OuterAllocaBlock;
2119 OI->EntryBB = PRegEntryBB;
2120 OI->ExitBB = PRegExitBB;
2121 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2122 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2123
2124 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2126 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2127
2128 CodeExtractorAnalysisCache CEAC(*OuterFn);
2129 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2130 /* AggregateArgs */ false,
2131 /* BlockFrequencyInfo */ nullptr,
2132 /* BranchProbabilityInfo */ nullptr,
2133 /* AssumptionCache */ nullptr,
2134 /* AllowVarArgs */ true,
2135 /* AllowAlloca */ true,
2136 /* AllocationBlock */ OuterAllocaBlock,
2137 /* DeallocationBlocks */ {},
2138 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2139
2140 // Find inputs to, outputs from the code region.
2141 BasicBlock *CommonExit = nullptr;
2142 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2143 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2144
2145 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2146 /*CollectGlobalInputs=*/true);
2147
2148 Inputs.remove_if([&](Value *I) {
2150 return GV->getValueType() == OpenMPIRBuilder::Ident;
2151
2152 return false;
2153 });
2154
2155 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2156
2157 FunctionCallee TIDRTLFn =
2158 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2159
2160 auto PrivHelper = [&](Value &V) -> Error {
2161 if (&V == TIDAddr || &V == ZeroAddr) {
2162 OI->ExcludeArgsFromAggregate.push_back(&V);
2163 return Error::success();
2164 }
2165
2167 for (Use &U : V.uses())
2168 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2169 if (ParallelRegionBlockSet.count(UserI->getParent()))
2170 Uses.insert(&U);
2171
2172 // __kmpc_fork_call expects extra arguments as pointers. If the input
2173 // already has a pointer type, everything is fine. Otherwise, store the
2174 // value onto stack and load it back inside the to-be-outlined region. This
2175 // will ensure only the pointer will be passed to the function.
2176 // FIXME: if there are more than 15 trailing arguments, they must be
2177 // additionally packed in a struct.
2178 Value *Inner = &V;
2179 if (!V.getType()->isPointerTy()) {
2181 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2182
2183 Builder.restoreIP(OuterAllocIP);
2184 Value *Ptr;
2185 if (UsesDeviceSharedMemory) {
2186 // Use device shared memory instead, if needed.
2187 Ptr = createOMPAllocShared(Builder, V.getType(),
2188 V.getName() + ".reloaded");
2189 for (BasicBlock *DeallocBlock : OuterDeallocBlocks) {
2190 assert(DeallocBlock->getParent() ==
2191 OuterAllocIP.getNodeParent()->getParent() &&
2192 "Dealloc block must be in the allocation's function to reuse "
2193 "its debug location");
2194 createOMPFreeShared({DeallocBlock->getFirstInsertionPt(),
2195 Builder.getCurrentDebugLocation()},
2196 Ptr, V.getType());
2197 }
2198 } else {
2199 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2200 V.getName() + ".reloaded");
2201 }
2202
2203 // Store to stack at end of the block that currently branches to the entry
2204 // block of the to-be-outlined region.
2205 Builder.SetInsertPoint(InsertBB,
2206 InsertBB->getTerminator()->getIterator());
2207 Builder.CreateStore(&V, Ptr);
2208
2209 // Load back next to allocations in the to-be-outlined region.
2210 Builder.restoreIP(InnerAllocaIP);
2211 Inner = Builder.CreateLoad(V.getType(), Ptr);
2212 }
2213
2214 Value *ReplacementValue = nullptr;
2215 CallInst *CI = dyn_cast<CallInst>(&V);
2216 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2217 ReplacementValue = PrivTID;
2218 } else {
2219 InsertPointOrErrorTy AfterIP =
2220 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2221 if (!AfterIP)
2222 return AfterIP.takeError();
2223 Builder.restoreIP(*AfterIP);
2224 InnerAllocaIP =
2225 InnerAllocaIP.getNodeParent()->getTerminator()->getIterator();
2226
2227 assert(ReplacementValue &&
2228 "Expected copy/create callback to set replacement value!");
2229 if (ReplacementValue == &V)
2230 return Error::success();
2231 }
2232
2233 for (Use *UPtr : Uses)
2234 UPtr->set(ReplacementValue);
2235
2236 return Error::success();
2237 };
2238
2239 // Reset the inner alloca insertion as it will be used for loading the values
2240 // wrapped into pointers before passing them into the to-be-outlined region.
2241 // Configure it to insert immediately after the fake use of zero address so
2242 // that they are available in the generated body and so that the
2243 // OpenMP-related values (thread ID and zero address pointers) remain leading
2244 // in the argument list.
2245 InnerAllocaIP = ZeroAddrUse->getNextNode()->getIterator();
2246
2247 // Reset the outer alloca insertion point to the entry of the relevant block
2248 // in case it was invalidated.
2249 OuterAllocIP = OuterAllocaBlock->getFirstInsertionPt();
2250
2251 for (Value *Input : Inputs) {
2252 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2253 if (Error Err = PrivHelper(*Input))
2254 return Err;
2255 }
2256 LLVM_DEBUG({
2257 for (Value *Output : Outputs)
2258 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2259 });
2260 assert(Outputs.empty() &&
2261 "OpenMP outlining should not produce live-out values!");
2262
2263 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2264 LLVM_DEBUG({
2265 for (auto *BB : Blocks)
2266 dbgs() << " PBR: " << BB->getName() << "\n";
2267 });
2268
2269 // Adjust the finalization stack, verify the adjustment, and call the
2270 // finalize function a last time to finalize values between the pre-fini
2271 // block and the exit block if we left the parallel "the normal way".
2272 auto FiniInfo = FinalizationStack.pop_back_val();
2273 (void)FiniInfo;
2274 assert(FiniInfo.DK == OMPD_parallel &&
2275 "Unexpected finalization stack state!");
2276
2277 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2278
2279 InsertPointTy PreFiniIP(PRegPreFiniTI->getIterator());
2280 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2281 if (!FiniBBOrErr)
2282 return FiniBBOrErr.takeError();
2283 {
2285 Builder.restoreIP(PreFiniIP);
2286 Builder.CreateBr(*FiniBBOrErr);
2287 // There's currently a branch to omp.par.exit. Delete it. We will get there
2288 // via the fini block
2289 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2290 Term->eraseFromParent();
2291 }
2292
2293 // Register the outlined info.
2294 addOutlineInfo(std::move(OI));
2295
2296 InsertPointTy AfterIP(UI->getParent()->end());
2297 UI->eraseFromParent();
2298
2299 return AfterIP;
2300}
2301
2303 // Build call void __kmpc_flush(ident_t *loc)
2304 uint32_t SrcLocStrSize;
2305 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2306 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2307
2309 Args);
2310}
2311
2313 if (!updateToLocation(Loc))
2314 return;
2315 emitFlush(Loc);
2316}
2317
2319 Value *Message) {
2320 if (!updateToLocation(Loc))
2321 return;
2322
2323 // Build call void __kmpc_error(ident_t *loc, int severity,
2324 // const char *message)
2325 uint32_t SrcLocStrSize;
2326 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2327 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2328 // Severity: 1 = warning, 2 = fatal.
2329 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2330 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2331 Value *Args[] = {Ident, Severity, MessageArg};
2332
2334 Args);
2335}
2336
2338 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2339 uint32_t SrcLocStrSize;
2340 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2341 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2342 Constant *I32Null = ConstantInt::getNullValue(Int32);
2343 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2344
2346 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2347}
2348
2354
2356 const DependData &Dep) {
2357 // Store the pointer to the variable
2358 Value *Addr = Builder.CreateStructGEP(
2359 DependInfo, Entry,
2360 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2361 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2362 Builder.CreateStore(DepValPtr, Addr);
2363 // Store the size of the variable
2364 Value *Size = Builder.CreateStructGEP(
2365 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2366 Builder.CreateStore(
2367 ConstantInt::get(SizeTy,
2368 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2369 Size);
2370 // Store the dependency kind
2371 Value *Flags = Builder.CreateStructGEP(
2372 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2373 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2374 static_cast<unsigned int>(Dep.DepKind)),
2375 Flags);
2376}
2377
2378// Processes the dependencies in Dependencies and does the following
2379// - Allocates space on the stack of an array of DependInfo objects
2380// - Populates each DependInfo object with relevant information of
2381// the corresponding dependence.
2382// - All code is inserted in the entry block of the current function.
2384 OpenMPIRBuilder &OMPBuilder,
2386 // Early return if we have no dependencies to process
2387 if (Dependencies.empty())
2388 return nullptr;
2389
2390 // Given a vector of DependData objects, in this function we create an
2391 // array on the stack that holds kmp_depend_info objects corresponding
2392 // to each dependency. This is then passed to the OpenMP runtime.
2393 // For example, if there are 'n' dependencies then the following psedo
2394 // code is generated. Assume the first dependence is on a variable 'a'
2395 //
2396 // \code{c}
2397 // DepArray = alloc(n x sizeof(kmp_depend_info);
2398 // idx = 0;
2399 // DepArray[idx].base_addr = ptrtoint(&a);
2400 // DepArray[idx].len = 8;
2401 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2402 // ++idx;
2403 // DepArray[idx].base_addr = ...;
2404 // \endcode
2405
2406 IRBuilderBase &Builder = OMPBuilder.Builder;
2407 Type *DependInfo = OMPBuilder.DependInfo;
2408
2409 Value *DepArray = nullptr;
2410 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2411 {
2412 // Use a InsertPointGuard to restore the location back along with the
2413 // insertion point.
2414 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2415 Builder.SetInsertPoint(
2416 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2417 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2418 }
2419
2420 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2421 Value *Base =
2422 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2423 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2424 }
2425 return DepArray;
2426}
2427
2429 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2430 // global_tid);
2431 uint32_t SrcLocStrSize;
2432 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2433 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2434 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2435
2436 // Ignore return result until untied tasks are supported.
2438 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2439}
2440
2442 DependenciesInfo Dependencies,
2443 bool IsNowait) {
2444 if (!updateToLocation(Loc))
2445 return;
2446
2447 Value *DepArray = nullptr;
2448 Type *DepArrayTy = nullptr;
2449 Value *NumDeps = nullptr;
2450 if (Dependencies.DepArray) {
2451 DepArray = Dependencies.DepArray;
2452 NumDeps = Dependencies.NumDeps;
2453 } else if (!Dependencies.Deps.empty()) {
2454 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2455 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2456 {
2458 BasicBlock &entryBB =
2459 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2460 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2461 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2462 }
2463
2464 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2465 Value *Base =
2466 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2467 this->emitTaskDependency(Builder, Base, Dep);
2468 }
2469 }
2470
2471 if (DepArray) {
2472 uint32_t SrcLocStrSize;
2473 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2474 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2475 Value *Args[] = {
2476 Ident,
2477 getOrCreateThreadID(Ident),
2478 NumDeps,
2479 DepArray,
2480 ConstantInt::get(Builder.getInt32Ty(), 0),
2482 ConstantInt::get(Builder.getInt32Ty(), IsNowait)};
2485 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2486 Args);
2487 } else {
2489 }
2490}
2491
2492/// Create the task duplication function passed to kmpc_taskloop.
2493Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2494 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2495 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2496 if (!DupCB)
2498 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2499
2500 // From OpenMP Runtime p_task_dup_t:
2501 // Routine optionally generated by the compiler for setting the lastprivate
2502 // flag and calling needed constructors for private/firstprivate objects (used
2503 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2504 // lastprivate flag.
2505 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2506
2507 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2508
2509 FunctionType *DupFuncTy = FunctionType::get(
2510 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2511 /*isVarArg=*/false);
2512
2513 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2514 "omp_taskloop_dup", M);
2515 Value *DestTaskArg = DupFunction->getArg(0);
2516 Value *SrcTaskArg = DupFunction->getArg(1);
2517 Value *LastprivateFlagArg = DupFunction->getArg(2);
2518 DestTaskArg->setName("dest_task");
2519 SrcTaskArg->setName("src_task");
2520 LastprivateFlagArg->setName("lastprivate_flag");
2521
2522 IRBuilderBase::InsertPointGuard Guard(Builder);
2523 Builder.SetInsertPoint(
2524 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2525
2526 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2527 Type *TaskWithPrivatesTy =
2528 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2529 Value *TaskPrivates = Builder.CreateGEP(
2530 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2531 Value *ContextPtr = Builder.CreateGEP(
2532 PrivatesTy, TaskPrivates,
2533 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2534 return ContextPtr;
2535 };
2536
2537 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2538 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2539
2540 DestTaskContextPtr->setName("destPtr");
2541 SrcTaskContextPtr->setName("srcPtr");
2542
2543 InsertPointTy AllocaIP(DupFunction->getEntryBlock().begin());
2544 InsertPointTy CodeGenIP = Builder.saveIP();
2545 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2546 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2547 if (!AfterIPOrError)
2548 return AfterIPOrError.takeError();
2549 Builder.restoreIP(*AfterIPOrError);
2550
2551 Builder.CreateRetVoid();
2552
2553 return DupFunction;
2554}
2555
2556OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2557 const LocationDescription &Loc, InsertPointTy AllocaIP,
2558 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2559 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2560 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2561 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2562 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2563 Value *TaskContextStructPtrVal, bool FreeAgent) {
2564
2565 if (!updateToLocation(Loc))
2566 return InsertPointTy();
2567
2568 uint32_t SrcLocStrSize;
2569 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2570 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2571
2572 BasicBlock *TaskloopExitBB =
2573 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2574 BasicBlock *TaskloopBodyBB =
2575 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2576 BasicBlock *TaskloopAllocaBB =
2577 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2578
2579 InsertPointTy TaskloopAllocaIP = TaskloopAllocaBB->begin();
2580 InsertPointTy TaskloopBodyIP = TaskloopBodyBB->begin();
2581
2582 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2583 return Err;
2584
2585 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2586 if (!result) {
2587 return result.takeError();
2588 }
2589
2590 llvm::CanonicalLoopInfo *CLI = result.get();
2591 auto OI = std::make_unique<OutlineInfo>();
2592 OI->EntryBB = TaskloopAllocaBB;
2593 OI->OuterAllocBB = AllocaIP.getNodeParent();
2594 OI->ExitBB = TaskloopExitBB;
2595 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2596 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2597
2598 // Add the thread ID argument.
2599 SmallVector<Instruction *> ToBeDeleted;
2600 // dummy instruction to be used as a fake argument
2601 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2602 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2603 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2604 TaskloopAllocaIP, "lb", false, true);
2605 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2606 TaskloopAllocaIP, "ub", false, true);
2607 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2608 TaskloopAllocaIP, "step", false, true);
2609 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2610 // aggregate struct
2611 OI->Inputs.insert(FakeLB);
2612 OI->Inputs.insert(FakeUB);
2613 OI->Inputs.insert(FakeStep);
2614 if (TaskContextStructPtrVal)
2615 OI->Inputs.insert(TaskContextStructPtrVal);
2616 assert(((TaskContextStructPtrVal && DupCB) ||
2617 (!TaskContextStructPtrVal && !DupCB)) &&
2618 "Task context struct ptr and duplication callback must be both set "
2619 "or both null");
2620
2621 // It isn't safe to run the duplication bodygen callback inside the post
2622 // outlining callback so this has to be run now before we know the real task
2623 // shareds structure type.
2624 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2625 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2626 Type *FakeSharedsTy = StructType::get(
2627 Builder.getContext(),
2628 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2629 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2630 FakeSharedsTy,
2631 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2632 if (!TaskDupFnOrErr) {
2633 return TaskDupFnOrErr.takeError();
2634 }
2635 Value *TaskDupFn = *TaskDupFnOrErr;
2636
2637 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2638 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2639 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2640 FakeSharedsTy, Final, Mergeable, Priority,
2641 NumOfCollapseLoops,
2642 FreeAgent](Function &OutlinedFn) mutable {
2643 // Replace the Stale CI by appropriate RTL function call.
2644 assert(OutlinedFn.hasOneUse() &&
2645 "there must be a single user for the outlined function");
2646 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2647
2648 /* Create the casting for the Bounds Values that can be used when outlining
2649 * to replace the uses of the fakes with real values */
2650 BasicBlock *CodeReplBB = StaleCI->getParent();
2651 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2652 Value *CastedLBVal =
2653 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2654 Value *CastedUBVal =
2655 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2656 Value *CastedStepVal =
2657 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2658
2659 Builder.SetInsertPoint(StaleCI);
2660
2661 // Gather the arguments for emitting the runtime call for
2662 // @__kmpc_omp_task_alloc
2663 Function *TaskAllocFn =
2664 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2665
2666 Value *ThreadID = getOrCreateThreadID(Ident);
2667
2668 if (!NoGroup) {
2669 // Emit runtime call for @__kmpc_taskgroup
2670 Function *TaskgroupFn =
2671 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2672 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2673 }
2674
2675 // `flags` Argument Configuration
2676 // Task is tied if (Flags & 1) == 1.
2677 // Task is untied if (Flags & 1) == 0.
2678 // Task is final if (Flags & 2) == 2.
2679 // Task is not final if (Flags & 2) == 0.
2680 // Task is mergeable if (Flags & 4) == 4.
2681 // Task is not mergeable if (Flags & 4) == 0.
2682 // Task is priority if (Flags & 32) == 32.
2683 // Task is not priority if (Flags & 32) == 0.
2684 // Task is free-agent eligible if (Flags & 128) == 128.
2685 // Task is not free-agent eligible if (Flags & 128) == 0.
2686 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2687 if (Final)
2688 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2689 if (Mergeable)
2690 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2691 if (Priority)
2692 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2693 if (FreeAgent)
2694 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2695
2696 Value *TaskSize = Builder.getInt64(
2697 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2698
2699 AllocaInst *ArgStructAlloca =
2701 assert(ArgStructAlloca &&
2702 "Unable to find the alloca instruction corresponding to arguments "
2703 "for extracted function");
2704 std::optional<TypeSize> ArgAllocSize =
2705 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2706 assert(ArgAllocSize &&
2707 "Unable to determine size of arguments for extracted function");
2708 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2709
2710 // Emit the @__kmpc_omp_task_alloc runtime call
2711 // The runtime call returns a pointer to an area where the task captured
2712 // variables must be copied before the task is run (TaskData)
2713 CallInst *TaskData = Builder.CreateCall(
2714 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2715 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2716 /*task_func=*/&OutlinedFn});
2717
2718 Value *Shareds = StaleCI->getArgOperand(1);
2719 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2720 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2721 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2722 SharedsSize);
2723 // Get the pointer to loop lb, ub, step from task ptr
2724 // and set up the lowerbound,upperbound and step values
2725 llvm::Value *Lb = Builder.CreateGEP(
2726 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2727
2728 llvm::Value *Ub = Builder.CreateGEP(
2729 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2730
2731 llvm::Value *Step = Builder.CreateGEP(
2732 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2733 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2734
2735 // set up the arguments for emitting kmpc_taskloop runtime call
2736 // setting values for ifval, nogroup, sched, grainsize, task_dup
2737 Value *IfCondVal =
2738 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2739 : Builder.getInt32(1);
2740 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2741 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2742 Value *NoGroupVal = Builder.getInt32(1);
2743 Value *SchedVal = Builder.getInt32(Sched);
2744 Value *GrainSizeVal =
2745 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2746 : Builder.getInt64(0);
2747 Value *TaskDup = TaskDupFn;
2748
2749 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2750 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2751
2752 // taskloop runtime call
2753 Function *TaskloopFn =
2754 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2755 Builder.CreateCall(TaskloopFn, Args);
2756
2757 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2758 // nogroup is not defined
2759 if (!NoGroup) {
2760 Function *EndTaskgroupFn =
2761 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2762 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2763 }
2764
2765 StaleCI->eraseFromParent();
2766
2767 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2768
2769 LoadInst *SharedsOutlined =
2770 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2771 OutlinedFn.getArg(1)->replaceUsesWithIf(
2772 SharedsOutlined,
2773 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2774
2775 Value *IV = CLI->getIndVar();
2776 Type *IVTy = IV->getType();
2777 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2778
2779 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2780 // UpperBound. These GEP's can be reused for loading the tasks respective
2781 // bounds.
2782 Value *TaskLB = nullptr;
2783 Value *TaskUB = nullptr;
2784 Value *TaskStep = nullptr;
2785 Value *LoadTaskLB = nullptr;
2786 Value *LoadTaskUB = nullptr;
2787 Value *LoadTaskStep = nullptr;
2788 for (Instruction &I : *TaskloopAllocaBB) {
2789 if (I.getOpcode() == Instruction::GetElementPtr) {
2790 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2791 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2792 switch (CI->getZExtValue()) {
2793 case 0:
2794 TaskLB = &I;
2795 break;
2796 case 1:
2797 TaskUB = &I;
2798 break;
2799 case 2:
2800 TaskStep = &I;
2801 break;
2802 }
2803 }
2804 } else if (I.getOpcode() == Instruction::Load) {
2805 LoadInst &Load = cast<LoadInst>(I);
2806 if (Load.getPointerOperand() == TaskLB) {
2807 assert(TaskLB != nullptr && "Expected value for TaskLB");
2808 LoadTaskLB = &I;
2809 } else if (Load.getPointerOperand() == TaskUB) {
2810 assert(TaskUB != nullptr && "Expected value for TaskUB");
2811 LoadTaskUB = &I;
2812 } else if (Load.getPointerOperand() == TaskStep) {
2813 assert(TaskStep != nullptr && "Expected value for TaskStep");
2814 LoadTaskStep = &I;
2815 }
2816 }
2817 }
2818
2819 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2820
2821 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2822 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2823 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2824 Value *TripCountMinusOne = Builder.CreateSDiv(
2825 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2826 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2827 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2828 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2829 // set the trip count in the CLI
2830 CLI->setTripCount(CastedTripCount);
2831
2832 Builder.SetInsertPoint(CLI->getBody(),
2833 CLI->getBody()->getFirstInsertionPt());
2834
2835 if (NumOfCollapseLoops > 1) {
2836 llvm::SmallVector<User *> UsersToReplace;
2837 // When using the collapse clause, the bounds of the loop have to be
2838 // adjusted to properly represent the iterator of the outer loop.
2839 Value *IVPlusTaskLB = Builder.CreateAdd(
2840 CLI->getIndVar(),
2841 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2842 // To ensure every Use is correctly captured, we first want to record
2843 // which users to replace the value in, and then replace the value.
2844 for (auto IVUse = CLI->getIndVar()->uses().begin();
2845 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2846 User *IVUser = IVUse->getUser();
2847 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2848 if (Op->getOpcode() == Instruction::URem ||
2849 Op->getOpcode() == Instruction::UDiv) {
2850 UsersToReplace.push_back(IVUser);
2851 }
2852 }
2853 }
2854 for (User *User : UsersToReplace) {
2855 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2856 }
2857 } else {
2858 // The canonical loop is generated with a fixed lower bound. We need to
2859 // update the index calculation code to use the task's lower bound. The
2860 // generated code looks like this:
2861 // %omp_loop.iv = phi ...
2862 // ...
2863 // %tmp = mul [type] %omp_loop.iv, step
2864 // %user_index = add [type] tmp, lb
2865 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2866 // of the normalised induction variable:
2867 // 1. This one: converting the normalised IV to the user IV
2868 // 2. The increment (add)
2869 // 3. The comparison against the trip count (icmp)
2870 // (1) is the only use that is a mul followed by an add so this cannot
2871 // match other IR.
2872 assert(CLI->getIndVar()->getNumUses() == 3 &&
2873 "Canonical loop should have exactly three uses of the ind var");
2874 for (User *IVUser : CLI->getIndVar()->users()) {
2875 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2876 if (Mul->getOpcode() == Instruction::Mul) {
2877 for (User *MulUser : Mul->users()) {
2878 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2879 if (Add->getOpcode() == Instruction::Add) {
2880 Add->setOperand(1, CastedTaskLB);
2881 }
2882 }
2883 }
2884 }
2885 }
2886 }
2887 }
2888
2889 FakeLB->replaceAllUsesWith(CastedLBVal);
2890 FakeUB->replaceAllUsesWith(CastedUBVal);
2891 FakeStep->replaceAllUsesWith(CastedStepVal);
2892 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2893 I->eraseFromParent();
2894 }
2895 };
2896
2897 addOutlineInfo(std::move(OI));
2898 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2899 return Builder.saveIP();
2900}
2901
2904 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2906 llvm::Type::getInt32Ty(M.getContext()));
2907}
2908
2910 const LocationDescription &Loc, InsertPointTy AllocaIP,
2911 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2912 bool Tied, Value *Final, Value *IfCondition,
2913 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2914 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2915
2916 if (!updateToLocation(Loc))
2917 return InsertPointTy();
2918
2919 uint32_t SrcLocStrSize;
2920 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2921 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2922 // The current basic block is split into four basic blocks. After outlining,
2923 // they will be mapped as follows:
2924 // ```
2925 // def current_fn() {
2926 // current_basic_block:
2927 // br label %task.exit
2928 // task.exit:
2929 // ; instructions after task
2930 // }
2931 // def outlined_fn() {
2932 // task.alloca:
2933 // br label %task.body
2934 // task.body:
2935 // ret void
2936 // }
2937 // ```
2938 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2939 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2940 BasicBlock *TaskAllocaBB =
2941 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2942
2943 InsertPointTy TaskAllocaIP = TaskAllocaBB->begin();
2944 InsertPointTy TaskBodyIP = TaskBodyBB->begin();
2945 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2946 return Err;
2947
2948 auto OI = std::make_unique<OutlineInfo>();
2949 OI->EntryBB = TaskAllocaBB;
2950 OI->OuterAllocBB = AllocaIP.getNodeParent();
2951 OI->ExitBB = TaskExitBB;
2952 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2953 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2954
2955 // Add the thread ID argument.
2957 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2958 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2959
2960 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2961 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2962 TaskAllocaBB,
2963 ToBeDeleted](Function &OutlinedFn) mutable {
2964 // Replace the Stale CI by appropriate RTL function call.
2965 assert(OutlinedFn.hasOneUse() &&
2966 "there must be a single user for the outlined function");
2967 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2968
2969 // HasShareds is true if any variables are captured in the outlined region,
2970 // false otherwise.
2971 bool HasShareds = StaleCI->arg_size() > 1;
2972 Builder.SetInsertPoint(StaleCI);
2973
2974 // Gather the arguments for emitting the runtime call for
2975 // @__kmpc_omp_task_alloc
2976 Function *TaskAllocFn =
2977 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2978
2979 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2980 // call.
2981 Value *ThreadID = getOrCreateThreadID(Ident);
2982
2983 // Argument - `flags`
2984 // Task is tied iff (Flags & 1) == 1.
2985 // Task is untied iff (Flags & 1) == 0.
2986 // Task is final iff (Flags & 2) == 2.
2987 // Task is not final iff (Flags & 2) == 0.
2988 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2989 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2990 // Task is detachable iff (Flags & 64) == 64.
2991 // Task is not detachable iff (Flags & 64) == 0.
2992 // Task is priority iff (Flags & 32) == 32.
2993 // Task is not priority iff (Flags & 32) == 0.
2994 // Task is free-agent eligible iff (Flags & 128) == 128.
2995 // Task is not free-agent eligible iff (Flags & 128) == 0.
2996 // TODO: Handle the other flags.
2997 Value *Flags = Builder.getInt32(Tied);
2998 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2999 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
3000 if (Final) {
3001 Value *FinalFlag =
3002 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3003 Flags = Builder.CreateOr(FinalFlag, Flags);
3004 }
3005
3006 if (Mergeable || UseMergedIf0Path)
3007 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3008 if (EventHandle)
3009 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3010 if (Priority)
3011 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3012 if (FreeAgent)
3013 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3014
3015 // Argument - `sizeof_kmp_task_t` (TaskSize)
3016 // Tasksize refers to the size in bytes of kmp_task_t data structure
3017 // including private vars accessed in task.
3018 // TODO: add kmp_task_t_with_privates (privates)
3019 Value *TaskSize = Builder.getInt64(
3020 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3021
3022 // Argument - `sizeof_shareds` (SharedsSize)
3023 // SharedsSize refers to the shareds array size in the kmp_task_t data
3024 // structure.
3025 Value *SharedsSize = Builder.getInt64(0);
3026 if (HasShareds) {
3027 AllocaInst *ArgStructAlloca =
3029 assert(ArgStructAlloca &&
3030 "Unable to find the alloca instruction corresponding to arguments "
3031 "for extracted function");
3032 std::optional<TypeSize> ArgAllocSize =
3033 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3034 assert(ArgAllocSize &&
3035 "Unable to determine size of arguments for extracted function");
3036 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3037 }
3038 // Emit the @__kmpc_omp_task_alloc runtime call
3039 // The runtime call returns a pointer to an area where the task captured
3040 // variables must be copied before the task is run (TaskData)
3042 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3043 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3044 /*task_func=*/&OutlinedFn});
3045
3046 if (Affinities.Count && Affinities.Info) {
3048 OMPRTL___kmpc_omp_reg_task_with_affinity);
3049
3050 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3051 Affinities.Count, Affinities.Info});
3052 }
3053
3054 // Emit detach clause initialization.
3055 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3056 // task_descriptor);
3057 if (EventHandle) {
3059 OMPRTL___kmpc_task_allow_completion_event);
3060 llvm::Value *EventVal =
3061 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3062 llvm::Value *EventHandleAddr =
3063 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3064 Builder.getPtrTy(0));
3065 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3066 Builder.CreateStore(EventVal, EventHandleAddr);
3067 }
3068 // Copy the arguments for outlined function
3069 if (HasShareds) {
3070 Value *Shareds = StaleCI->getArgOperand(1);
3071 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3072 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3073 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3074 SharedsSize);
3075 }
3076
3077 if (Priority) {
3078 //
3079 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3080 // we populate the priority information into the "kmp_task_t" here
3081 //
3082 // The struct "kmp_task_t" definition is available in kmp.h
3083 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3084 // data2 is used for priority
3085 //
3086 Type *Int32Ty = Builder.getInt32Ty();
3087 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3088 // kmp_task_t* => { ptr }
3089 Type *TaskPtr = StructType::get(VoidPtr);
3090 Value *TaskGEP =
3091 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3092 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3093 Type *TaskStructType = StructType::get(
3094 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3095 Value *PriorityData = Builder.CreateInBoundsGEP(
3096 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3097 // kmp_cmplrdata_t => { ptr, ptr }
3098 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3099 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3100 PriorityData, {Zero, Zero});
3101 Builder.CreateStore(Priority, CmplrData);
3102 }
3103
3104 Value *DepArray = nullptr;
3105 Value *NumDeps = nullptr;
3106 if (Dependencies.DepArray) {
3107 DepArray = Dependencies.DepArray;
3108 NumDeps = Dependencies.NumDeps;
3109 } else if (!Dependencies.Deps.empty()) {
3110 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3111 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3112 }
3113
3114 // In the presence of the `if` clause, the following IR is generated:
3115 // ...
3116 // %data = call @__kmpc_omp_task_alloc(...)
3117 // br i1 %if_condition, label %then, label %else
3118 // then:
3119 // call @__kmpc_omp_task(...)
3120 // br label %exit
3121 // else:
3122 // ;; Wait for resolution of dependencies, if any, before
3123 // ;; beginning the task
3124 // call @__kmpc_omp_wait_deps(...)
3125 // call @__kmpc_omp_task_begin_if0(...)
3126 // call @outlined_fn(...)
3127 // call @__kmpc_omp_task_complete_if0(...)
3128 // br label %exit
3129 // exit:
3130 // ...
3131 if (IfCondition && !UseMergedIf0Path) {
3132 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3133 // terminator.
3134 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3135 Instruction *IfTerminator =
3136 Builder.GetInsertPoint()->getParent()->getTerminator();
3137 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3138 Builder.SetInsertPoint(IfTerminator);
3139 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3140 &ElseTI);
3141 Builder.SetInsertPoint(ElseTI);
3142
3143 if (DepArray) {
3144 Function *TaskWaitFn =
3145 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3147 TaskWaitFn,
3148 {Ident, ThreadID, NumDeps, DepArray,
3149 ConstantInt::get(Builder.getInt32Ty(), 0),
3151 }
3152 Function *TaskBeginFn =
3153 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3154 Function *TaskCompleteFn =
3155 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3156 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3157 CallInst *CI = nullptr;
3158 if (HasShareds)
3159 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3160 else
3161 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3162 CI->setDebugLoc(StaleCI->getDebugLoc());
3163 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3164 Builder.SetInsertPoint(ThenTI);
3165 }
3166
3167 if (DepArray) {
3168 Function *TaskFn =
3169 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3171 TaskFn,
3172 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3173 ConstantInt::get(Builder.getInt32Ty(), 0),
3175
3176 } else {
3177 // Emit the @__kmpc_omp_task runtime call to spawn the task
3178 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3179 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3180 }
3181
3182 StaleCI->eraseFromParent();
3183
3184 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3185 if (HasShareds) {
3186 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3187 OutlinedFn.getArg(1)->replaceUsesWithIf(
3188 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3189 }
3190
3191 // The insert point may refer to one of the instructions about to be
3192 // deleted. It is not needed anymore so clear it instead of leaving it
3193 // dangling.
3194 Builder.ClearInsertionPoint();
3195 for (Instruction *I : llvm::reverse(ToBeDeleted))
3196 I->eraseFromParent();
3197 };
3198
3199 addOutlineInfo(std::move(OI));
3200 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3201
3202 return Builder.saveIP();
3203}
3204
3206 const LocationDescription &Loc, InsertPointTy AllocaIP,
3207 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3208 if (!updateToLocation(Loc))
3209 return InsertPointTy();
3210
3211 uint32_t SrcLocStrSize;
3212 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3213 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3214 Value *ThreadID = getOrCreateThreadID(Ident);
3215
3216 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3217 Function *TaskgroupFn =
3218 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3219 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3220
3221 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3222 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3223 return Err;
3224
3225 Builder.SetInsertPoint(TaskgroupExitBB);
3226 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3227 Function *EndTaskgroupFn =
3228 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3229 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3230
3231 return Builder.saveIP();
3232}
3233
3235 const LocationDescription &Loc, InsertPointTy AllocaIP,
3237 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3238 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3239
3240 if (!updateToLocation(Loc))
3241 return Loc.IP;
3242
3243 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3244
3245 // Each section is emitted as a switch case
3246 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3247 // -> OMP.createSection() which generates the IR for each section
3248 // Iterate through all sections and emit a switch construct:
3249 // switch (IV) {
3250 // case 0:
3251 // <SectionStmt[0]>;
3252 // break;
3253 // ...
3254 // case <NumSection> - 1:
3255 // <SectionStmt[<NumSection> - 1]>;
3256 // break;
3257 // }
3258 // ...
3259 // section_loop.after:
3260 // <FiniCB>;
3261 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3262 Builder.restoreIP(CodeGenIP);
3264 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3265 Function *CurFn = Continue->getParent();
3266 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3267
3268 unsigned CaseNumber = 0;
3269 for (auto SectionCB : SectionCBs) {
3271 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3272 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3273 Builder.SetInsertPoint(CaseBB);
3274 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3275 if (Error Err = SectionCB(InsertPointTy(), CaseEndBr->getIterator(), {}))
3276 return Err;
3277 CaseNumber++;
3278 }
3279 // remove the existing terminator from body BB since there can be no
3280 // terminators after switch/case
3281 return Error::success();
3282 };
3283 // Loop body ends here
3284 // LowerBound, UpperBound, and STride for createCanonicalLoop
3285 Type *I32Ty = Type::getInt32Ty(M.getContext());
3286 Value *LB = ConstantInt::get(I32Ty, 0);
3287 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3288 Value *ST = ConstantInt::get(I32Ty, 1);
3290 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3291 if (!LoopInfo)
3292 return LoopInfo.takeError();
3293
3294 InsertPointOrErrorTy WsloopIP =
3295 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3296 WorksharingLoopType::ForStaticLoop, !IsNowait);
3297 if (!WsloopIP)
3298 return WsloopIP.takeError();
3299 InsertPointTy AfterIP = *WsloopIP;
3300
3301 BasicBlock *LoopFini = AfterIP.getNodeParent()->getSinglePredecessor();
3302 assert(LoopFini && "Bad structure of static workshare loop finalization");
3303
3304 // Apply the finalization callback in LoopAfterBB
3305 auto FiniInfo = FinalizationStack.pop_back_val();
3306 assert(FiniInfo.DK == OMPD_sections &&
3307 "Unexpected finalization stack state!");
3308 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3309 return Err;
3310
3311 return AfterIP;
3312}
3313
3316 BodyGenCallbackTy BodyGenCB,
3317 FinalizeCallbackTy FiniCB) {
3318 if (!updateToLocation(Loc))
3319 return Loc.IP;
3320
3321 auto FiniCBWrapper = [&](InsertPointTy IP) {
3322 if (IP != IP.getNodeParent()->end())
3323 return FiniCB(IP);
3324 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3325 // will fail because that function requires the Finalization Basic Block to
3326 // have a terminator, which is already removed by EmitOMPRegionBody.
3327 // IP is currently at cancelation block.
3328 // We need to backtrack to the condition block to fetch
3329 // the exit block and create a branch from cancelation
3330 // to exit block.
3332 Builder.restoreIP(IP);
3333 auto *CaseBB = Loc.IP.getNodeParent();
3334 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3335 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3336 Instruction *I = Builder.CreateBr(ExitBB);
3337 IP = I->getIterator();
3338 return FiniCB(IP);
3339 };
3340
3341 Directive OMPD = Directive::OMPD_sections;
3342 // Since we are using Finalization Callback here, HasFinalize
3343 // and IsCancellable have to be true
3344 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3345 /*Conditional*/ false, /*hasFinalize*/ true,
3346 /*IsCancellable*/ true);
3347}
3348
3354
3355Value *OpenMPIRBuilder::getGPUThreadID() {
3358 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3359 {});
3360}
3361
3362Value *OpenMPIRBuilder::getGPUWarpSize() {
3364 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3365}
3366
3367Value *OpenMPIRBuilder::getNVPTXWarpID() {
3368 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3369 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3370}
3371
3372Value *OpenMPIRBuilder::getNVPTXLaneID() {
3373 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3374 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3375 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3376 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3377 "nvptx_lane_id");
3378}
3379
3380Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3381 Type *ToType) {
3382 Type *FromType = From->getType();
3383 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3384 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3385 assert(FromSize > 0 && "From size must be greater than zero");
3386 assert(ToSize > 0 && "To size must be greater than zero");
3387 if (FromType == ToType)
3388 return From;
3389 if (FromSize == ToSize)
3390 return Builder.CreateBitCast(From, ToType);
3391 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3392 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3393 InsertPointTy SaveIP = Builder.saveIP();
3394 Builder.restoreIP(AllocaIP);
3395 Value *CastItem = Builder.CreateAlloca(ToType);
3396 Builder.restoreIP(SaveIP);
3397
3398 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3399 CastItem, Builder.getPtrTy(0));
3400 Builder.CreateStore(From, ValCastItem);
3401 return Builder.CreateLoad(ToType, CastItem);
3402}
3403
3404Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3405 Value *Element,
3406 Type *ElementType,
3407 Value *Offset) {
3408 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3409 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3410
3411 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3412 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3413 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3414 Value *WarpSize =
3415 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3417 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3418 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3419 Value *WarpSizeCast =
3420 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3421 Value *ShuffleCall =
3422 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3423 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3424 // down to the requested element type, otherwise storing the result would
3425 // write past the end of an element narrower than the shuffle width.
3426 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3427}
3428
3429void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3430 Value *DstAddr, Type *ElemType,
3431 Value *Offset, Type *ReductionArrayTy,
3432 bool IsByRefElem) {
3433 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3434 // Create the loop over the big sized data.
3435 // ptr = (void*)Elem;
3436 // ptrEnd = (void*) Elem + 1;
3437 // Step = 8;
3438 // while (ptr + Step < ptrEnd)
3439 // shuffle((int64_t)*ptr);
3440 // Step = 4;
3441 // while (ptr + Step < ptrEnd)
3442 // shuffle((int32_t)*ptr);
3443 // ...
3444 Type *IndexTy = Builder.getIndexTy(
3445 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3446 Value *ElemPtr = DstAddr;
3447 Value *Ptr = SrcAddr;
3448 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3449 if (Size < IntSize)
3450 continue;
3451 Type *IntType = Builder.getIntNTy(IntSize * 8);
3452 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3453 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3454 Value *SrcAddrGEP =
3455 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3456 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3457 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3458
3459 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3460 if ((Size / IntSize) > 1) {
3461 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3462 SrcAddrGEP, Builder.getPtrTy());
3463 BasicBlock *PreCondBB =
3464 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3465 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3466 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3467 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3468 emitBlock(PreCondBB, CurFunc);
3469 PHINode *PhiSrc =
3470 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3471 PhiSrc->addIncoming(Ptr, CurrentBB);
3472 PHINode *PhiDest =
3473 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3474 PhiDest->addIncoming(ElemPtr, CurrentBB);
3475 Ptr = PhiSrc;
3476 ElemPtr = PhiDest;
3477 Value *PtrDiff = Builder.CreatePtrDiff(
3478 Builder.getInt8Ty(), PtrEnd,
3479 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3480 Builder.CreateCondBr(
3481 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3482 ExitBB);
3483 emitBlock(ThenBB, CurFunc);
3484 Value *Res = createRuntimeShuffleFunction(
3485 AllocaIP,
3486 Builder.CreateAlignedLoad(
3487 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3488 IntType, Offset);
3489 Builder.CreateAlignedStore(Res, ElemPtr,
3490 M.getDataLayout().getPrefTypeAlign(ElemType));
3491 Value *LocalPtr =
3492 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3493 Value *LocalElemPtr =
3494 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3495 PhiSrc->addIncoming(LocalPtr, ThenBB);
3496 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3497 emitBranch(PreCondBB);
3498 emitBlock(ExitBB, CurFunc);
3499 } else {
3500 // The shuffled value comes back as the chunk's integer type, so the
3501 // store covers exactly this chunk regardless of what ElemType is.
3502 Value *Res = createRuntimeShuffleFunction(
3503 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3504 Builder.CreateStore(Res, ElemPtr);
3505 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3506 ElemPtr =
3507 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3508 }
3509 Size = Size % IntSize;
3510 }
3511}
3512
3513Error OpenMPIRBuilder::emitReductionListCopy(
3514 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3515 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3516 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3517 Type *IndexTy = Builder.getIndexTy(
3518 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3519 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3520
3521 // Iterates, element-by-element, through the source Reduce list and
3522 // make a copy.
3523 for (auto En : enumerate(ReductionInfos)) {
3524 const ReductionInfo &RI = En.value();
3525 Value *SrcElementAddr = nullptr;
3526 AllocaInst *DestAlloca = nullptr;
3527 Value *DestElementAddr = nullptr;
3528 Value *DestElementPtrAddr = nullptr;
3529 // Should we shuffle in an element from a remote lane?
3530 bool ShuffleInElement = false;
3531 // Set to true to update the pointer in the dest Reduce list to a
3532 // newly created element.
3533 bool UpdateDestListPtr = false;
3534
3535 // Step 1.1: Get the address for the src element in the Reduce list.
3536 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3537 ReductionArrayTy, SrcBase,
3538 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3539 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3540
3541 // Step 1.2: Create a temporary to store the element in the destination
3542 // Reduce list.
3543 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3544 ReductionArrayTy, DestBase,
3545 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3546 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3547 switch (Action) {
3549 InsertPointTy CurIP = Builder.saveIP();
3550 Builder.restoreIP(AllocaIP);
3551
3552 Type *DestAllocaType =
3553 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3554 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3555 ".omp.reduction.element");
3556 DestAlloca->setAlignment(
3557 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3558 DestElementAddr = DestAlloca;
3559 DestElementAddr =
3560 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3561 DestElementAddr->getName() + ".ascast");
3562 Builder.restoreIP(CurIP);
3563 ShuffleInElement = true;
3564 UpdateDestListPtr = true;
3565 break;
3566 }
3568 DestElementAddr =
3569 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3570 break;
3571 }
3572 }
3573
3574 // Now that all active lanes have read the element in the
3575 // Reduce list, shuffle over the value from the remote lane.
3576 if (ShuffleInElement) {
3577 Type *ShuffleType = RI.ElementType;
3578 Value *ShuffleSrcAddr = SrcElementAddr;
3579 Value *ShuffleDestAddr = DestElementAddr;
3580 AllocaInst *LocalStorage = nullptr;
3581
3582 if (IsByRefElem) {
3583 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3584 assert(RI.ByRefAllocatedType &&
3585 "Expected by-ref allocated type to be set");
3586 // For by-ref reductions, we need to copy from the remote lane the
3587 // actual value of the partial reduction computed by that remote lane;
3588 // rather than, for example, a pointer to that data or, even worse, a
3589 // pointer to the descriptor of the by-ref reduction element.
3590 ShuffleType = RI.ByRefElementType;
3591
3592 if (RI.DataPtrPtrGen) {
3593 // Descriptor-based by-ref: extract data pointer from descriptor.
3594 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3595 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3596
3597 if (!GenResult)
3598 return GenResult.takeError();
3599
3600 ShuffleSrcAddr =
3601 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3602
3603 {
3604 InsertPointTy OldIP = Builder.saveIP();
3605 Builder.restoreIP(AllocaIP);
3606
3607 LocalStorage = Builder.CreateAlloca(ShuffleType);
3608 Builder.restoreIP(OldIP);
3609 ShuffleDestAddr = LocalStorage;
3610 }
3611 } else {
3612 // Non-descriptor by-ref: the pointer already references data
3613 // directly. Shuffle into the destination alloca.
3614 ShuffleDestAddr = DestElementAddr;
3615 }
3616 }
3617
3618 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3619 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3620
3621 if (IsByRefElem && RI.DataPtrPtrGen) {
3622 // Copy descriptor from source and update base_ptr to shuffled data
3623 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3624 DestAlloca, Builder.getPtrTy(), ".ascast");
3625
3626 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3627 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3628 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3629
3630 if (!GenResult)
3631 return GenResult.takeError();
3632 }
3633 } else {
3634 switch (RI.EvaluationKind) {
3635 case EvalKind::Scalar: {
3636 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3637 // Store the source element value to the dest element address.
3638 Builder.CreateStore(Elem, DestElementAddr);
3639 break;
3640 }
3641 case EvalKind::Complex: {
3642 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3643 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3644 Value *SrcReal = Builder.CreateLoad(
3645 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3646 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3647 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3648 Value *SrcImg = Builder.CreateLoad(
3649 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3650
3651 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3652 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3653 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3654 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3655 Builder.CreateStore(SrcReal, DestRealPtr);
3656 Builder.CreateStore(SrcImg, DestImgPtr);
3657 break;
3658 }
3659 case EvalKind::Aggregate: {
3660 Value *SizeVal = Builder.getInt64(
3661 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3662 Builder.CreateMemCpy(
3663 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3664 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3665 SizeVal, false);
3666 break;
3667 }
3668 };
3669 }
3670
3671 // Step 3.1: Modify reference in dest Reduce list as needed.
3672 // Modifying the reference in Reduce list to point to the newly
3673 // created element. The element is live in the current function
3674 // scope and that of functions it invokes (i.e., reduce_function).
3675 // RemoteReduceData[i] = (void*)&RemoteElem
3676 if (UpdateDestListPtr) {
3677 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3678 DestElementAddr, Builder.getPtrTy(),
3679 DestElementAddr->getName() + ".ascast");
3680 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3681 }
3682 }
3683
3684 return Error::success();
3685}
3686
3687Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3688 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3689 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3690 IRBuilder<>::InsertPointGuard IPG(Builder);
3691 LLVMContext &Ctx = M.getContext();
3692 FunctionType *FuncTy = FunctionType::get(
3693 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3694 /* IsVarArg */ false);
3695 Function *WcFunc =
3697 "_omp_reduction_inter_warp_copy_func", &M);
3698 WcFunc->setCallingConv(Config.getRuntimeCC());
3699 WcFunc->setAttributes(FuncAttrs);
3700 WcFunc->addParamAttr(0, Attribute::NoUndef);
3701 WcFunc->addParamAttr(1, Attribute::NoUndef);
3702 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3703 Builder.SetInsertPoint(EntryBB);
3704 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3705
3706 // ReduceList: thread local Reduce list.
3707 // At the stage of the computation when this function is called, partially
3708 // aggregated values reside in the first lane of every active warp.
3709 Argument *ReduceListArg = WcFunc->getArg(0);
3710 // NumWarps: number of warps active in the parallel region. This could
3711 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3712 Argument *NumWarpsArg = WcFunc->getArg(1);
3713
3714 // This array is used as a medium to transfer, one reduce element at a time,
3715 // the data from the first lane of every warp to lanes in the first warp
3716 // in order to perform the final step of a reduction in a parallel region
3717 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3718 // for reduced latency, as well as to have a distinct copy for concurrently
3719 // executing target regions. The array is declared with common linkage so
3720 // as to be shared across compilation units.
3721 StringRef TransferMediumName =
3722 "__openmp_nvptx_data_transfer_temporary_storage";
3723 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3724 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3725 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3726 if (!TransferMedium) {
3727 TransferMedium = new GlobalVariable(
3728 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3729 UndefValue::get(ArrayTy), TransferMediumName,
3730 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3731 /*AddressSpace=*/3);
3732 }
3733
3734 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3735 Value *GPUThreadID = getGPUThreadID();
3736 // nvptx_lane_id = nvptx_id % warpsize
3737 Value *LaneID = getNVPTXLaneID();
3738 // nvptx_warp_id = nvptx_id / warpsize
3739 Value *WarpID = getNVPTXWarpID();
3740
3741 InsertPointTy AllocaIP = Builder.GetInsertBlock()->getFirstInsertionPt();
3742 Type *Arg0Type = ReduceListArg->getType();
3743 Type *Arg1Type = NumWarpsArg->getType();
3744 Builder.restoreIP(AllocaIP);
3745 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3746 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3747 AllocaInst *NumWarpsAlloca =
3748 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3749 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3750 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3751 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3752 NumWarpsAlloca, Builder.getPtrTy(0),
3753 NumWarpsAlloca->getName() + ".ascast");
3754 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3755 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3756 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3757 InsertPointTy CodeGenIP =
3758 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3759 Builder.restoreIP(CodeGenIP);
3760
3761 Value *ReduceList =
3762 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3763
3764 for (auto En : enumerate(ReductionInfos)) {
3765 //
3766 // Warp master copies reduce element to transfer medium in __shared__
3767 // memory.
3768 //
3769 const ReductionInfo &RI = En.value();
3770 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3771 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3772 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3773 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3774 Type *CType = Builder.getIntNTy(TySize * 8);
3775
3776 unsigned NumIters = RealTySize / TySize;
3777 if (NumIters == 0)
3778 continue;
3779 Value *Cnt = nullptr;
3780 Value *CntAddr = nullptr;
3781 BasicBlock *PrecondBB = nullptr;
3782 BasicBlock *ExitBB = nullptr;
3783 if (NumIters > 1) {
3784 CodeGenIP = Builder.saveIP();
3785 Builder.restoreIP(AllocaIP);
3786 CntAddr =
3787 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3788
3789 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3790 CntAddr->getName() + ".ascast");
3791 Builder.restoreIP(CodeGenIP);
3792 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3793 CntAddr,
3794 /*Volatile=*/false);
3795 PrecondBB = BasicBlock::Create(Ctx, "precond");
3796 ExitBB = BasicBlock::Create(Ctx, "exit");
3797 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3798 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3799 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3800 /*Volatile=*/false);
3801 Value *Cmp = Builder.CreateICmpULT(
3802 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3803 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3804 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3805 }
3806
3807 // kmpc_barrier.
3808 InsertPointOrErrorTy BarrierIP1 =
3810 omp::Directive::OMPD_unknown,
3811 /* ForceSimpleCall */ false,
3812 /* CheckCancelFlag */ true);
3813 if (!BarrierIP1)
3814 return BarrierIP1.takeError();
3815 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3816 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3817 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3818
3819 // if (lane_id == 0)
3820 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3821 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3822 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3823
3824 // Reduce element = LocalReduceList[i]
3825 auto *RedListArrayTy =
3826 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3827 Type *IndexTy = Builder.getIndexTy(
3828 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3829 Value *ElemPtrPtr =
3830 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3831 {ConstantInt::get(IndexTy, 0),
3832 ConstantInt::get(IndexTy, En.index())});
3833 // elemptr = ((CopyType*)(elemptrptr)) + I
3834 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3835
3836 if (IsByRefElem && RI.DataPtrPtrGen) {
3837 InsertPointOrErrorTy GenRes =
3838 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3839
3840 if (!GenRes)
3841 return GenRes.takeError();
3842
3843 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3844 }
3845
3846 if (NumIters > 1)
3847 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3848
3849 // Get pointer to location in transfer medium.
3850 // MediumPtr = &medium[warp_id]
3851 Value *MediumPtr = Builder.CreateInBoundsGEP(
3852 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3853 // elem = *elemptr
3854 //*MediumPtr = elem
3855 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3856 // Store the source element value to the dest element address.
3857 Builder.CreateStore(Elem, MediumPtr,
3858 /*IsVolatile*/ true);
3859 Builder.CreateBr(MergeBB);
3860
3861 // else
3862 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3863 Builder.CreateBr(MergeBB);
3864
3865 // endif
3866 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3867 InsertPointOrErrorTy BarrierIP2 =
3869 omp::Directive::OMPD_unknown,
3870 /* ForceSimpleCall */ false,
3871 /* CheckCancelFlag */ true);
3872 if (!BarrierIP2)
3873 return BarrierIP2.takeError();
3874
3875 // Warp 0 copies reduce element from transfer medium
3876 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3877 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3878 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3879
3880 Value *NumWarpsVal =
3881 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3882 // Up to 32 threads in warp 0 are active.
3883 Value *IsActiveThread =
3884 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3885 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3886
3887 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3888
3889 // SecMediumPtr = &medium[tid]
3890 // SrcMediumVal = *SrcMediumPtr
3891 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3892 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3893 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3894 Value *TargetElemPtrPtr =
3895 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3896 {ConstantInt::get(IndexTy, 0),
3897 ConstantInt::get(IndexTy, En.index())});
3898 Value *TargetElemPtrVal =
3899 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3900 Value *TargetElemPtr = TargetElemPtrVal;
3901
3902 if (IsByRefElem && RI.DataPtrPtrGen) {
3903 InsertPointOrErrorTy GenRes =
3904 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3905
3906 if (!GenRes)
3907 return GenRes.takeError();
3908
3909 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3910 }
3911
3912 if (NumIters > 1)
3913 TargetElemPtr =
3914 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3915
3916 // *TargetElemPtr = SrcMediumVal;
3917 Value *SrcMediumValue =
3918 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3919 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3920 Builder.CreateBr(W0MergeBB);
3921
3922 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3923 Builder.CreateBr(W0MergeBB);
3924
3925 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3926
3927 if (NumIters > 1) {
3928 Cnt = Builder.CreateNSWAdd(
3929 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3930 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3931
3932 auto *CurFn = Builder.GetInsertBlock()->getParent();
3933 emitBranch(PrecondBB);
3934 emitBlock(ExitBB, CurFn);
3935 }
3936 RealTySize %= TySize;
3937 }
3938 }
3939
3940 Builder.CreateRetVoid();
3941
3942 return WcFunc;
3943}
3944
3945Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3946 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3947 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3948 LLVMContext &Ctx = M.getContext();
3949 IRBuilder<>::InsertPointGuard IPG(Builder);
3950 FunctionType *FuncTy =
3951 FunctionType::get(Builder.getVoidTy(),
3952 {Builder.getPtrTy(), Builder.getInt16Ty(),
3953 Builder.getInt16Ty(), Builder.getInt16Ty()},
3954 /* IsVarArg */ false);
3955 Function *SarFunc =
3957 "_omp_reduction_shuffle_and_reduce_func", &M);
3958 SarFunc->setCallingConv(Config.getRuntimeCC());
3959 SarFunc->setAttributes(FuncAttrs);
3960 SarFunc->addParamAttr(0, Attribute::NoUndef);
3961 SarFunc->addParamAttr(1, Attribute::NoUndef);
3962 SarFunc->addParamAttr(2, Attribute::NoUndef);
3963 SarFunc->addParamAttr(3, Attribute::NoUndef);
3964 SarFunc->addParamAttr(1, Attribute::SExt);
3965 SarFunc->addParamAttr(2, Attribute::SExt);
3966 SarFunc->addParamAttr(3, Attribute::SExt);
3967 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3968 Builder.SetInsertPoint(EntryBB);
3969 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3970
3971 // Thread local Reduce list used to host the values of data to be reduced.
3972 Argument *ReduceListArg = SarFunc->getArg(0);
3973 // Current lane id; could be logical.
3974 Argument *LaneIDArg = SarFunc->getArg(1);
3975 // Offset of the remote source lane relative to the current lane.
3976 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3977 // Algorithm version. This is expected to be known at compile time.
3978 Argument *AlgoVerArg = SarFunc->getArg(3);
3979
3980 Type *ReduceListArgType = ReduceListArg->getType();
3981 Type *LaneIDArgType = LaneIDArg->getType();
3982 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3983 Value *ReduceListAlloca = Builder.CreateAlloca(
3984 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3985 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3986 LaneIDArg->getName() + ".addr");
3987 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3988 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3989 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3990 AlgoVerArg->getName() + ".addr");
3991 ArrayType *RedListArrayTy =
3992 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3993
3994 // Create a local thread-private variable to host the Reduce list
3995 // from a remote lane.
3996 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3997 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3998
3999 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4000 ReduceListAlloca, ReduceListArgType,
4001 ReduceListAlloca->getName() + ".ascast");
4002 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4003 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4004 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4005 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4006 RemoteLaneOffsetAlloca->getName() + ".ascast");
4007 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4008 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4009 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4010 RemoteReductionListAlloca, Builder.getPtrTy(),
4011 RemoteReductionListAlloca->getName() + ".ascast");
4012
4013 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4014 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4015 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4016 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4017
4018 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4019 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4020 Value *RemoteLaneOffset =
4021 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4022 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4023
4024 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4025
4026 // This loop iterates through the list of reduce elements and copies,
4027 // element by element, from a remote lane in the warp to RemoteReduceList,
4028 // hosted on the thread's stack.
4029 Error EmitRedLsCpRes = emitReductionListCopy(
4030 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4031 ReduceList, RemoteListAddrCast, IsByRef,
4032 {RemoteLaneOffset, nullptr, nullptr});
4033
4034 if (EmitRedLsCpRes)
4035 return EmitRedLsCpRes;
4036
4037 // The actions to be performed on the Remote Reduce list is dependent
4038 // on the algorithm version.
4039 //
4040 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4041 // LaneId % 2 == 0 && Offset > 0):
4042 // do the reduction value aggregation
4043 //
4044 // The thread local variable Reduce list is mutated in place to host the
4045 // reduced data, which is the aggregated value produced from local and
4046 // remote lanes.
4047 //
4048 // Note that AlgoVer is expected to be a constant integer known at compile
4049 // time.
4050 // When AlgoVer==0, the first conjunction evaluates to true, making
4051 // the entire predicate true during compile time.
4052 // When AlgoVer==1, the second conjunction has only the second part to be
4053 // evaluated during runtime. Other conjunctions evaluates to false
4054 // during compile time.
4055 // When AlgoVer==2, the third conjunction has only the second part to be
4056 // evaluated during runtime. Other conjunctions evaluates to false
4057 // during compile time.
4058 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4059 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4060 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4061 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4062 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4063 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4064 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4065 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4066 Value *RemoteOffsetComp =
4067 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4068 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4069 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4070 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4071
4072 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4073 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4074 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4075
4076 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4077 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4078 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4079 ReduceList, Builder.getPtrTy());
4080 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4081 RemoteListAddrCast, Builder.getPtrTy());
4082 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4083 ->addFnAttr(Attribute::NoUnwind);
4084 Builder.CreateBr(MergeBB);
4085
4086 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4087 Builder.CreateBr(MergeBB);
4088
4089 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4090
4091 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4092 // Reduce list.
4093 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4094 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4095 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4096
4097 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4098 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4099 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4100 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4101
4102 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4103
4104 EmitRedLsCpRes = emitReductionListCopy(
4105 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4106 RemoteListAddrCast, ReduceList, IsByRef);
4107
4108 if (EmitRedLsCpRes)
4109 return EmitRedLsCpRes;
4110
4111 Builder.CreateBr(CpyMergeBB);
4112
4113 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4114 Builder.CreateBr(CpyMergeBB);
4115
4116 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4117
4118 Builder.CreateRetVoid();
4119
4120 return SarFunc;
4121}
4122
4124OpenMPIRBuilder::generateReductionDescriptor(
4125 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4126 Type *DescriptorType,
4127 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4128 DataPtrPtrGen) {
4129
4130 // Copy the source descriptor to preserve all metadata (rank, extents,
4131 // strides, etc.)
4132 Value *DescriptorSize =
4133 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4134 Builder.CreateMemCpy(
4135 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4136 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4137 DescriptorSize);
4138
4139 // Update the base pointer field to point to the local shuffled data
4140 Value *DataPtrField;
4141 InsertPointOrErrorTy GenResult =
4142 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4143
4144 if (!GenResult)
4145 return GenResult.takeError();
4146
4147 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4148 DataPtr, Builder.getPtrTy(), ".ascast"),
4149 DataPtrField);
4150
4151 return Builder.saveIP();
4152}
4153
4154Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4155 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4156 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4157 InsertPointTy OldIP = Builder.saveIP();
4158 Builder.restoreIP(AllocaIP);
4159
4160 AllocaInst *DescriptorAlloca =
4161 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4162 DescriptorAlloca->setAlignment(
4163 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4164 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4165 DescriptorAlloca, DescriptorPtrTy,
4166 DescriptorAlloca->getName() + ".ascast");
4167
4168 Builder.restoreIP(OldIP);
4169
4170 InsertPointOrErrorTy GenResult =
4171 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4172 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4173 if (!GenResult)
4174 return GenResult.takeError();
4175
4176 return DescriptorAddr;
4177}
4178
4179Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4180 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4181 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4182 IRBuilder<>::InsertPointGuard IPG(Builder);
4183 LLVMContext &Ctx = M.getContext();
4184 FunctionType *FuncTy = FunctionType::get(
4185 Builder.getVoidTy(),
4186 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4187 /* IsVarArg */ false);
4188 Function *LtGCFunc =
4190 "_omp_reduction_list_to_global_copy_func", &M);
4191 LtGCFunc->setAttributes(FuncAttrs);
4192 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4193 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4194 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4195
4196 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4197 Builder.SetInsertPoint(EntryBlock);
4198 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4199
4200 // Buffer: global reduction buffer.
4201 Argument *BufferArg = LtGCFunc->getArg(0);
4202 // Idx: index of the buffer.
4203 Argument *IdxArg = LtGCFunc->getArg(1);
4204 // ReduceList: thread local Reduce list.
4205 Argument *ReduceListArg = LtGCFunc->getArg(2);
4206
4207 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4208 BufferArg->getName() + ".addr");
4209 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4210 IdxArg->getName() + ".addr");
4211 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4212 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4213 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4214 BufferArgAlloca, Builder.getPtrTy(),
4215 BufferArgAlloca->getName() + ".ascast");
4216 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4217 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4218 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4219 ReduceListArgAlloca, Builder.getPtrTy(),
4220 ReduceListArgAlloca->getName() + ".ascast");
4221
4222 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4223 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4224 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4225
4226 Value *LocalReduceList =
4227 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4228 Value *BufferArgVal =
4229 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4230 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4231 Type *IndexTy = Builder.getIndexTy(
4232 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4233 for (auto En : enumerate(ReductionInfos)) {
4234 const ReductionInfo &RI = En.value();
4235 auto *RedListArrayTy =
4236 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4237 // Reduce element = LocalReduceList[i]
4238 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4239 RedListArrayTy, LocalReduceList,
4240 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4241 // elemptr = ((CopyType*)(elemptrptr)) + I
4242 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4243
4244 // Global = Buffer.VD[Idx];
4245 Value *BufferVD =
4246 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4247 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4248 ReductionsBufferTy, BufferVD, 0, En.index());
4249
4250 switch (RI.EvaluationKind) {
4251 case EvalKind::Scalar: {
4252 Value *TargetElement;
4253
4254 if (IsByRef.empty() || !IsByRef[En.index()]) {
4255 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4256 } else {
4257 if (RI.DataPtrPtrGen) {
4258 InsertPointOrErrorTy GenResult =
4259 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4260
4261 if (!GenResult)
4262 return GenResult.takeError();
4263
4264 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4265 }
4266 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4267 }
4268
4269 Builder.CreateStore(TargetElement, GlobVal);
4270 break;
4271 }
4272 case EvalKind::Complex: {
4273 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4274 RI.ElementType, ElemPtr, 0, 0, ".realp");
4275 Value *SrcReal = Builder.CreateLoad(
4276 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4277 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4278 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4279 Value *SrcImg = Builder.CreateLoad(
4280 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4281
4282 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4283 RI.ElementType, GlobVal, 0, 0, ".realp");
4284 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4285 RI.ElementType, GlobVal, 0, 1, ".imagp");
4286 Builder.CreateStore(SrcReal, DestRealPtr);
4287 Builder.CreateStore(SrcImg, DestImgPtr);
4288 break;
4289 }
4290 case EvalKind::Aggregate: {
4291 Value *SizeVal =
4292 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4293 Builder.CreateMemCpy(
4294 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4295 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4296 break;
4297 }
4298 }
4299 }
4300
4301 Builder.CreateRetVoid();
4302 return LtGCFunc;
4303}
4304
4305Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4306 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4307 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4308 IRBuilder<>::InsertPointGuard IPG(Builder);
4309 LLVMContext &Ctx = M.getContext();
4310 FunctionType *FuncTy = FunctionType::get(
4311 Builder.getVoidTy(),
4312 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4313 /* IsVarArg */ false);
4314 Function *LtGRFunc =
4316 "_omp_reduction_list_to_global_reduce_func", &M);
4317 LtGRFunc->setAttributes(FuncAttrs);
4318 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4319 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4320 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4321
4322 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4323 Builder.SetInsertPoint(EntryBlock);
4324 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4325
4326 // Buffer: global reduction buffer.
4327 Argument *BufferArg = LtGRFunc->getArg(0);
4328 // Idx: index of the buffer.
4329 Argument *IdxArg = LtGRFunc->getArg(1);
4330 // ReduceList: thread local Reduce list.
4331 Argument *ReduceListArg = LtGRFunc->getArg(2);
4332
4333 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4334 BufferArg->getName() + ".addr");
4335 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4336 IdxArg->getName() + ".addr");
4337 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4338 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4339 auto *RedListArrayTy =
4340 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4341
4342 // 1. Build a list of reduction variables.
4343 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4344 Value *LocalReduceList =
4345 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4346
4347 InsertPointTy AllocaIP(EntryBlock->begin());
4348
4349 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4350 BufferArgAlloca, Builder.getPtrTy(),
4351 BufferArgAlloca->getName() + ".ascast");
4352 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4353 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4354 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4355 ReduceListArgAlloca, Builder.getPtrTy(),
4356 ReduceListArgAlloca->getName() + ".ascast");
4357 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4358 LocalReduceList, Builder.getPtrTy(),
4359 LocalReduceList->getName() + ".ascast");
4360
4361 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4362 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4363 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4364
4365 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4366 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4367 Type *IndexTy = Builder.getIndexTy(
4368 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4369 for (auto En : enumerate(ReductionInfos)) {
4370 const ReductionInfo &RI = En.value();
4371
4372 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4373 RedListArrayTy, LocalReduceListAddrCast,
4374 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4375 Value *BufferVD =
4376 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4377 // Global = Buffer.VD[Idx];
4378 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4379 ReductionsBufferTy, BufferVD, 0, En.index());
4380
4381 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4382 // Get source descriptor from the reduce list argument
4383 Value *ReduceList =
4384 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4385 Value *SrcElementPtrPtr =
4386 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4387 {ConstantInt::get(IndexTy, 0),
4388 ConstantInt::get(IndexTy, En.index())});
4389 Value *SrcDescriptorAddr =
4390 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4391
4392 // Copy descriptor from source and update base_ptr to global buffer data
4393 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4394 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4395 if (!ByRefAlloc)
4396 return ByRefAlloc.takeError();
4397
4398 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4399 } else {
4400 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4401 }
4402 }
4403
4404 // Call reduce_function(GlobalReduceList, ReduceList)
4405 Value *ReduceList =
4406 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4407 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4408 ->addFnAttr(Attribute::NoUnwind);
4409 Builder.CreateRetVoid();
4410 return LtGRFunc;
4411}
4412
4413Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4414 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4415 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4416 IRBuilder<>::InsertPointGuard IPG(Builder);
4417 LLVMContext &Ctx = M.getContext();
4418 FunctionType *FuncTy = FunctionType::get(
4419 Builder.getVoidTy(),
4420 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4421 /* IsVarArg */ false);
4422 Function *GtLCFunc =
4424 "_omp_reduction_global_to_list_copy_func", &M);
4425 GtLCFunc->setAttributes(FuncAttrs);
4426 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4427 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4428 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4429
4430 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4431 Builder.SetInsertPoint(EntryBlock);
4432 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4433
4434 // Buffer: global reduction buffer.
4435 Argument *BufferArg = GtLCFunc->getArg(0);
4436 // Idx: index of the buffer.
4437 Argument *IdxArg = GtLCFunc->getArg(1);
4438 // ReduceList: thread local Reduce list.
4439 Argument *ReduceListArg = GtLCFunc->getArg(2);
4440
4441 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4442 BufferArg->getName() + ".addr");
4443 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4444 IdxArg->getName() + ".addr");
4445 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4446 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4447 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4448 BufferArgAlloca, Builder.getPtrTy(),
4449 BufferArgAlloca->getName() + ".ascast");
4450 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4451 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4452 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4453 ReduceListArgAlloca, Builder.getPtrTy(),
4454 ReduceListArgAlloca->getName() + ".ascast");
4455 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4456 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4457 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4458
4459 Value *LocalReduceList =
4460 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4461 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4462 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4463 Type *IndexTy = Builder.getIndexTy(
4464 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4465 for (auto En : enumerate(ReductionInfos)) {
4466 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4467 auto *RedListArrayTy =
4468 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4469 // Reduce element = LocalReduceList[i]
4470 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4471 RedListArrayTy, LocalReduceList,
4472 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4473 // elemptr = ((CopyType*)(elemptrptr)) + I
4474 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4475 // Global = Buffer.VD[Idx];
4476 Value *BufferVD =
4477 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4478 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4479 ReductionsBufferTy, BufferVD, 0, En.index());
4480
4481 switch (RI.EvaluationKind) {
4482 case EvalKind::Scalar: {
4483 Type *ElemType = RI.ElementType;
4484
4485 if (!IsByRef.empty() && IsByRef[En.index()]) {
4486 ElemType = RI.ByRefElementType;
4487 if (RI.DataPtrPtrGen) {
4488 InsertPointOrErrorTy GenResult =
4489 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4490
4491 if (!GenResult)
4492 return GenResult.takeError();
4493
4494 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4495 }
4496 }
4497
4498 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4499 Builder.CreateStore(TargetElement, ElemPtr);
4500 break;
4501 }
4502 case EvalKind::Complex: {
4503 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4504 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4505 Value *SrcReal = Builder.CreateLoad(
4506 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4507 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4508 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4509 Value *SrcImg = Builder.CreateLoad(
4510 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4511
4512 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4513 RI.ElementType, ElemPtr, 0, 0, ".realp");
4514 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4515 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4516 Builder.CreateStore(SrcReal, DestRealPtr);
4517 Builder.CreateStore(SrcImg, DestImgPtr);
4518 break;
4519 }
4520 case EvalKind::Aggregate: {
4521 Value *SizeVal =
4522 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4523 Builder.CreateMemCpy(
4524 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4525 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4526 SizeVal, false);
4527 break;
4528 }
4529 }
4530 }
4531
4532 Builder.CreateRetVoid();
4533 return GtLCFunc;
4534}
4535
4536Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4537 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4538 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4539 IRBuilder<>::InsertPointGuard IPG(Builder);
4540 LLVMContext &Ctx = M.getContext();
4541 auto *FuncTy = FunctionType::get(
4542 Builder.getVoidTy(),
4543 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4544 /* IsVarArg */ false);
4545 Function *GtLRFunc =
4547 "_omp_reduction_global_to_list_reduce_func", &M);
4548 GtLRFunc->setAttributes(FuncAttrs);
4549 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4550 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4551 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4552
4553 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4554 Builder.SetInsertPoint(EntryBlock);
4555 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4556
4557 // Buffer: global reduction buffer.
4558 Argument *BufferArg = GtLRFunc->getArg(0);
4559 // Idx: index of the buffer.
4560 Argument *IdxArg = GtLRFunc->getArg(1);
4561 // ReduceList: thread local Reduce list.
4562 Argument *ReduceListArg = GtLRFunc->getArg(2);
4563
4564 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4565 BufferArg->getName() + ".addr");
4566 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4567 IdxArg->getName() + ".addr");
4568 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4569 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4570 ArrayType *RedListArrayTy =
4571 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4572
4573 // 1. Build a list of reduction variables.
4574 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4575 Value *LocalReduceList =
4576 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4577
4578 InsertPointTy AllocaIP(EntryBlock->begin());
4579
4580 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4581 BufferArgAlloca, Builder.getPtrTy(),
4582 BufferArgAlloca->getName() + ".ascast");
4583 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4584 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4585 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4586 ReduceListArgAlloca, Builder.getPtrTy(),
4587 ReduceListArgAlloca->getName() + ".ascast");
4588 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4589 LocalReduceList, Builder.getPtrTy(),
4590 LocalReduceList->getName() + ".ascast");
4591
4592 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4593 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4594 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4595
4596 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4597 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4598 Type *IndexTy = Builder.getIndexTy(
4599 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4600 for (auto En : enumerate(ReductionInfos)) {
4601 const ReductionInfo &RI = En.value();
4602
4603 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4604 RedListArrayTy, ReductionList,
4605 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4606 // Global = Buffer.VD[Idx];
4607 Value *BufferVD =
4608 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4609 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4610 ReductionsBufferTy, BufferVD, 0, En.index());
4611
4612 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4613 // Get source descriptor from the reduce list
4614 Value *ReduceListVal =
4615 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4616 Value *SrcElementPtrPtr =
4617 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4618 {ConstantInt::get(IndexTy, 0),
4619 ConstantInt::get(IndexTy, En.index())});
4620 Value *SrcDescriptorAddr =
4621 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4622
4623 // Copy descriptor from source and update base_ptr to global buffer data
4624 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4625 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4626 if (!ByRefAlloc)
4627 return ByRefAlloc.takeError();
4628
4629 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4630 } else {
4631 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4632 }
4633 }
4634
4635 // Call reduce_function(ReduceList, GlobalReduceList)
4636 Value *ReduceList =
4637 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4638 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4639 ->addFnAttr(Attribute::NoUnwind);
4640 Builder.CreateRetVoid();
4641 return GtLRFunc;
4642}
4643
4644std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4645 std::string Suffix =
4646 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4647 return (Name + Suffix).str();
4648}
4649
4650Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4651 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4653 AttributeList FuncAttrs) {
4654 IRBuilder<>::InsertPointGuard IPG(Builder);
4655 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4656 {Builder.getPtrTy(), Builder.getPtrTy()},
4657 /* IsVarArg */ false);
4658 std::string Name = getReductionFuncName(ReducerName);
4659 Function *ReductionFunc =
4661 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4662 ReductionFunc->setAttributes(FuncAttrs);
4663 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4664 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4665 BasicBlock *EntryBB =
4666 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4667 Builder.SetInsertPoint(EntryBB);
4668 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4669
4670 // Need to alloca memory here and deal with the pointers before getting
4671 // LHS/RHS pointers out
4672 Value *LHSArrayPtr = nullptr;
4673 Value *RHSArrayPtr = nullptr;
4674 Argument *Arg0 = ReductionFunc->getArg(0);
4675 Argument *Arg1 = ReductionFunc->getArg(1);
4676 Type *Arg0Type = Arg0->getType();
4677 Type *Arg1Type = Arg1->getType();
4678
4679 Value *LHSAlloca =
4680 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4681 Value *RHSAlloca =
4682 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4683 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4684 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4685 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4686 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4687 Builder.CreateStore(Arg0, LHSAddrCast);
4688 Builder.CreateStore(Arg1, RHSAddrCast);
4689 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4690 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4691
4692 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4693 Type *IndexTy = Builder.getIndexTy(
4694 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4695 SmallVector<Value *> LHSPtrs, RHSPtrs;
4696 for (auto En : enumerate(ReductionInfos)) {
4697 const ReductionInfo &RI = En.value();
4698 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4699 RedArrayTy, RHSArrayPtr,
4700 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4701 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4702 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4703 RHSI8Ptr, RI.PrivateVariable->getType(),
4704 RHSI8Ptr->getName() + ".ascast");
4705
4706 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4707 RedArrayTy, LHSArrayPtr,
4708 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4709 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4710 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4711 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4712
4714 LHSPtrs.emplace_back(LHSPtr);
4715 RHSPtrs.emplace_back(RHSPtr);
4716 } else {
4717 Value *LHS = LHSPtr;
4718 Value *RHS = RHSPtr;
4719
4720 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4721 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4722 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4723 }
4724
4725 Value *Reduced;
4726 InsertPointOrErrorTy AfterIP =
4727 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4728 if (!AfterIP)
4729 return AfterIP.takeError();
4730 if (!Builder.GetInsertBlock())
4731 return ReductionFunc;
4732
4733 Builder.restoreIP(*AfterIP);
4734
4735 if (!IsByRef.empty() && !IsByRef[En.index()])
4736 Builder.CreateStore(Reduced, LHSPtr);
4737 }
4738 }
4739
4741 for (auto En : enumerate(ReductionInfos)) {
4742 unsigned Index = En.index();
4743 const ReductionInfo &RI = En.value();
4744 Value *LHSFixupPtr, *RHSFixupPtr;
4745 Builder.restoreIP(RI.ReductionGenClang(
4746 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4747
4748 // Fix the CallBack code genereated to use the correct Values for the LHS
4749 // and RHS
4750 LHSFixupPtr->replaceUsesWithIf(
4751 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4752 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4753 ReductionFunc;
4754 });
4755 RHSFixupPtr->replaceUsesWithIf(
4756 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4757 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4758 ReductionFunc;
4759 });
4760 }
4761
4762 Builder.CreateRetVoid();
4763 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4764 // to the entry block (this is dones for higher opt levels by later passes in
4765 // the pipeline). This has caused issues because non-entry `alloca`s force the
4766 // function to use dynamic stack allocations and we might run out of scratch
4767 // memory.
4768 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4769
4770 return ReductionFunc;
4771}
4772
4773static void
4775 bool IsGPU) {
4776 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4777 (void)RI;
4778 assert(RI.Variable && "expected non-null variable");
4779 assert(RI.PrivateVariable && "expected non-null private variable");
4780 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4781 "expected non-null reduction generator callback");
4782 if (!IsGPU) {
4783 assert(
4784 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4785 "expected variables and their private equivalents to have the same "
4786 "type");
4787 }
4788 assert(RI.Variable->getType()->isPointerTy() &&
4789 "expected variables to be pointers");
4790 }
4791}
4792
4793// The atomic cross-team reduction fast path applies when every reduction in the
4794// set can be represented by an atomicrmw. Clang only populates it for scalar
4795// reductions with a supported atomic operator.
4798 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4799 return static_cast<bool>(RI.AtomicReductionGen);
4800 });
4801}
4802
4804 const LocationDescription &Loc, InsertPointTy AllocaIP,
4805 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4806 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4807 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4808 Value *SrcLocInfo) {
4809 if (!updateToLocation(Loc))
4810 return InsertPointTy();
4811 Builder.restoreIP(CodeGenIP);
4812 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4813 LLVMContext &Ctx = M.getContext();
4814
4815 // Source location for the ident struct
4816 if (!SrcLocInfo) {
4817 uint32_t SrcLocStrSize;
4818 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4819 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4820 }
4821
4822 if (ReductionInfos.size() == 0)
4823 return Builder.saveIP();
4824
4825 BasicBlock *ContinuationBlock = nullptr;
4827 // Copied code from createReductions
4828 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
4829 ContinuationBlock = InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
4830 InsertBlock->getTerminator()->eraseFromParent();
4831 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4832 }
4833
4834 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4835 AttributeList FuncAttrs;
4836 AttrBuilder AttrBldr(Ctx);
4837 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4838 AttrBldr.addAttribute(Attr);
4839 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4840 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4841
4842 CodeGenIP = Builder.saveIP();
4843 Expected<Function *> ReductionResult = createReductionFunction(
4844 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4845 ReductionGenCBKind, FuncAttrs);
4846 if (!ReductionResult)
4847 return ReductionResult.takeError();
4848 Function *ReductionFunc = *ReductionResult;
4849 Builder.restoreIP(CodeGenIP);
4850
4851 // Set the grid value in the config needed for lowering later on
4852 if (GridValue.has_value())
4853 Config.setGridValue(GridValue.value());
4854 else
4855 Config.setGridValue(getGridValue(T, ReductionFunc));
4856
4857 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4858 // RedList, shuffle_reduce_func, interwarp_copy_func);
4859 // or
4860 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4861 Value *Res;
4862
4863 // 1. Build a list of reduction variables.
4864 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4865 auto Size = ReductionInfos.size();
4866 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4867 Type *FuncPtrTy =
4868 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4869 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4870 CodeGenIP = Builder.saveIP();
4871 Builder.restoreIP(AllocaIP);
4872 Value *ReductionListAlloca =
4873 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4874 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4875 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4876 Builder.restoreIP(CodeGenIP);
4877 Type *IndexTy = Builder.getIndexTy(
4878 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4879 for (auto En : enumerate(ReductionInfos)) {
4880 const ReductionInfo &RI = En.value();
4881 Value *ElemPtr = Builder.CreateInBoundsGEP(
4882 RedArrayTy, ReductionList,
4883 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4884
4885 Value *PrivateVar = RI.PrivateVariable;
4886 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4887 if (IsByRefElem)
4888 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4889
4890 Value *CastElem =
4891 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4892 Builder.CreateStore(CastElem, ElemPtr);
4893 }
4894 CodeGenIP = Builder.saveIP();
4895 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4896 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4897
4898 if (!SarFunc)
4899 return SarFunc.takeError();
4900
4901 Expected<Function *> CopyResult =
4902 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4903 if (!CopyResult)
4904 return CopyResult.takeError();
4905 Function *WcFunc = *CopyResult;
4906 Builder.restoreIP(CodeGenIP);
4907
4908 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4909
4910 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4911 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4912 // not currently use it. It is computed here conservatively as max(element
4913 // sizes) * N rather than the exact sum, which over-calculates the size for
4914 // mixed reduction types but is harmless given the argument is unused.
4915 // TODO: Consider dropping this computation if the runtime API is ever revised
4916 // to remove the unused parameter.
4917 unsigned MaxDataSize = 0;
4918 SmallVector<Type *> ReductionTypeArgs;
4919 for (auto En : enumerate(ReductionInfos)) {
4920 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4921 // the actual data size stored in the global reduction buffer, consistent
4922 // with the ReductionsBufferTy struct used for GEP offsets below.
4923 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4924 ? En.value().ByRefElementType
4925 : En.value().ElementType;
4926 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4927 if (Size > MaxDataSize)
4928 MaxDataSize = Size;
4929 ReductionTypeArgs.emplace_back(RedTypeArg);
4930 }
4931 Value *ReductionDataSize =
4932 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4933
4934 // Helper function to copy thread-local data back to the original reduction
4935 // list.
4936 Function *CopyScratchToListFunc = nullptr;
4937 // Thread-local storage for the reduction variables.
4938 Value *ScratchForCopyBack = nullptr;
4939 // RL pointer to which the final value from the per-thread scratch should be
4940 // copied back. (Basically RL, appropriately casted if necessary.)
4941 Value *RLForCopyBack = RL;
4942
4943 bool IsAtomicReduction =
4944 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4945
4946 if (!IsTeamsReduction) {
4947 Value *SarFuncCast =
4948 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4949 Value *WcFuncCast =
4950 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4951 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4952 WcFuncCast};
4954 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4955 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4956 } else if (IsAtomicReduction) {
4957 // Atomic cross-team reduction fast path: determine the team's main thread
4958 // that is later to fold its value atomically into the mapped variable.
4959 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4960 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4961 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4962 } else {
4963 CodeGenIP = Builder.saveIP();
4964 StructType *ReductionsBufferTy = StructType::create(
4965 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4966
4967 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4968 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4969 if (!LtGCFunc)
4970 return LtGCFunc.takeError();
4971
4972 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4973 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4974 if (!GtLCFunc)
4975 return GtLCFunc.takeError();
4976
4977 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4978 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4979 if (!GtLRFunc)
4980 return GtLRFunc.takeError();
4981
4982 Builder.restoreIP(CodeGenIP);
4983
4984 // The runtime's cross-team final aggregate uses the storage pointed at by
4985 // its reduce-list argument as per-thread scratch. When the surrounding
4986 // kernel is already in SPMD execution mode, clang emitted each reduction
4987 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4988 // (RL) is already per-thread and nothing else is needed.
4989 //
4990 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4991 // Generic-mode globalization put the reduction private into team-shared
4992 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4993 // point all threads of the last team would race on the shared LDS slot.
4994 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4995 // value in, and hand the per-thread RL to the runtime instead. The writer
4996 // thread copies the final value from that per-thread scratch back to RL
4997 // before running the existing combine path below.
4998
4999 // Thread-local RL (might need localization below before being passed to the
5000 // runtime).
5001 Value *RuntimeRL = RL;
5002
5003 if (!IsSPMD) {
5004 CodeGenIP = Builder.saveIP();
5005 Builder.restoreIP(AllocaIP);
5006 // Allocate thread-local buffer for the reduction variables.
5007 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
5008 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
5009 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5010 PerThreadScratchAlloca, PtrTy,
5011 PerThreadScratchAlloca->getName() + ".ascast");
5012 // Allocate thread-local buffer for the pointers to the reduction
5013 // variables.
5014 Value *PerThreadRedListAlloca =
5015 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5016 ".omp.reduction.per_thread_red_list");
5017 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5018 PerThreadRedListAlloca, PtrTy,
5019 PerThreadRedListAlloca->getName() + ".ascast");
5020 Builder.restoreIP(CodeGenIP);
5021
5022 // Iterate over the reduction variables and copy the team-local value to
5023 // the thread-local buffer.
5024 for (auto En : enumerate(ReductionInfos)) {
5025 const ReductionInfo &RI = En.value();
5026 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5027
5028 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5029 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5030 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5031 0, En.index());
5032
5033 Value *RuntimeListEntry = FieldPtr;
5034 if (IsByRefElem && RI.DataPtrPtrGen) {
5035 Value *SrcDescriptor =
5036 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5037 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5038 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5039 if (!Descriptor)
5040 return Descriptor.takeError();
5041 RuntimeListEntry = *Descriptor;
5042 }
5043 Builder.CreateStore(RuntimeListEntry, Slot);
5044 }
5045 // The copy helpers were emitted with default-AS (AS 0) pointer params
5046 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5047 // but PerThreadScratch and RL live in the target's default AS, which
5048 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5049 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5050 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5051 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5052 PerThreadScratch, CopyArg0Ty);
5053 RLForCopyBack =
5054 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5055 // Use index 0 because there is no array of target values to index into,
5056 // there is only one thread-local memory slot.
5057 // restoreIP above left a stale/empty debug location; this inlinable call
5058 // to a debug-info-bearing helper needs one or the verifier rejects the
5059 // module ("!dbg attachment points at wrong subprogram") after inlining.
5060 Builder.SetCurrentDebugLocation(Loc.DL);
5061 Builder.CreateCall(
5062 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5063 CopyScratchToListFunc = *GtLCFunc;
5064 }
5065
5066 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5067 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5068
5069 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5070 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5071 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5072 }
5073
5074 // 5. Build if (res == 1)
5075 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5076 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5077 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5078 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5079
5080 // 6. Build then branch: where we have reduced values in the master
5081 // thread in each team.
5082 // __kmpc_end_reduce{_nowait}(<gtid>);
5083 // break;
5084 emitBlock(ThenBB, CurFunc);
5085
5086 // Copy the writer thread's per-thread scratch result back into the original
5087 // red-list storage before the existing combine path reads RI.PrivateVariable.
5088 // Set a debug location: this inlinable call to a debug-info-bearing helper
5089 // needs one or the verifier rejects the module after inlining.
5090 if (ScratchForCopyBack) {
5091 Builder.SetCurrentDebugLocation(Loc.DL);
5092 Builder.CreateCall(
5093 CopyScratchToListFunc,
5094 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5095 }
5096
5097 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5098 for (auto En : enumerate(ReductionInfos)) {
5099 const ReductionInfo &RI = En.value();
5100
5101 // Atomic cross-team fast path: each team's main thread folds its
5102 // team-reduced value directly into the mapped reduction variable with a
5103 // single atomicrmw.
5104 if (IsAtomicReduction) {
5106 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5107 if (!AfterIP)
5108 return AfterIP.takeError();
5109 Builder.restoreIP(*AfterIP);
5110 continue;
5111 }
5112
5114 Value *RedValue = RI.Variable;
5115
5116 Value *RHS =
5117 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5118
5120 Value *LHSPtr, *RHSPtr;
5121 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5122 &LHSPtr, &RHSPtr, CurFunc));
5123
5124 // Fix the CallBack code genereated to use the correct Values for the LHS
5125 // and RHS. Cast to match types before replacing (necessary to handle
5126 // different address spaces).
5127 if (LHSPtr->getType() != RedValue->getType())
5128 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5129 RedValue, LHSPtr->getType());
5130 if (RHSPtr->getType() != RHS->getType())
5131 RHS =
5132 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5133
5134 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5135 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5136 ReductionFunc;
5137 });
5138 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5139 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5140 ReductionFunc;
5141 });
5142 } else {
5143 if (IsByRef.empty() || !IsByRef[En.index()]) {
5144 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5145 "red.value." + Twine(En.index()));
5146 }
5147 Value *PrivateRedValue = Builder.CreateLoad(
5148 ValueType, RHS, "red.private.value" + Twine(En.index()));
5149 Value *Reduced;
5150 InsertPointOrErrorTy AfterIP =
5151 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5152 if (!AfterIP)
5153 return AfterIP.takeError();
5154 Builder.restoreIP(*AfterIP);
5155
5156 if (!IsByRef.empty() && !IsByRef[En.index()])
5157 Builder.CreateStore(Reduced, RI.Variable);
5158 }
5159 }
5160 emitBlock(ExitBB, CurFunc);
5161 if (ContinuationBlock) {
5162 Builder.CreateBr(ContinuationBlock);
5163 Builder.SetInsertPoint(ContinuationBlock);
5164 }
5165 Config.setEmitLLVMUsed();
5166
5167 return Builder.saveIP();
5168}
5169
5171 Type *VoidTy = Type::getVoidTy(M.getContext());
5172 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5173 auto *FuncTy =
5174 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5176 ".omp.reduction.func", &M);
5177}
5178
5180 Function *ReductionFunc,
5182 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5183 IRBuilder<>::InsertPointGuard IPG(Builder);
5184 Module *Module = ReductionFunc->getParent();
5185 BasicBlock *ReductionFuncBlock =
5186 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5187 Builder.SetInsertPoint(ReductionFuncBlock);
5188 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5189 Value *LHSArrayPtr = nullptr;
5190 Value *RHSArrayPtr = nullptr;
5191 if (IsGPU) {
5192 // Need to alloca memory here and deal with the pointers before getting
5193 // LHS/RHS pointers out
5194 //
5195 Argument *Arg0 = ReductionFunc->getArg(0);
5196 Argument *Arg1 = ReductionFunc->getArg(1);
5197 Type *Arg0Type = Arg0->getType();
5198 Type *Arg1Type = Arg1->getType();
5199
5200 Value *LHSAlloca =
5201 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5202 Value *RHSAlloca =
5203 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5204 Value *LHSAddrCast =
5205 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5206 Value *RHSAddrCast =
5207 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5208 Builder.CreateStore(Arg0, LHSAddrCast);
5209 Builder.CreateStore(Arg1, RHSAddrCast);
5210 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5211 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5212 } else {
5213 LHSArrayPtr = ReductionFunc->getArg(0);
5214 RHSArrayPtr = ReductionFunc->getArg(1);
5215 }
5216
5217 unsigned NumReductions = ReductionInfos.size();
5218 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5219
5220 for (auto En : enumerate(ReductionInfos)) {
5221 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5222 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5223 RedArrayTy, LHSArrayPtr, 0, En.index());
5224 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5225 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5226 LHSI8Ptr, RI.Variable->getType());
5227 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5228 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5229 RedArrayTy, RHSArrayPtr, 0, En.index());
5230 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5231 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5232 RHSI8Ptr, RI.PrivateVariable->getType());
5233 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5234 Value *Reduced;
5236 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5237 if (!AfterIP)
5238 return AfterIP.takeError();
5239
5240 Builder.restoreIP(*AfterIP);
5241 // TODO: Consider flagging an error.
5242 if (!Builder.GetInsertBlock())
5243 return Error::success();
5244
5245 // store is inside of the reduction region when using by-ref
5246 if (!IsByRef[En.index()])
5247 Builder.CreateStore(Reduced, LHSPtr);
5248 }
5249 Builder.CreateRetVoid();
5250 return Error::success();
5251}
5252
5254 const LocationDescription &Loc, InsertPointTy AllocaIP,
5255 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5256 bool IsNoWait, bool IsTeamsReduction) {
5257 assert(ReductionInfos.size() == IsByRef.size());
5258 if (Config.isGPU())
5259 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5260 IsByRef, IsNoWait, IsTeamsReduction);
5261
5262 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5263
5264 if (!updateToLocation(Loc))
5265 return InsertPointTy();
5266
5267 if (ReductionInfos.size() == 0)
5268 return Builder.saveIP();
5269
5270 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
5271 BasicBlock *ContinuationBlock =
5272 InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
5273 InsertBlock->getTerminator()->eraseFromParent();
5274
5275 // Create and populate array of type-erased pointers to private reduction
5276 // values.
5277 unsigned NumReductions = ReductionInfos.size();
5278 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5279 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getTerminator());
5280 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5281
5282 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5283 // Emitting the alloca moved the insertion point into the alloca block and
5284 // can clear the debug loc. Restore back to Loc.DL.
5285 Builder.SetCurrentDebugLocation(Loc.DL);
5286
5287 for (auto En : enumerate(ReductionInfos)) {
5288 unsigned Index = En.index();
5289 const ReductionInfo &RI = En.value();
5290 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5291 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5292 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5293 }
5294
5295 // Emit a call to the runtime function that orchestrates the reduction.
5296 // Declare the reduction function in the process.
5297 Type *IndexTy = Builder.getIndexTy(
5298 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5299 Function *Func = Builder.GetInsertBlock()->getParent();
5300 Module *Module = Func->getParent();
5301 uint32_t SrcLocStrSize;
5302 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5303 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5304 return RI.AtomicReductionGen;
5305 });
5306 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5307 CanGenerateAtomic
5308 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5309 : IdentFlag(0));
5310 Value *ThreadId = getOrCreateThreadID(Ident);
5311 Constant *NumVariables = Builder.getInt32(NumReductions);
5312 const DataLayout &DL = Module->getDataLayout();
5313 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5314 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5315 Function *ReductionFunc = getFreshReductionFunc(*Module);
5316 Value *Lock = getOMPCriticalRegionLock(".reduction");
5318 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5319 : RuntimeFunction::OMPRTL___kmpc_reduce);
5320 CallInst *ReduceCall =
5321 createRuntimeFunctionCall(ReduceFunc,
5322 {Ident, ThreadId, NumVariables, RedArraySize,
5323 RedArray, ReductionFunc, Lock},
5324 "reduce");
5325
5326 // Create final reduction entry blocks for the atomic and non-atomic case.
5327 // Emit IR that dispatches control flow to one of the blocks based on the
5328 // reduction supporting the atomic mode.
5329 BasicBlock *NonAtomicRedBlock =
5330 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5331 BasicBlock *AtomicRedBlock =
5332 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5333 SwitchInst *Switch =
5334 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5335 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5336 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5337
5338 // Populate the non-atomic reduction using the elementwise reduction function.
5339 // This loads the elements from the global and private variables and reduces
5340 // them before storing back the result to the global variable.
5341 Builder.SetInsertPoint(NonAtomicRedBlock);
5342 for (auto En : enumerate(ReductionInfos)) {
5343 const ReductionInfo &RI = En.value();
5345 // We have one less load for by-ref case because that load is now inside of
5346 // the reduction region
5347 Value *RedValue = RI.Variable;
5348 if (!IsByRef[En.index()]) {
5349 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5350 "red.value." + Twine(En.index()));
5351 }
5352 Value *PrivateRedValue =
5353 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5354 "red.private.value." + Twine(En.index()));
5355 Value *Reduced;
5356 InsertPointOrErrorTy AfterIP =
5357 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5358 if (!AfterIP)
5359 return AfterIP.takeError();
5360 Builder.restoreIP(*AfterIP);
5361
5362 if (!Builder.GetInsertBlock())
5363 return InsertPointTy();
5364 // for by-ref case, the load is inside of the reduction region
5365 if (!IsByRef[En.index()])
5366 Builder.CreateStore(Reduced, RI.Variable);
5367 }
5368 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5369 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5370 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5371 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5372 Builder.CreateBr(ContinuationBlock);
5373
5374 // Populate the atomic reduction using the atomic elementwise reduction
5375 // function. There are no loads/stores here because they will be happening
5376 // inside the atomic elementwise reduction.
5377 Builder.SetInsertPoint(AtomicRedBlock);
5378 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5379 for (const ReductionInfo &RI : ReductionInfos) {
5381 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5382 if (!AfterIP)
5383 return AfterIP.takeError();
5384 Builder.restoreIP(*AfterIP);
5385 if (!Builder.GetInsertBlock())
5386 return InsertPointTy();
5387 }
5388 Builder.CreateBr(ContinuationBlock);
5389 } else {
5390 Builder.CreateUnreachable();
5391 }
5392
5393 // Populate the outlined reduction function using the elementwise reduction
5394 // function. Partial values are extracted from the type-erased array of
5395 // pointers to private variables.
5396 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5397 IsByRef, /*isGPU=*/false);
5398 if (Err)
5399 return Err;
5400
5401 if (!Builder.GetInsertBlock())
5402 return InsertPointTy();
5403
5404 Builder.SetInsertPoint(ContinuationBlock);
5405 return Builder.saveIP();
5406}
5407
5410 BodyGenCallbackTy BodyGenCB,
5411 FinalizeCallbackTy FiniCB) {
5412 if (!updateToLocation(Loc))
5413 return Loc.IP;
5414
5415 Directive OMPD = Directive::OMPD_master;
5416 uint32_t SrcLocStrSize;
5417 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5418 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5419 Value *ThreadId = getOrCreateThreadID(Ident);
5420 Value *Args[] = {Ident, ThreadId};
5421
5422 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5423 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5424
5425 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5426 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5427
5428 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5429 /*Conditional*/ true, /*hasFinalize*/ true);
5430}
5431
5434 BodyGenCallbackTy BodyGenCB,
5435 FinalizeCallbackTy FiniCB, Value *Filter) {
5437 if (!updateToLocation(Loc))
5438 return Loc.IP;
5439
5440 Directive OMPD = Directive::OMPD_masked;
5441 uint32_t SrcLocStrSize;
5442 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5443 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5444 Value *ThreadId = getOrCreateThreadID(Ident);
5445 Value *Args[] = {Ident, ThreadId, Filter};
5446 Value *ArgsEnd[] = {Ident, ThreadId};
5447
5448 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5449 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5450
5451 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5452 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5453
5454 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5455 /*Conditional*/ true, /*hasFinalize*/ true);
5456}
5457
5459 llvm::FunctionCallee Callee,
5461 const llvm::Twine &Name) {
5462 llvm::CallInst *Call = Builder.CreateCall(
5463 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5464 Call->setDoesNotThrow();
5465 return Call;
5466}
5467
5468// Expects input basic block is dominated by BeforeScanBB.
5469// Once Scan directive is encountered, the code after scan directive should be
5470// dominated by AfterScanBB. Scan directive splits the code sequence to
5471// scan and input phase. Based on whether inclusive or exclusive
5472// clause is used in the scan directive and whether input loop or scan loop
5473// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5474// input loop and second is the scan loop. The code generated handles only
5475// inclusive scans now.
5477 const LocationDescription &Loc, InsertPointTy AllocaIP,
5478 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5479 bool IsInclusive, ScanInfo *ScanRedInfo) {
5480 if (ScanRedInfo->OMPFirstScanLoop) {
5481 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5482 ScanVarsType, ScanRedInfo);
5483 if (Err)
5484 return Err;
5485 }
5486 if (!updateToLocation(Loc))
5487 return Loc.IP;
5488
5489 llvm::Value *IV = ScanRedInfo->IV;
5490
5491 if (ScanRedInfo->OMPFirstScanLoop) {
5492 // Emit buffer[i] = red; at the end of the input phase.
5493 for (size_t i = 0; i < ScanVars.size(); i++) {
5494 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5495 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5496 Type *DestTy = ScanVarsType[i];
5497 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5498 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5499
5500 Builder.CreateStore(Src, Val);
5501 }
5502 }
5503 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5504 emitBlock(ScanRedInfo->OMPScanDispatch,
5505 Builder.GetInsertBlock()->getParent());
5506
5507 if (!ScanRedInfo->OMPFirstScanLoop) {
5508 IV = ScanRedInfo->IV;
5509 // Emit red = buffer[i]; at the entrance to the scan phase.
5510 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5511 for (size_t i = 0; i < ScanVars.size(); i++) {
5512 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5513 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5514 Type *DestTy = ScanVarsType[i];
5515 Value *SrcPtr =
5516 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5517 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5518 Builder.CreateStore(Src, ScanVars[i]);
5519 }
5520 }
5521
5522 // TODO: Update it to CreateBr and remove dead blocks
5523 llvm::Value *CmpI = Builder.getInt1(true);
5524 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5525 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5526 ScanRedInfo->OMPAfterScanBlock);
5527 } else {
5528 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5529 ScanRedInfo->OMPBeforeScanBlock);
5530 }
5531 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5532 Builder.GetInsertBlock()->getParent());
5533 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5534 return Builder.saveIP();
5535}
5536
5537Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5538 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5539 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5540
5541 Builder.restoreIP(AllocaIP);
5542 // Create the shared pointer at alloca IP.
5543 for (size_t i = 0; i < ScanVars.size(); i++) {
5544 llvm::Value *BuffPtr =
5545 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5546 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5547 }
5548
5549 // Allocate temporary buffer by master thread
5550 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5551 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5552 Builder.restoreIP(CodeGenIP);
5553 Value *AllocSpan =
5554 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5555 for (size_t i = 0; i < ScanVars.size(); i++) {
5556 Type *IntPtrTy = Builder.getInt32Ty();
5557 Value *Allocsize = Builder.CreateTypeSize(
5558 IntPtrTy, M.getDataLayout().getTypeAllocSize(ScanVarsType[i]));
5559 Value *Buff =
5560 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5561 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5562 }
5563 return Error::success();
5564 };
5565 // TODO: Perform finalization actions for variables. This has to be
5566 // called for variables which have destructors/finalizers.
5567 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5568
5569 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5570 llvm::Value *FilterVal = Builder.getInt32(0);
5572 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5573
5574 if (!AfterIP)
5575 return AfterIP.takeError();
5576 Builder.restoreIP(*AfterIP);
5577 BasicBlock *InputBB = Builder.GetInsertBlock();
5578 if (InputBB->hasTerminator())
5579 Builder.SetInsertPoint(InputBB->getTerminator());
5580 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5581 if (!AfterIP)
5582 return AfterIP.takeError();
5583 Builder.restoreIP(*AfterIP);
5584
5585 return Error::success();
5586}
5587
5588Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5589 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5590 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5591 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5592 Builder.restoreIP(CodeGenIP);
5593 for (ReductionInfo RedInfo : ReductionInfos) {
5594 Value *PrivateVar = RedInfo.PrivateVariable;
5595 Value *OrigVar = RedInfo.Variable;
5596 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5597 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5598
5599 Type *SrcTy = RedInfo.ElementType;
5600 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5601 "arrayOffset");
5602 Value *Src = Builder.CreateLoad(SrcTy, Val);
5603
5604 Builder.CreateStore(Src, OrigVar);
5605 Builder.CreateFree(Buff);
5606 }
5607 return Error::success();
5608 };
5609 // TODO: Perform finalization actions for variables. This has to be
5610 // called for variables which have destructors/finalizers.
5611 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5612
5613 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5614 Builder.SetInsertPoint(TI);
5615 else
5616 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5617
5618 llvm::Value *FilterVal = Builder.getInt32(0);
5620 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5621
5622 if (!AfterIP)
5623 return AfterIP.takeError();
5624 Builder.restoreIP(*AfterIP);
5625 BasicBlock *InputBB = Builder.GetInsertBlock();
5626 if (InputBB->hasTerminator())
5627 Builder.SetInsertPoint(InputBB->getTerminator());
5628 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5629 if (!AfterIP)
5630 return AfterIP.takeError();
5631 Builder.restoreIP(*AfterIP);
5632 return Error::success();
5633}
5634
5636 const LocationDescription &Loc,
5638 ScanInfo *ScanRedInfo) {
5639
5640 if (!updateToLocation(Loc))
5641 return Loc.IP;
5642 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5643 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5644 Builder.restoreIP(CodeGenIP);
5645 Function *CurFn = Builder.GetInsertBlock()->getParent();
5646 // for (int k = 0; k <= ceil(log2(n)); ++k)
5647 llvm::BasicBlock *LoopBB =
5648 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5649 llvm::BasicBlock *ExitBB =
5650 splitBB(Builder, false, "omp.outer.log.scan.exit");
5652 Builder.GetInsertBlock()->getModule(),
5653 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5654 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5655 llvm::Value *Arg =
5656 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5657 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5659 Builder.GetInsertBlock()->getModule(),
5660 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5661 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5662 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5663 llvm::Value *NMin1 = Builder.CreateNUWSub(
5664 ScanRedInfo->Span,
5665 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5666 Builder.SetInsertPoint(InputBB);
5667 Builder.CreateBr(LoopBB);
5668 emitBlock(LoopBB, CurFn);
5669 Builder.SetInsertPoint(LoopBB);
5670
5671 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5672 // size pow2k = 1;
5673 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5674 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5675 InputBB);
5676 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5677 InputBB);
5678 // for (size i = n - 1; i >= 2 ^ k; --i)
5679 // tmp[i] op= tmp[i-pow2k];
5680 llvm::BasicBlock *InnerLoopBB =
5681 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5682 llvm::BasicBlock *InnerExitBB =
5683 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5684 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5685 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5686 emitBlock(InnerLoopBB, CurFn);
5687 Builder.SetInsertPoint(InnerLoopBB);
5688 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5689 IVal->addIncoming(NMin1, LoopBB);
5690 for (ReductionInfo RedInfo : ReductionInfos) {
5691 Value *ReductionVal = RedInfo.PrivateVariable;
5692 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5693 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5694 Type *DestTy = RedInfo.ElementType;
5695 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5696 Value *LHSPtr =
5697 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5698 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5699 Value *RHSPtr =
5700 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5701 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5702 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5703 llvm::Value *Result;
5704 InsertPointOrErrorTy AfterIP =
5705 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5706 if (!AfterIP)
5707 return AfterIP.takeError();
5708 Builder.CreateStore(Result, LHSPtr);
5709 }
5710 llvm::Value *NextIVal = Builder.CreateNUWSub(
5711 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5712 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5713 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5714 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5715 emitBlock(InnerExitBB, CurFn);
5716 llvm::Value *Next = Builder.CreateNUWAdd(
5717 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5718 Counter->addIncoming(Next, Builder.GetInsertBlock());
5719 // pow2k <<= 1;
5720 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5721 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5722 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5723 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5724 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5725 return Error::success();
5726 };
5727
5728 // TODO: Perform finalization actions for variables. This has to be
5729 // called for variables which have destructors/finalizers.
5730 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5731
5732 llvm::Value *FilterVal = Builder.getInt32(0);
5734 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5735
5736 if (!AfterIP)
5737 return AfterIP.takeError();
5738 Builder.restoreIP(*AfterIP);
5739 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5740
5741 if (!AfterIP)
5742 return AfterIP.takeError();
5743 Builder.restoreIP(*AfterIP);
5744 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5745 if (Err)
5746 return Err;
5747
5748 return AfterIP;
5749}
5750
5751Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5752 llvm::function_ref<Error()> InputLoopGen,
5753 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5754 ScanInfo *ScanRedInfo) {
5755
5756 {
5757 // Emit loop with input phase:
5758 // for (i: 0..<num_iters>) {
5759 // <input phase>;
5760 // buffer[i] = red;
5761 // }
5762 ScanRedInfo->OMPFirstScanLoop = true;
5763 Error Err = InputLoopGen();
5764 if (Err)
5765 return Err;
5766 }
5767 {
5768 // Emit loop with scan phase:
5769 // for (i: 0..<num_iters>) {
5770 // red = buffer[i];
5771 // <scan phase>;
5772 // }
5773 ScanRedInfo->OMPFirstScanLoop = false;
5774 Error Err = ScanLoopGen(Builder);
5775 if (Err)
5776 return Err;
5777 }
5778 return Error::success();
5779}
5780
5781void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5782 Function *Fun = Builder.GetInsertBlock()->getParent();
5783 ScanRedInfo->OMPScanDispatch =
5784 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5785 ScanRedInfo->OMPAfterScanBlock =
5786 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5787 ScanRedInfo->OMPBeforeScanBlock =
5788 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5789 ScanRedInfo->OMPScanLoopExit =
5790 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5791}
5793 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5794 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5795 Module *M = F->getParent();
5796 LLVMContext &Ctx = M->getContext();
5797 Type *IndVarTy = TripCount->getType();
5798
5799 // Create the basic block structure.
5800 BasicBlock *Preheader =
5801 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5802 BasicBlock *Header =
5803 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5804 BasicBlock *Cond =
5805 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5806 BasicBlock *Body =
5807 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5808 BasicBlock *Latch =
5809 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5810 BasicBlock *Exit =
5811 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5812 BasicBlock *After =
5813 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5814
5815 // Use specified DebugLoc for new instructions.
5816 Builder.SetCurrentDebugLocation(DL);
5817
5818 Builder.SetInsertPoint(Preheader);
5819 Builder.CreateBr(Header);
5820
5821 Builder.SetInsertPoint(Header);
5822 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5823 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5824 Builder.CreateBr(Cond);
5825
5826 Builder.SetInsertPoint(Cond);
5827 Value *Cmp =
5828 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5829 Builder.CreateCondBr(Cmp, Body, Exit);
5830
5831 Builder.SetInsertPoint(Body);
5832 Builder.CreateBr(Latch);
5833
5834 Builder.SetInsertPoint(Latch);
5835 // Decide whether the induction variable increment can carry nsw.
5836 //
5837 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5838 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5839 // for valid programs 0 <= count <= INT_MAX always holds.
5840 //
5841 // Collapsed loops: the trip count is a product that can overflow i32 even for
5842 // a conforming program, so nsw is kept only when the product is a constant
5843 // that provably fits, dropped otherwise.
5844 bool HasNSW = Config.hasNoSignedWrap();
5845 if (HasNSW) {
5846 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5847 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5849 if (CI->getValue().ugt(SignedMax))
5850 HasNSW = false;
5851 } else if (IsCollapsed) {
5852 HasNSW = false;
5853 }
5854 }
5855 Value *Next =
5856 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5857 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5858 Builder.CreateBr(Header);
5859 IndVarPHI->addIncoming(Next, Latch);
5860
5861 Builder.SetInsertPoint(Exit);
5862 Builder.CreateBr(After);
5863
5864 // Remember and return the canonical control flow.
5865 LoopInfos.emplace_front();
5866 CanonicalLoopInfo *CL = &LoopInfos.front();
5867
5868 CL->Header = Header;
5869 CL->Cond = Cond;
5870 CL->Latch = Latch;
5871 CL->Exit = Exit;
5872
5873#ifndef NDEBUG
5874 CL->assertOK();
5875#endif
5876 return CL;
5877}
5878
5881 LoopBodyGenCallbackTy BodyGenCB,
5882 Value *TripCount, const Twine &Name) {
5883 BasicBlock *BB = Loc.IP.getNodeParent();
5884 BasicBlock *NextBB = BB->getNextNode();
5885
5886 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5887 NextBB, NextBB, Name);
5888 BasicBlock *After = CL->getAfter();
5889
5890 // If location is not set, don't connect the loop.
5891 if (updateToLocation(Loc)) {
5892 // Split the loop at the insertion point: Branch to the preheader and move
5893 // every following instruction to after the loop (the After BB). Also, the
5894 // new successor is the loop's after block.
5895 spliceBB(Builder, After, /*CreateBranch=*/false);
5896 Builder.CreateBr(CL->getPreheader());
5897 }
5898
5899 // Emit the body content. We do it after connecting the loop to the CFG to
5900 // avoid that the callback encounters degenerate BBs.
5901 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5902 return Err;
5903
5904#ifndef NDEBUG
5905 CL->assertOK();
5906#endif
5907 return CL;
5908}
5909
5911 ScanInfos.emplace_front();
5912 ScanInfo *Result = &ScanInfos.front();
5913 return Result;
5914}
5915
5919 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5920 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5921 LocationDescription ComputeLoc =
5922 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5923 updateToLocation(ComputeLoc);
5924
5926
5928 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5929 ScanRedInfo->Span = TripCount;
5930 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5931 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5932
5933 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5934 Builder.restoreIP(CodeGenIP);
5935 ScanRedInfo->IV = IV;
5936 createScanBBs(ScanRedInfo);
5937 BasicBlock *InputBlock = Builder.GetInsertBlock();
5938 Instruction *Terminator = InputBlock->getTerminator();
5939 assert(Terminator->getNumSuccessors() == 1);
5940 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5941 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5942 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5943 Builder.GetInsertBlock()->getParent());
5944 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5945 emitBlock(ScanRedInfo->OMPScanLoopExit,
5946 Builder.GetInsertBlock()->getParent());
5947 Builder.CreateBr(ContinueBlock);
5948 Builder.SetInsertPoint(
5949 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5950 return BodyGenCB(Builder.saveIP(), IV);
5951 };
5952
5953 const auto &&InputLoopGen = [&]() -> Error {
5955 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5956 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5957 if (!LoopInfo)
5958 return LoopInfo.takeError();
5959 Result.push_back(*LoopInfo);
5960 Builder.restoreIP((*LoopInfo)->getAfterIP());
5961 return Error::success();
5962 };
5963 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5965 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5966 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5967 if (!LoopInfo)
5968 return LoopInfo.takeError();
5969 Result.push_back(*LoopInfo);
5970 Builder.restoreIP((*LoopInfo)->getAfterIP());
5971 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5972 return Error::success();
5973 };
5974 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5975 if (Err)
5976 return Err;
5977 return Result;
5978}
5979
5981 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5982 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5983
5984 // Consider the following difficulties (assuming 8-bit signed integers):
5985 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5986 // DO I = 1, 100, 50
5987 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5988 // DO I = 100, 0, -128
5989
5990 // Start, Stop and Step must be of the same integer type.
5991 auto *IndVarTy = cast<IntegerType>(Start->getType());
5992 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5993 assert(IndVarTy == Step->getType() && "Step type mismatch");
5994
5996
5997 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5998 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5999
6000 // Like Step, but always positive.
6001 Value *Incr = Step;
6002
6003 // Distance between Start and Stop; always positive.
6004 Value *Span;
6005
6006 // Condition whether there are no iterations are executed at all, e.g. because
6007 // UB < LB.
6008 Value *ZeroCmp;
6009
6010 if (IsSigned) {
6011 // Ensure that increment is positive. If not, negate and invert LB and UB.
6012 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6013 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6014 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6015 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6016 Span = Builder.CreateSub(UB, LB, "", false, true);
6017 ZeroCmp = Builder.CreateICmp(
6018 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6019 } else {
6020 Span = Builder.CreateSub(Stop, Start, "", true);
6021 ZeroCmp = Builder.CreateICmp(
6022 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6023 }
6024
6025 Value *CountIfLooping;
6026 if (InclusiveStop) {
6027 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6028 } else {
6029 // Avoid incrementing past stop since it could overflow.
6030 Value *CountIfTwo = Builder.CreateAdd(
6031 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6032 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6033 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6034 }
6035
6036 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6037 "omp_" + Name + ".tripcount");
6038}
6039
6042 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6043 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6044 ScanInfo *ScanRedInfo) {
6045 LocationDescription ComputeLoc =
6046 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6047
6049 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6050
6051 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6052 Builder.restoreIP(CodeGenIP);
6053 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6054 /*HasNSW=*/Config.hasNoSignedWrap());
6055 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6056 /*HasNSW=*/Config.hasNoSignedWrap());
6057 if (InScan)
6058 ScanRedInfo->IV = IndVar;
6059 return BodyGenCB(Builder.saveIP(), IndVar);
6060 };
6061 LocationDescription LoopLoc =
6062 ComputeIP.isValid()
6063 ? Loc
6064 : LocationDescription(Builder.saveIP(),
6065 Builder.getCurrentDebugLocation());
6066 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6067}
6068
6069// Returns an LLVM function to call for initializing loop bounds using OpenMP
6070// static scheduling for composite `distribute parallel for` depending on
6071// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6072// integers as unsigned similarly to CanonicalLoopInfo.
6073static FunctionCallee
6075 OpenMPIRBuilder &OMPBuilder) {
6076 unsigned Bitwidth = Ty->getIntegerBitWidth();
6077 if (Bitwidth == 32)
6078 return OMPBuilder.getOrCreateRuntimeFunction(
6079 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6080 if (Bitwidth == 64)
6081 return OMPBuilder.getOrCreateRuntimeFunction(
6082 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6083 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6084}
6085
6086// Returns an LLVM function to call for initializing loop bounds using OpenMP
6087// static scheduling depending on `type`. Only i32 and i64 are supported by the
6088// runtime. Always interpret integers as unsigned similarly to
6089// CanonicalLoopInfo.
6091 OpenMPIRBuilder &OMPBuilder) {
6092 unsigned Bitwidth = Ty->getIntegerBitWidth();
6093 if (Bitwidth == 32)
6094 return OMPBuilder.getOrCreateRuntimeFunction(
6095 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6096 if (Bitwidth == 64)
6097 return OMPBuilder.getOrCreateRuntimeFunction(
6098 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6099 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6100}
6101
6102OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6103 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6104 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6105 OMPScheduleType DistScheduleSchedType) {
6106 assert(CLI->isValid() && "Requires a valid canonical loop");
6107 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6108 "Require dedicated allocate IP");
6109
6110 // Set up the source location value for OpenMP runtime.
6111 Builder.restoreIP(CLI->getPreheaderIP());
6112 Builder.SetCurrentDebugLocation(DL);
6113
6114 uint32_t SrcLocStrSize;
6115 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6117 switch (LoopType) {
6118 case WorksharingLoopType::ForStaticLoop:
6119 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6120 break;
6121 case WorksharingLoopType::DistributeStaticLoop:
6122 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6123 break;
6124 case WorksharingLoopType::DistributeForStaticLoop:
6125 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6126 break;
6127 }
6128 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6129
6130 // Declare useful OpenMP runtime functions.
6131 Value *IV = CLI->getIndVar();
6132 Type *IVTy = IV->getType();
6133 FunctionCallee StaticInit =
6134 LoopType == WorksharingLoopType::DistributeForStaticLoop
6135 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6136 : getKmpcForStaticInitForType(IVTy, M, *this);
6137 FunctionCallee StaticFini =
6138 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6139
6140 // Allocate space for computed loop bounds as expected by the "init" function.
6141 Builder.SetInsertPoint(
6142 AllocaIP.getNodeParent()->getFirstNonPHIOrDbgOrAlloca());
6143
6144 Type *I32Type = Type::getInt32Ty(M.getContext());
6145 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6146 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6147 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6148 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6149 CLI->setLastIter(PLastIter);
6150
6151 // At the end of the preheader, prepare for calling the "init" function by
6152 // storing the current loop bounds into the allocated space. A canonical loop
6153 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6154 // and produces an inclusive upper bound.
6155 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6156 Constant *Zero = ConstantInt::get(IVTy, 0);
6157 Constant *One = ConstantInt::get(IVTy, 1);
6158 Builder.CreateStore(Zero, PLowerBound);
6159 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6160 Builder.CreateStore(UpperBound, PUpperBound);
6161 Builder.CreateStore(One, PStride);
6162
6163 Value *ThreadNum =
6164 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6165
6166 OMPScheduleType SchedType =
6167 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6168 ? OMPScheduleType::OrderedDistribute
6170 Constant *SchedulingType =
6171 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6172
6173 // Call the "init" function and update the trip count of the loop with the
6174 // value it produced.
6175 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6176 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6177 this](Value *SchedulingType, auto &Builder) {
6178 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6179 PLowerBound, PUpperBound});
6180 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6181 Value *PDistUpperBound =
6182 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6183 Args.push_back(PDistUpperBound);
6184 }
6185 Args.append({PStride, One, Zero});
6186 createRuntimeFunctionCall(StaticInit, Args);
6187 };
6188 BuildInitCall(SchedulingType, Builder);
6189 if (HasDistSchedule &&
6190 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6191 Constant *DistScheduleSchedType = ConstantInt::get(
6192 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6193 // We want to emit a second init function call for the dist_schedule clause
6194 // to the Distribute construct. This should only be done however if a
6195 // Workshare Loop is nested within a Distribute Construct
6196 BuildInitCall(DistScheduleSchedType, Builder);
6197 }
6198 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6199 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6200 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6201 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6202 CLI->setTripCount(TripCount);
6203
6204 // Update all uses of the induction variable except the one in the condition
6205 // block that compares it with the actual upper bound, and the increment in
6206 // the latch block.
6207
6208 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6209 Builder.SetInsertPoint(CLI->getBody(),
6210 CLI->getBody()->getFirstInsertionPt());
6211 Builder.SetCurrentDebugLocation(DL);
6212 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6213 /*HasNSW=*/Config.hasNoSignedWrap());
6214 });
6215
6216 // In the "exit" block, call the "fini" function.
6217 Builder.SetInsertPoint(CLI->getExit(),
6218 CLI->getExit()->getTerminator()->getIterator());
6219 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6220
6221 // Add the barrier if requested.
6222 if (NeedsBarrier) {
6223 InsertPointOrErrorTy BarrierIP =
6225 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6226 /* CheckCancelFlag */ false);
6227 if (!BarrierIP)
6228 return BarrierIP.takeError();
6229 }
6230
6231 InsertPointTy AfterIP = CLI->getAfterIP();
6232 CLI->invalidate();
6233
6234 return AfterIP;
6235}
6236
6237static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6238 LoopInfo &LI);
6239static void addLoopMetadata(CanonicalLoopInfo *Loop,
6241
6243 LLVMContext &Ctx, Loop *Loop,
6245 SmallVector<Metadata *> &LoopMDList) {
6246 SmallSet<BasicBlock *, 8> Reachable;
6247
6248 // Get the basic blocks from the loop in which memref instructions
6249 // can be found.
6250 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6251 // preferably without running any passes.
6252 for (BasicBlock *Block : Loop->getBlocks()) {
6253 if (Block == CLI->getCond() || Block == CLI->getHeader())
6254 continue;
6255 Reachable.insert(Block);
6256 }
6257
6258 // Add access group metadata to memory-access instructions.
6260 for (BasicBlock *BB : Reachable)
6262 // TODO: If the loop has existing parallel access metadata, have
6263 // to combine two lists.
6264 LoopMDList.push_back(MDNode::get(
6265 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6266}
6267
6269OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6270 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6271 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6272 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6273 assert(CLI->isValid() && "Requires a valid canonical loop");
6274 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6275
6276 LLVMContext &Ctx = CLI->getFunction()->getContext();
6277 Value *IV = CLI->getIndVar();
6278 Value *OrigTripCount = CLI->getTripCount();
6279 Type *IVTy = IV->getType();
6280 assert(IVTy->getIntegerBitWidth() <= 64 &&
6281 "Max supported tripcount bitwidth is 64 bits");
6282 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6283 : Type::getInt64Ty(Ctx);
6284 Type *I32Type = Type::getInt32Ty(M.getContext());
6285 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6286 Constant *One = ConstantInt::get(InternalIVTy, 1);
6287
6288 Function *F = CLI->getFunction();
6289 // Blocks must have terminators.
6290 // FIXME: Don't run analyses on incomplete/invalid IR.
6291 SmallVector<Instruction *> UIs;
6292 for (BasicBlock &BB : *F)
6293 if (!BB.hasTerminator())
6294 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6296 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6297 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6298 LoopAnalysis LIA;
6299 LoopInfo &&LI = LIA.run(*F, FAM);
6300 for (Instruction *I : UIs)
6301 I->eraseFromParent();
6302 Loop *L = LI.getLoopFor(CLI->getHeader());
6303 SmallVector<Metadata *> LoopMDList;
6304 if (ChunkSize || DistScheduleChunkSize)
6305 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6306 addLoopMetadata(CLI, LoopMDList);
6307
6308 // Declare useful OpenMP runtime functions.
6309 FunctionCallee StaticInit =
6310 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6311 FunctionCallee StaticFini =
6312 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6313
6314 // Allocate space for computed loop bounds as expected by the "init" function.
6315 Builder.restoreIP(AllocaIP);
6316 Builder.SetCurrentDebugLocation(DL);
6317 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6318 Value *PLowerBound =
6319 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6320 Value *PUpperBound =
6321 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6322 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6323 CLI->setLastIter(PLastIter);
6324
6325 // Set up the source location value for the OpenMP runtime.
6326 Builder.restoreIP(CLI->getPreheaderIP());
6327 Builder.SetCurrentDebugLocation(DL);
6328
6329 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6330 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6331 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6332 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6333 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6334 "distschedulechunksize");
6335 Value *CastedTripCount =
6336 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6337
6338 Constant *SchedulingType =
6339 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6340 Constant *DistSchedulingType =
6341 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6342 Builder.CreateStore(Zero, PLowerBound);
6343 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6344 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6345 Value *UpperBound =
6346 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6347 Builder.CreateStore(UpperBound, PUpperBound);
6348 Builder.CreateStore(One, PStride);
6349
6350 // Call the "init" function and update the trip count of the loop with the
6351 // value it produced.
6352 uint32_t SrcLocStrSize;
6353 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6354 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6355 if (DistScheduleSchedType != OMPScheduleType::None) {
6356 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6357 }
6358 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6359 Value *ThreadNum =
6360 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6361 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6362 PUpperBound, PStride, One,
6363 this](Value *SchedulingType, Value *ChunkSize,
6364 auto &Builder) {
6366 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6367 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6368 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6369 /*pstride=*/PStride, /*incr=*/One,
6370 /*chunk=*/ChunkSize});
6371 };
6372 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6373 if (DistScheduleSchedType != OMPScheduleType::None &&
6374 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6375 SchedType != OMPScheduleType::OrderedDistribute) {
6376 // We want to emit a second init function call for the dist_schedule clause
6377 // to the Distribute construct. This should only be done however if a
6378 // Workshare Loop is nested within a Distribute Construct
6379 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6380 }
6381
6382 // Load values written by the "init" function.
6383 Value *FirstChunkStart =
6384 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6385 Value *FirstChunkStop =
6386 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6387 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6388 Value *ChunkRange =
6389 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6390 Value *NextChunkStride =
6391 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6392
6393 // Create outer "dispatch" loop for enumerating the chunks.
6394 BasicBlock *DispatchEnter = splitBB(Builder, true);
6395 Value *DispatchCounter;
6396
6397 // It is safe to assume this didn't return an error because the callback
6398 // passed into createCanonicalLoop is the only possible error source, and it
6399 // always returns success.
6400 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6401 {Builder.saveIP(), DL},
6402 [&](InsertPointTy BodyIP, Value *Counter) {
6403 DispatchCounter = Counter;
6404 return Error::success();
6405 },
6406 FirstChunkStart, CastedTripCount, NextChunkStride,
6407 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6408 "dispatch"));
6409
6410 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6411 // not have to preserve the canonical invariant.
6412 BasicBlock *DispatchBody = DispatchCLI->getBody();
6413 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6414 BasicBlock *DispatchExit = DispatchCLI->getExit();
6415 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6416 DispatchCLI->invalidate();
6417
6418 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6419 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6420 redirectTo(CLI->getExit(), DispatchLatch, DL);
6421 redirectTo(DispatchBody, DispatchEnter, DL);
6422
6423 // Prepare the prolog of the chunk loop.
6424 Builder.restoreIP(CLI->getPreheaderIP());
6425 Builder.SetCurrentDebugLocation(DL);
6426
6427 // Compute the number of iterations of the chunk loop.
6428 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6429 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6430 Value *IsLastChunk =
6431 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6432 Value *CountUntilOrigTripCount =
6433 Builder.CreateSub(CastedTripCount, DispatchCounter);
6434 Value *ChunkTripCount = Builder.CreateSelect(
6435 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6436 Value *BackcastedChunkTC =
6437 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6438 CLI->setTripCount(BackcastedChunkTC);
6439
6440 // Update all uses of the induction variable except the one in the condition
6441 // block that compares it with the actual upper bound, and the increment in
6442 // the latch block.
6443 Value *BackcastedDispatchCounter =
6444 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6445 CLI->mapIndVar([&](Instruction *) -> Value * {
6446 Builder.restoreIP(CLI->getBodyIP());
6447 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6448 });
6449
6450 // In the "exit" block, call the "fini" function.
6451 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6452 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6453
6454 // Add the barrier if requested.
6455 if (NeedsBarrier) {
6456 InsertPointOrErrorTy AfterIP =
6457 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6458 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6459 if (!AfterIP)
6460 return AfterIP.takeError();
6461 }
6462
6463#ifndef NDEBUG
6464 // Even though we currently do not support applying additional methods to it,
6465 // the chunk loop should remain a canonical loop.
6466 CLI->assertOK();
6467#endif
6468
6469 return DispatchAfter->getFirstInsertionPt();
6470}
6471
6472// Returns an LLVM function to call for executing an OpenMP static worksharing
6473// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6474// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6475static FunctionCallee
6477 WorksharingLoopType LoopType) {
6478 unsigned Bitwidth = Ty->getIntegerBitWidth();
6479 Module &M = OMPBuilder->M;
6480 switch (LoopType) {
6481 case WorksharingLoopType::ForStaticLoop:
6482 if (Bitwidth == 32)
6483 return OMPBuilder->getOrCreateRuntimeFunction(
6484 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6485 if (Bitwidth == 64)
6486 return OMPBuilder->getOrCreateRuntimeFunction(
6487 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6488 break;
6489 case WorksharingLoopType::DistributeStaticLoop:
6490 if (Bitwidth == 32)
6491 return OMPBuilder->getOrCreateRuntimeFunction(
6492 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6493 if (Bitwidth == 64)
6494 return OMPBuilder->getOrCreateRuntimeFunction(
6495 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6496 break;
6497 case WorksharingLoopType::DistributeForStaticLoop:
6498 if (Bitwidth == 32)
6499 return OMPBuilder->getOrCreateRuntimeFunction(
6500 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6501 if (Bitwidth == 64)
6502 return OMPBuilder->getOrCreateRuntimeFunction(
6503 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6504 break;
6505 }
6506 if (Bitwidth != 32 && Bitwidth != 64) {
6507 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6508 }
6509 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6510}
6511
6512// Inserts a call to proper OpenMP Device RTL function which handles
6513// loop worksharing.
6515 WorksharingLoopType LoopType,
6516 BasicBlock *InsertBlock, Value *Ident,
6517 Value *LoopBodyArg, Value *TripCount,
6518 Function &LoopBodyFn, bool NoLoop) {
6519 Type *TripCountTy = TripCount->getType();
6520 Module &M = OMPBuilder->M;
6521 IRBuilder<> &Builder = OMPBuilder->Builder;
6522 FunctionCallee RTLFn =
6523 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6524 SmallVector<Value *, 8> RealArgs;
6525 RealArgs.push_back(Ident);
6526 RealArgs.push_back(&LoopBodyFn);
6527 RealArgs.push_back(LoopBodyArg);
6528 RealArgs.push_back(TripCount);
6529 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6530 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6531 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6532 Builder.restoreIP(std::prev(InsertBlock->end()));
6533 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6534 return;
6535 }
6536 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6537 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6538 Builder.restoreIP(std::prev(InsertBlock->end()));
6539 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6540
6541 RealArgs.push_back(
6542 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6543 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6544 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6545 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6546 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6547 } else {
6548 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6549 }
6550
6551 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6552}
6553
6555 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6556 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6557 WorksharingLoopType LoopType, bool NoLoop) {
6558 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6559 BasicBlock *Preheader = CLI->getPreheader();
6560 Value *TripCount = CLI->getTripCount();
6561
6562 // After loop body outling, the loop body contains only set up
6563 // of loop body argument structure and the call to the outlined
6564 // loop body function. Firstly, we need to move setup of loop body args
6565 // into loop preheader.
6566 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6567 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6568
6569 // The next step is to remove the whole loop. We do not it need anymore.
6570 // That's why make an unconditional branch from loop preheader to loop
6571 // exit block
6572 Builder.restoreIP(Preheader->end());
6573 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6574 Preheader->getTerminator()->eraseFromParent();
6575 Builder.CreateBr(CLI->getExit());
6576
6577 // Delete dead loop blocks
6578 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6579 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6580 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6581 CleanUpInfo.EntryBB = CLI->getHeader();
6582 CleanUpInfo.ExitBB = CLI->getExit();
6583 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6584 DeleteDeadBlocks(BlocksToBeRemoved);
6585
6586 // Find the instruction which corresponds to loop body argument structure
6587 // and remove the call to loop body function instruction.
6588 Value *LoopBodyArg;
6589 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6590 assert(OutlinedFnUser &&
6591 "Expected unique undroppable user of outlined function");
6592 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6593 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6594 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6595 "Expected outlined function call to be located in loop preheader");
6596 // Check in case no argument structure has been passed.
6597 if (OutlinedFnCallInstruction->arg_size() > 1)
6598 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6599 else
6600 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6601 OutlinedFnCallInstruction->eraseFromParent();
6602
6603 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6604 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6605
6606 for (auto &ToBeDeletedItem : ToBeDeleted)
6607 ToBeDeletedItem->eraseFromParent();
6608 CLI->invalidate();
6609}
6610
6611OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6612 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6613 WorksharingLoopType LoopType, bool NeedsBarrier, bool NoLoop) {
6614 uint32_t SrcLocStrSize;
6615 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6617 switch (LoopType) {
6618 case WorksharingLoopType::ForStaticLoop:
6619 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6620 break;
6621 case WorksharingLoopType::DistributeStaticLoop:
6622 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6623 break;
6624 case WorksharingLoopType::DistributeForStaticLoop:
6625 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6626 break;
6627 }
6628 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6629
6630 auto OI = std::make_unique<OutlineInfo>();
6631 OI->OuterAllocBB = CLI->getPreheader();
6632 Function *OuterFn = CLI->getPreheader()->getParent();
6633
6634 // Instructions which need to be deleted at the end of code generation
6635 SmallVector<Instruction *, 4> ToBeDeleted;
6636
6637 OI->OuterAllocBB = AllocaIP.getNodeParent();
6638
6639 // Mark the body loop as region which needs to be extracted
6640 OI->EntryBB = CLI->getBody();
6641 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6642 "omp.prelatch");
6643
6644 // Prepare loop body for extraction
6645 Builder.restoreIP(CLI->getPreheader()->begin());
6646
6647 // Insert new loop counter variable which will be used only in loop
6648 // body.
6649 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6650 Instruction *NewLoopCntLoad =
6651 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6652 // New loop counter instructions are redundant in the loop preheader when
6653 // code generation for workshare loop is finshed. That's why mark them as
6654 // ready for deletion.
6655 ToBeDeleted.push_back(NewLoopCntLoad);
6656 ToBeDeleted.push_back(NewLoopCnt);
6657
6658 // Analyse loop body region. Find all input variables which are used inside
6659 // loop body region.
6660 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6662 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6663
6664 CodeExtractorAnalysisCache CEAC(*OuterFn);
6665 CodeExtractor Extractor(Blocks,
6666 /* DominatorTree */ nullptr,
6667 /* AggregateArgs */ true,
6668 /* BlockFrequencyInfo */ nullptr,
6669 /* BranchProbabilityInfo */ nullptr,
6670 /* AssumptionCache */ nullptr,
6671 /* AllowVarArgs */ true,
6672 /* AllowAlloca */ true,
6673 /* AllocationBlock */ CLI->getPreheader(),
6674 /* DeallocationBlocks */ {},
6675 /* Suffix */ ".omp_wsloop",
6676 /* AggrArgsIn0AddrSpace */ true);
6677
6678 BasicBlock *CommonExit = nullptr;
6679 SetVector<Value *> SinkingCands, HoistingCands;
6680
6681 // Find allocas outside the loop body region which are used inside loop
6682 // body
6683 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6684
6685 // We need to model loop body region as the function f(cnt, loop_arg).
6686 // That's why we replace loop induction variable by the new counter
6687 // which will be one of loop body function argument
6689 CLI->getIndVar()->user_end());
6690 for (auto Use : Users) {
6691 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6692 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6693 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6694 }
6695 }
6696 }
6697 // Make sure that loop counter variable is not merged into loop body
6698 // function argument structure and it is passed as separate variable
6699 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6700
6701 // PostOutline CB is invoked when loop body function is outlined and
6702 // loop body is replaced by call to outlined function. We need to add
6703 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6704 // function will handle loop control logic.
6705 //
6706 OI->PostOutlineCB = [=, ToBeDeletedVec =
6707 std::move(ToBeDeleted)](Function &OutlinedFn) {
6708 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6709 LoopType, NoLoop);
6710 };
6711 addOutlineInfo(std::move(OI));
6712
6713 // Keep the barrier outside the outlined loop body so that every thread
6714 // encounters it, including threads that execute no iterations.
6715 if (NeedsBarrier) {
6716 Builder.SetInsertPoint(CLI->getExit()->getTerminator());
6717 // Standalone distribute loops never request a barrier. For both regular
6718 // worksharing loops and combined distribute/for loops, the barrier is
6719 // associated with the worksharing loop, hence OMPD_for.
6720 InsertPointOrErrorTy BarrierIP =
6721 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6722 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6723 if (!BarrierIP)
6724 return BarrierIP.takeError();
6725 }
6726 return CLI->getAfterIP();
6727}
6728
6731 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6732 bool HasSimdModifier, bool HasMonotonicModifier,
6733 bool HasNonmonotonicModifier, bool HasOrderedClause,
6734 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6735 Value *DistScheduleChunkSize) {
6736 if (Config.isTargetDevice())
6737 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6738 NoLoop);
6739 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6740 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6741 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6742
6743 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6744 OMPScheduleType::ModifierOrdered;
6745 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6746 if (HasDistSchedule) {
6747 DistScheduleSchedType = DistScheduleChunkSize
6748 ? OMPScheduleType::OrderedDistributeChunked
6749 : OMPScheduleType::OrderedDistribute;
6750 }
6751 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6752 case OMPScheduleType::BaseStatic:
6753 case OMPScheduleType::BaseDistribute:
6754 assert((!ChunkSize || !DistScheduleChunkSize) &&
6755 "No chunk size with static-chunked schedule");
6756 if (IsOrdered && !HasDistSchedule)
6757 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6758 NeedsBarrier, ChunkSize);
6759 // FIXME: Monotonicity ignored?
6760 if (DistScheduleChunkSize)
6761 return applyStaticChunkedWorkshareLoop(
6762 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6763 DistScheduleChunkSize, DistScheduleSchedType);
6764 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6765 HasDistSchedule);
6766
6767 case OMPScheduleType::BaseStaticChunked:
6768 case OMPScheduleType::BaseDistributeChunked:
6769 if (IsOrdered && !HasDistSchedule)
6770 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6771 NeedsBarrier, ChunkSize);
6772 // FIXME: Monotonicity ignored?
6773 return applyStaticChunkedWorkshareLoop(
6774 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6775 DistScheduleChunkSize, DistScheduleSchedType);
6776
6777 case OMPScheduleType::BaseRuntime:
6778 case OMPScheduleType::BaseAuto:
6779 case OMPScheduleType::BaseGreedy:
6780 case OMPScheduleType::BaseBalanced:
6781 case OMPScheduleType::BaseSteal:
6782 case OMPScheduleType::BaseRuntimeSimd:
6783 assert(!ChunkSize &&
6784 "schedule type does not support user-defined chunk sizes");
6785 [[fallthrough]];
6786 case OMPScheduleType::BaseGuidedSimd:
6787 case OMPScheduleType::BaseDynamicChunked:
6788 case OMPScheduleType::BaseGuidedChunked:
6789 case OMPScheduleType::BaseGuidedIterativeChunked:
6790 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6791 case OMPScheduleType::BaseStaticBalancedChunked:
6792 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6793 NeedsBarrier, ChunkSize);
6794
6795 default:
6796 llvm_unreachable("Unknown/unimplemented schedule kind");
6797 }
6798}
6799
6800/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6801/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6802/// the runtime. Always interpret integers as unsigned similarly to
6803/// CanonicalLoopInfo.
6804static FunctionCallee
6806 unsigned Bitwidth = Ty->getIntegerBitWidth();
6807 if (Bitwidth == 32)
6808 return OMPBuilder.getOrCreateRuntimeFunction(
6809 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6810 if (Bitwidth == 64)
6811 return OMPBuilder.getOrCreateRuntimeFunction(
6812 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6813 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6814}
6815
6816/// Returns an LLVM function to call for updating the next loop using OpenMP
6817/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6818/// the runtime. Always interpret integers as unsigned similarly to
6819/// CanonicalLoopInfo.
6820static FunctionCallee
6822 unsigned Bitwidth = Ty->getIntegerBitWidth();
6823 if (Bitwidth == 32)
6824 return OMPBuilder.getOrCreateRuntimeFunction(
6825 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6826 if (Bitwidth == 64)
6827 return OMPBuilder.getOrCreateRuntimeFunction(
6828 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6829 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6830}
6831
6832/// Returns an LLVM function to call for finalizing the dynamic loop using
6833/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6834/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6835static FunctionCallee
6837 unsigned Bitwidth = Ty->getIntegerBitWidth();
6838 if (Bitwidth == 32)
6839 return OMPBuilder.getOrCreateRuntimeFunction(
6840 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6841 if (Bitwidth == 64)
6842 return OMPBuilder.getOrCreateRuntimeFunction(
6843 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6844 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6845}
6846
6848OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6849 InsertPointTy AllocaIP,
6850 OMPScheduleType SchedType,
6851 bool NeedsBarrier, Value *Chunk) {
6852 assert(CLI->isValid() && "Requires a valid canonical loop");
6853 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6854 "Require dedicated allocate IP");
6856 "Require valid schedule type");
6857
6858 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6859 OMPScheduleType::ModifierOrdered;
6860
6861 // Set up the source location value for OpenMP runtime.
6862 Builder.SetCurrentDebugLocation(DL);
6863
6864 uint32_t SrcLocStrSize;
6865 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6866 Value *SrcLoc =
6867 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6868
6869 // Declare useful OpenMP runtime functions.
6870 Value *IV = CLI->getIndVar();
6871 Type *IVTy = IV->getType();
6872 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6873 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6874
6875 // Allocate space for computed loop bounds as expected by the "init" function.
6876 Builder.SetInsertPoint(
6877 AllocaIP.getNodeParent()->getFirstNonPHIOrDbgOrAlloca());
6878 Type *I32Type = Type::getInt32Ty(M.getContext());
6879 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6880 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6881 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6882 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6883 CLI->setLastIter(PLastIter);
6884
6885 // At the end of the preheader, prepare for calling the "init" function by
6886 // storing the current loop bounds into the allocated space. A canonical loop
6887 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6888 // and produces an inclusive upper bound.
6889 BasicBlock *PreHeader = CLI->getPreheader();
6890 Builder.SetInsertPoint(PreHeader->getTerminator());
6891 Constant *One = ConstantInt::get(IVTy, 1);
6892 Builder.CreateStore(One, PLowerBound);
6893 Value *UpperBound = CLI->getTripCount();
6894 Builder.CreateStore(UpperBound, PUpperBound);
6895 Builder.CreateStore(One, PStride);
6896
6897 BasicBlock *Header = CLI->getHeader();
6898 BasicBlock *Exit = CLI->getExit();
6899 BasicBlock *Cond = CLI->getCond();
6900 BasicBlock *Latch = CLI->getLatch();
6901 InsertPointTy AfterIP = CLI->getAfterIP();
6902
6903 // The CLI will be "broken" in the code below, as the loop is no longer
6904 // a valid canonical loop.
6905
6906 if (!Chunk)
6907 Chunk = One;
6908
6909 Value *ThreadNum =
6910 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6911
6912 Constant *SchedulingType =
6913 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6914
6915 // Call the "init" function.
6916 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6917 /* LowerBound */ One, UpperBound,
6918 /* step */ One, Chunk});
6919
6920 // An outer loop around the existing one.
6921 BasicBlock *OuterCond = BasicBlock::Create(
6922 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6923 PreHeader->getParent());
6924 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6925 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6927 DynamicNext,
6928 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6929 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6930 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6931 Value *LowerBound =
6932 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6933 Builder.CreateCondBr(MoreWork, Header, Exit);
6934
6935 // Change PHI-node in loop header to use outer cond rather than preheader,
6936 // and set IV to the LowerBound.
6937 Instruction *Phi = &Header->front();
6938 auto *PI = cast<PHINode>(Phi);
6939 PI->setIncomingBlock(0, OuterCond);
6940 PI->setIncomingValue(0, LowerBound);
6941
6942 // Then set the pre-header to jump to the OuterCond
6943 Instruction *Term = PreHeader->getTerminator();
6944 auto *Br = cast<UncondBrInst>(Term);
6945 Br->setSuccessor(OuterCond);
6946
6947 // Modify the inner condition:
6948 // * Use the UpperBound returned from the DynamicNext call.
6949 // * jump to the loop outer loop when done with one of the inner loops.
6950 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6951 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6952 Instruction *Comp = &*Builder.GetInsertPoint();
6953 auto *CI = cast<CmpInst>(Comp);
6954 CI->setOperand(1, UpperBound);
6955 // Redirect the inner exit to branch to outer condition.
6956 Instruction *Branch = &Cond->back();
6957 auto *BI = cast<CondBrInst>(Branch);
6958 assert(BI->getSuccessor(1) == Exit);
6959 BI->setSuccessor(1, OuterCond);
6960
6961 // Call the "fini" function if "ordered" is present in wsloop directive.
6962 if (Ordered) {
6963 Builder.SetInsertPoint(&Latch->back());
6964 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6965 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6966 }
6967
6968 // Add the barrier if requested.
6969 if (NeedsBarrier) {
6970 Builder.SetInsertPoint(&Exit->back());
6971 InsertPointOrErrorTy BarrierIP =
6973 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6974 /* CheckCancelFlag */ false);
6975 if (!BarrierIP)
6976 return BarrierIP.takeError();
6977 }
6978
6979 CLI->invalidate();
6980 return AfterIP;
6981}
6982
6983/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6984/// after this \p OldTarget will be orphaned.
6986 BasicBlock *NewTarget, DebugLoc DL) {
6987 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6988 redirectTo(Pred, NewTarget, DL);
6989}
6990
6992 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6993 // We add a block to BBsToKeep iff we have proven it has an external use.
6995
6996 while (true) {
6997 bool Changed = false;
6998
6999 for (BasicBlock *BB : BBs) {
7000 if (BBsToKeep.contains(BB))
7001 continue;
7002
7003 for (Use &U : BB->uses()) {
7004 auto *UseInst = dyn_cast<Instruction>(U.getUser());
7005 if (!UseInst)
7006 continue;
7007 BasicBlock *UseBB = UseInst->getParent();
7008 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
7009 BBsToKeep.insert(BB);
7010 Changed = true;
7011 break;
7012 }
7013 }
7014 }
7015
7016 if (!Changed)
7017 break;
7018 }
7019
7021 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7022 DeleteDeadBlocks(BBsToDelete);
7023}
7024
7025CanonicalLoopInfo *
7027 InsertPointTy ComputeIP) {
7028 assert(Loops.size() >= 1 && "At least one loop required");
7029 size_t NumLoops = Loops.size();
7030
7031 // Nothing to do if there is already just one loop.
7032 if (NumLoops == 1)
7033 return Loops.front();
7034
7035 CanonicalLoopInfo *Outermost = Loops.front();
7036 CanonicalLoopInfo *Innermost = Loops.back();
7037 BasicBlock *OrigPreheader = Outermost->getPreheader();
7038 BasicBlock *OrigAfter = Outermost->getAfter();
7039 Function *F = OrigPreheader->getParent();
7040
7041 // Loop control blocks that may become orphaned later.
7042 SmallVector<BasicBlock *, 12> OldControlBBs;
7043 OldControlBBs.reserve(6 * Loops.size());
7045 Loop->collectControlBlocks(OldControlBBs);
7046
7047 // Setup the IRBuilder for inserting the trip count computation.
7048 Builder.SetCurrentDebugLocation(DL);
7049 if (ComputeIP.isValid())
7050 Builder.restoreIP(ComputeIP);
7051 else
7052 Builder.restoreIP(Outermost->getPreheaderIP());
7053
7054 // Derive the collapsed' loop trip count.
7055 // TODO: Find common/largest indvar type.
7056 Value *CollapsedTripCount = nullptr;
7057 for (CanonicalLoopInfo *L : Loops) {
7058 assert(L->isValid() &&
7059 "All loops to collapse must be valid canonical loops");
7060 Value *OrigTripCount = L->getTripCount();
7061 if (!CollapsedTripCount) {
7062 CollapsedTripCount = OrigTripCount;
7063 continue;
7064 }
7065
7066 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7067 CollapsedTripCount =
7068 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7069 }
7070
7071 // Create the collapsed loop control flow.
7072 CanonicalLoopInfo *Result =
7073 createLoopSkeleton(DL, CollapsedTripCount, F,
7074 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7075 /*IsCollapsed=*/true);
7076
7077 // Build the collapsed loop body code.
7078 // Start with deriving the input loop induction variables from the collapsed
7079 // one, using a divmod scheme. To preserve the original loops' order, the
7080 // innermost loop use the least significant bits.
7081 Builder.restoreIP(Result->getBodyIP());
7082
7083 Value *Leftover = Result->getIndVar();
7084 SmallVector<Value *> NewIndVars;
7085 NewIndVars.resize(NumLoops);
7086 for (int i = NumLoops - 1; i >= 1; --i) {
7087 Value *OrigTripCount = Loops[i]->getTripCount();
7088
7089 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7090 NewIndVars[i] = NewIndVar;
7091
7092 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7093 }
7094 // Outermost loop gets all the remaining bits.
7095 NewIndVars[0] = Leftover;
7096
7097 // Construct the loop body control flow.
7098 // We progressively construct the branch structure following in direction of
7099 // the control flow, from the leading in-between code, the loop nest body, the
7100 // trailing in-between code, and rejoining the collapsed loop's latch.
7101 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7102 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7103 // its predecessors as sources.
7104 BasicBlock *ContinueBlock = Result->getBody();
7105 BasicBlock *ContinuePred = nullptr;
7106 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7107 BasicBlock *NextSrc) {
7108 if (ContinueBlock)
7109 redirectTo(ContinueBlock, Dest, DL);
7110 else
7111 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7112
7113 ContinueBlock = nullptr;
7114 ContinuePred = NextSrc;
7115 };
7116
7117 // The code before the nested loop of each level.
7118 // Because we are sinking it into the nest, it will be executed more often
7119 // that the original loop. More sophisticated schemes could keep track of what
7120 // the in-between code is and instantiate it only once per thread.
7121 for (size_t i = 0; i < NumLoops - 1; ++i)
7122 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7123
7124 // Connect the loop nest body.
7125 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7126
7127 // The code after the nested loop at each level.
7128 for (size_t i = NumLoops - 1; i > 0; --i)
7129 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7130
7131 // Connect the finished loop to the collapsed loop latch.
7132 ContinueWith(Result->getLatch(), nullptr);
7133
7134 // Replace the input loops with the new collapsed loop.
7135 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7136 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7137
7138 // Replace the input loop indvars with the derived ones.
7139 for (size_t i = 0; i < NumLoops; ++i)
7140 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7141
7142 // Remove unused parts of the input loops.
7143 removeUnusedBlocksFromParent(OldControlBBs);
7144
7145 for (CanonicalLoopInfo *L : Loops)
7146 L->invalidate();
7147
7148#ifndef NDEBUG
7149 Result->assertOK();
7150#endif
7151 return Result;
7152}
7153
7154std::vector<CanonicalLoopInfo *>
7156 ArrayRef<Value *> TileSizes) {
7157 assert(TileSizes.size() == Loops.size() &&
7158 "Must pass as many tile sizes as there are loops");
7159 int NumLoops = Loops.size();
7160 assert(NumLoops >= 1 && "At least one loop to tile required");
7161
7162 CanonicalLoopInfo *OutermostLoop = Loops.front();
7163 CanonicalLoopInfo *InnermostLoop = Loops.back();
7164 Function *F = OutermostLoop->getBody()->getParent();
7165 BasicBlock *InnerEnter = InnermostLoop->getBody();
7166 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7167
7168 // Loop control blocks that may become orphaned later.
7169 SmallVector<BasicBlock *, 12> OldControlBBs;
7170 OldControlBBs.reserve(6 * Loops.size());
7172 Loop->collectControlBlocks(OldControlBBs);
7173
7174 // Collect original trip counts and induction variable to be accessible by
7175 // index. Also, the structure of the original loops is not preserved during
7176 // the construction of the tiled loops, so do it before we scavenge the BBs of
7177 // any original CanonicalLoopInfo.
7178 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7179 for (CanonicalLoopInfo *L : Loops) {
7180 assert(L->isValid() && "All input loops must be valid canonical loops");
7181 OrigTripCounts.push_back(L->getTripCount());
7182 OrigIndVars.push_back(L->getIndVar());
7183 }
7184
7185 // Collect the code between loop headers. These may contain SSA definitions
7186 // that are used in the loop nest body. To be usable with in the innermost
7187 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7188 // these instructions may be executed more often than before the tiling.
7189 // TODO: It would be sufficient to only sink them into body of the
7190 // corresponding tile loop.
7192 for (int i = 0; i < NumLoops - 1; ++i) {
7193 CanonicalLoopInfo *Surrounding = Loops[i];
7194 CanonicalLoopInfo *Nested = Loops[i + 1];
7195
7196 BasicBlock *EnterBB = Surrounding->getBody();
7197 BasicBlock *ExitBB = Nested->getHeader();
7198 InbetweenCode.emplace_back(EnterBB, ExitBB);
7199 }
7200
7201 // Compute the trip counts of the floor loops.
7202 Builder.SetCurrentDebugLocation(DL);
7203 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7204 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7205 for (int i = 0; i < NumLoops; ++i) {
7206 Value *TileSize = TileSizes[i];
7207 Value *OrigTripCount = OrigTripCounts[i];
7208 Type *IVType = OrigTripCount->getType();
7209
7210 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7211 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7212
7213 // 0 if tripcount divides the tilesize, 1 otherwise.
7214 // 1 means we need an additional iteration for a partial tile.
7215 //
7216 // Unfortunately we cannot just use the roundup-formula
7217 // (tripcount + tilesize - 1)/tilesize
7218 // because the summation might overflow. We do not want introduce undefined
7219 // behavior when the untiled loop nest did not.
7220 Value *FloorTripOverflow =
7221 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7222
7223 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7224 Value *FloorTripCount =
7225 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7226 "omp_floor" + Twine(i) + ".tripcount", true);
7227
7228 // Remember some values for later use.
7229 FloorCompleteCount.push_back(FloorCompleteTripCount);
7230 FloorCount.push_back(FloorTripCount);
7231 FloorRems.push_back(FloorTripRem);
7232 }
7233
7234 // Generate the new loop nest, from the outermost to the innermost.
7235 std::vector<CanonicalLoopInfo *> Result;
7236 Result.reserve(NumLoops * 2);
7237
7238 // The basic block of the surrounding loop that enters the nest generated
7239 // loop.
7240 BasicBlock *Enter = OutermostLoop->getPreheader();
7241
7242 // The basic block of the surrounding loop where the inner code should
7243 // continue.
7244 BasicBlock *Continue = OutermostLoop->getAfter();
7245
7246 // Where the next loop basic block should be inserted.
7247 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7248
7249 auto EmbeddNewLoop =
7250 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7251 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7252 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7253 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7254 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7255 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7256
7257 // Setup the position where the next embedded loop connects to this loop.
7258 Enter = EmbeddedLoop->getBody();
7259 Continue = EmbeddedLoop->getLatch();
7260 OutroInsertBefore = EmbeddedLoop->getLatch();
7261 return EmbeddedLoop;
7262 };
7263
7264 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7265 const Twine &NameBase) {
7266 for (auto P : enumerate(TripCounts)) {
7267 CanonicalLoopInfo *EmbeddedLoop =
7268 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7269 Result.push_back(EmbeddedLoop);
7270 }
7271 };
7272
7273 EmbeddNewLoops(FloorCount, "floor");
7274
7275 // Within the innermost floor loop, emit the code that computes the tile
7276 // sizes.
7277 Builder.SetInsertPoint(Enter->getTerminator());
7278 SmallVector<Value *, 4> TileCounts;
7279 for (int i = 0; i < NumLoops; ++i) {
7280 CanonicalLoopInfo *FloorLoop = Result[i];
7281 Value *TileSize = TileSizes[i];
7282
7283 Value *FloorIsEpilogue =
7284 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7285 Value *TileTripCount =
7286 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7287
7288 TileCounts.push_back(TileTripCount);
7289 }
7290
7291 // Create the tile loops.
7292 EmbeddNewLoops(TileCounts, "tile");
7293
7294 // Insert the inbetween code into the body.
7295 BasicBlock *BodyEnter = Enter;
7296 BasicBlock *BodyEntered = nullptr;
7297 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7298 BasicBlock *EnterBB = P.first;
7299 BasicBlock *ExitBB = P.second;
7300
7301 if (BodyEnter)
7302 redirectTo(BodyEnter, EnterBB, DL);
7303 else
7304 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7305
7306 BodyEnter = nullptr;
7307 BodyEntered = ExitBB;
7308 }
7309
7310 // Append the original loop nest body into the generated loop nest body.
7311 if (BodyEnter)
7312 redirectTo(BodyEnter, InnerEnter, DL);
7313 else
7314 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7316
7317 // Replace the original induction variable with an induction variable computed
7318 // from the tile and floor induction variables.
7319 Builder.restoreIP(Result.back()->getBodyIP());
7320 for (int i = 0; i < NumLoops; ++i) {
7321 CanonicalLoopInfo *FloorLoop = Result[i];
7322 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7323 Value *OrigIndVar = OrigIndVars[i];
7324 Value *Size = TileSizes[i];
7325
7326 Value *Scale =
7327 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7328 Value *Shift =
7329 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7330 OrigIndVar->replaceAllUsesWith(Shift);
7331 }
7332
7333 // Remove unused parts of the original loops.
7334 removeUnusedBlocksFromParent(OldControlBBs);
7335
7336 for (CanonicalLoopInfo *L : Loops)
7337 L->invalidate();
7338
7339#ifndef NDEBUG
7340 for (CanonicalLoopInfo *GenL : Result)
7341 GenL->assertOK();
7342#endif
7343 return Result;
7344}
7345
7346/// Attach metadata \p Properties to the basic block described by \p BB. If the
7347/// basic block already has metadata, the basic block properties are appended.
7350 // Nothing to do if no property to attach.
7351 if (Properties.empty())
7352 return;
7353
7354 LLVMContext &Ctx = BB->getContext();
7355 SmallVector<Metadata *> NewProperties;
7356 NewProperties.push_back(nullptr);
7357
7358 // If the basic block already has metadata, prepend it to the new metadata.
7359 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7360 if (Existing)
7361 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7362
7363 append_range(NewProperties, Properties);
7364 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7365 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7366
7367 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7368}
7369
7370/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7371/// loop already has metadata, the loop properties are appended.
7374 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7375
7376 // Attach metadata to the loop's latch
7377 BasicBlock *Latch = Loop->getLatch();
7378 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7380}
7381
7382/// Attach llvm.access.group metadata to the memref instructions of \p Block
7384 LoopInfo &LI) {
7385 for (Instruction &I : *Block) {
7386 if (I.mayReadOrWriteMemory()) {
7387 // TODO: This instruction may already have access group from
7388 // other pragmas e.g. #pragma clang loop vectorize. Append
7389 // so that the existing metadata is not overwritten.
7390 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7391 }
7392 }
7393}
7394
7395CanonicalLoopInfo *
7397 CanonicalLoopInfo *firstLoop = Loops.front();
7398 CanonicalLoopInfo *lastLoop = Loops.back();
7399 Function *F = firstLoop->getPreheader()->getParent();
7400
7401 // Loop control blocks that will become orphaned later
7402 SmallVector<BasicBlock *> oldControlBBs;
7404 Loop->collectControlBlocks(oldControlBBs);
7405
7406 // Collect original trip counts
7407 SmallVector<Value *> origTripCounts;
7408 for (CanonicalLoopInfo *L : Loops) {
7409 assert(L->isValid() && "All input loops must be valid canonical loops");
7410 origTripCounts.push_back(L->getTripCount());
7411 }
7412
7413 Builder.SetCurrentDebugLocation(DL);
7414
7415 // Compute max trip count.
7416 // The fused loop will be from 0 to max(origTripCounts)
7417 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7418 F, firstLoop->getHeader());
7419 Builder.SetInsertPoint(TCBlock);
7420 Value *fusedTripCount = nullptr;
7421 for (CanonicalLoopInfo *L : Loops) {
7422 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7423 Value *origTripCount = L->getTripCount();
7424 if (!fusedTripCount) {
7425 fusedTripCount = origTripCount;
7426 continue;
7427 }
7428 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7429 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7430 ".omp.fuse.tc");
7431 }
7432
7433 // Generate new loop
7434 CanonicalLoopInfo *fused =
7435 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7436 lastLoop->getLatch(), "fused");
7437
7438 // Replace original loops with the fused loop
7439 // Preheader and After are not considered inside the CLI.
7440 // These are used to compute the individual TCs of the loops
7441 // so they have to be put before the resulting fused loop.
7442 // Moving them up for readability.
7443 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7444 Loops[i]->getPreheader()->moveBefore(TCBlock);
7445 Loops[i]->getAfter()->moveBefore(TCBlock);
7446 }
7447 lastLoop->getPreheader()->moveBefore(TCBlock);
7448
7449 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7450 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7451 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7452 }
7453 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7454 redirectTo(TCBlock, fused->getPreheader(), DL);
7455 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7456
7457 // Build the fused body
7458 // Create new Blocks with conditions that jump to the original loop bodies
7460 SmallVector<Value *> condValues;
7461 for (size_t i = 0; i < Loops.size(); ++i) {
7462 BasicBlock *condBlock = BasicBlock::Create(
7463 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7464 Builder.SetInsertPoint(condBlock);
7465 Value *condValue =
7466 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7467 condBBs.push_back(condBlock);
7468 condValues.push_back(condValue);
7469 }
7470 // Join the condition blocks with the bodies of the original loops
7471 redirectTo(fused->getBody(), condBBs[0], DL);
7472 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7473 Builder.SetInsertPoint(condBBs[i]);
7474 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7475 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7476 // Replace the IV with the fused IV
7477 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7478 }
7479 // Last body jumps to the created end body block
7480 Builder.SetInsertPoint(condBBs.back());
7481 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7482 fused->getLatch());
7483 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7484 // Replace the IV with the fused IV
7485 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7486
7487 // The loop latch must have only one predecessor. Currently it is branched to
7488 // from both the last condition block and the last loop body
7489 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7490 "omp.fused.pre_latch");
7491
7492 // Remove unused parts
7493 removeUnusedBlocksFromParent(oldControlBBs);
7494
7495 // Invalidate old CLIs
7496 for (CanonicalLoopInfo *L : Loops)
7497 L->invalidate();
7498
7499#ifndef NDEBUG
7500 fused->assertOK();
7501#endif
7502 return fused;
7503}
7504
7506 LLVMContext &Ctx = Builder.getContext();
7508 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7509 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7510}
7511
7513 LLVMContext &Ctx = Builder.getContext();
7515 Loop, {
7516 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7517 });
7518}
7519
7520void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7521 Value *IfCond, ValueToValueMapTy &VMap,
7522 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7523 const Twine &NamePrefix) {
7524 Function *F = CanonicalLoop->getFunction();
7525
7526 // We can't do
7527 // if (cond) {
7528 // simd_loop;
7529 // } else {
7530 // non_simd_loop;
7531 // }
7532 // because then the CanonicalLoopInfo would only point to one of the loops:
7533 // leading to other constructs operating on the same loop to malfunction.
7534 // Instead generate
7535 // while (...) {
7536 // if (cond) {
7537 // simd_body;
7538 // } else {
7539 // not_simd_body;
7540 // }
7541 // }
7542 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7543 // body at -O3
7544
7545 // Define where if branch should be inserted
7546 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7547
7548 // Create additional blocks for the if statement
7549 BasicBlock *Cond = SplitBeforeIt->getParent();
7550 llvm::LLVMContext &C = Cond->getContext();
7552 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7554 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7555
7556 // Create if condition branch.
7557 Builder.SetInsertPoint(SplitBeforeIt);
7558 Instruction *BrInstr =
7559 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7560 InsertPointTy IP(++BrInstr->getIterator());
7561 // Then block contains branch to omp loop body which needs to be vectorized
7562 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7563 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7564
7565 Builder.SetInsertPoint(ElseBlock);
7566
7567 // Clone loop for the else branch
7569
7570 SmallVector<BasicBlock *, 8> ExistingBlocks;
7571 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7572 ExistingBlocks.push_back(ThenBlock);
7573 ExistingBlocks.append(L->block_begin(), L->block_end());
7574 // Cond is the block that has the if clause condition
7575 // LoopCond is omp_loop.cond
7576 // LoopHeader is omp_loop.header
7577 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7578 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7579 assert(LoopCond && LoopHeader && "Invalid loop structure");
7580 for (BasicBlock *Block : ExistingBlocks) {
7581 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7582 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7583 continue;
7584 }
7585 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7586
7587 // fix name not to be omp.if.then
7588 if (Block == ThenBlock)
7589 NewBB->setName(NamePrefix + ".if.else");
7590
7591 NewBB->moveBefore(CanonicalLoop->getExit());
7592 VMap[Block] = NewBB;
7593 NewBlocks.push_back(NewBB);
7594 }
7595 remapInstructionsInBlocks(NewBlocks, VMap);
7596 Builder.CreateBr(NewBlocks.front());
7597
7598 // The loop latch must have only one predecessor. Currently it is branched to
7599 // from both the 'then' and 'else' branches.
7600 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7601 NamePrefix + ".pre_latch");
7602
7603 // Ensure that the then block is added to the loop so we add the attributes in
7604 // the next step
7605 L->addBasicBlockToLoop(ThenBlock, LI);
7606}
7607
7608unsigned
7610 const StringMap<bool> &Features) {
7611 if (TargetTriple.isX86()) {
7612 if (Features.lookup("avx512f"))
7613 return 512;
7614 else if (Features.lookup("avx"))
7615 return 256;
7616 return 128;
7617 }
7618 if (TargetTriple.isPPC())
7619 return 128;
7620 if (TargetTriple.isWasm())
7621 return 128;
7622 if (TargetTriple.isSystemZ())
7623 return 64;
7624 return 0;
7625}
7626
7628 MapVector<Value *, Value *> AlignedVars,
7629 Value *IfCond, OrderKind Order,
7630 ConstantInt *Simdlen, ConstantInt *Safelen) {
7631 LLVMContext &Ctx = Builder.getContext();
7632
7633 Function *F = CanonicalLoop->getFunction();
7634
7635 // Blocks must have terminators.
7636 // FIXME: Don't run analyses on incomplete/invalid IR.
7638 for (BasicBlock &BB : *F)
7639 if (!BB.hasTerminator())
7640 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7641
7642 // TODO: We should not rely on pass manager. Currently we use pass manager
7643 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7644 // object. We should have a method which returns all blocks between
7645 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7647 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7648 FAM.registerPass([]() { return LoopAnalysis(); });
7649 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7650
7651 LoopAnalysis LIA;
7652 LoopInfo &&LI = LIA.run(*F, FAM);
7653
7654 for (Instruction *I : UIs)
7655 I->eraseFromParent();
7656
7657 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7658 if (AlignedVars.size()) {
7659 InsertPointTy IP = Builder.saveIP();
7660 for (auto &AlignedItem : AlignedVars) {
7661 Value *AlignedPtr = AlignedItem.first;
7662 Value *Alignment = AlignedItem.second;
7663 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7664 Builder.SetInsertPoint(loadInst->getNextNode());
7665 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7666 Alignment);
7667 }
7668 Builder.restoreIP(IP);
7669 }
7670
7671 if (IfCond) {
7672 ValueToValueMapTy VMap;
7673 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7674 }
7675
7677
7678 // Get the basic blocks from the loop in which memref instructions
7679 // can be found.
7680 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7681 // preferably without running any passes.
7682 for (BasicBlock *Block : L->getBlocks()) {
7683 if (Block == CanonicalLoop->getCond() ||
7684 Block == CanonicalLoop->getHeader())
7685 continue;
7686 Reachable.insert(Block);
7687 }
7688
7689 SmallVector<Metadata *> LoopMDList;
7690
7691 // In presence of finite 'safelen', it may be unsafe to mark all
7692 // the memory instructions parallel, because loop-carried
7693 // dependences of 'safelen' iterations are possible.
7694 // If clause order(concurrent) is specified then the memory instructions
7695 // are marked parallel even if 'safelen' is finite.
7696 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7697 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7698
7699 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7700 // versions so we can't add the loop attributes in that case.
7701 if (IfCond) {
7702 // we can still add llvm.loop.parallel_access
7703 addLoopMetadata(CanonicalLoop, LoopMDList);
7704 return;
7705 }
7706
7707 // Use the above access group metadata to create loop level
7708 // metadata, which should be distinct for each loop.
7709 LoopMDList.push_back(
7710 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7711
7712 if (Simdlen || Safelen) {
7713 // If both simdlen and safelen clauses are specified, the value of the
7714 // simdlen parameter must be less than or equal to the value of the safelen
7715 // parameter. Therefore, use safelen only in the absence of simdlen.
7716 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7717 LoopMDList.push_back(
7718 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7719 ConstantAsMetadata::get(VectorizeWidth)}));
7720 }
7721
7722 addLoopMetadata(CanonicalLoop, LoopMDList);
7723}
7724
7725/// Create the TargetMachine object to query the backend for optimization
7726/// preferences.
7727///
7728/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7729/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7730/// needed for the LLVM pass pipline. We use some default options to avoid
7731/// having to pass too many settings from the frontend that probably do not
7732/// matter.
7733///
7734/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7735/// method. If we are going to use TargetMachine for more purposes, especially
7736/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7737/// might become be worth requiring front-ends to pass on their TargetMachine,
7738/// or at least cache it between methods. Note that while fontends such as Clang
7739/// have just a single main TargetMachine per translation unit, "target-cpu" and
7740/// "target-features" that determine the TargetMachine are per-function and can
7741/// be overrided using __attribute__((target("OPTIONS"))).
7742static std::unique_ptr<TargetMachine>
7744 Module *M = F->getParent();
7745
7746 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7747 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7748 const llvm::Triple &Triple = M->getTargetTriple();
7749
7750 std::string Error;
7752 if (!TheTarget)
7753 return {};
7754
7756 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7757 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7758 /*CodeModel=*/std::nullopt, OptLevel));
7759}
7760
7761/// Heuristically determine the best-performant unroll factor for \p CLI. This
7762/// depends on the target processor. We are re-using the same heuristics as the
7763/// LoopUnrollPass.
7765 Function *F = CLI->getFunction();
7766
7767 // Assume the user requests the most aggressive unrolling, even if the rest of
7768 // the code is optimized using a lower setting.
7770 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7771
7772 // Blocks must have terminators.
7773 // FIXME: Don't run analyses on incomplete/invalid IR.
7775 for (BasicBlock &BB : *F)
7776 if (!BB.hasTerminator())
7777 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7778
7780 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7781 FAM.registerPass([]() { return AssumptionAnalysis(); });
7782 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7783 FAM.registerPass([]() { return LoopAnalysis(); });
7784 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7785 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7786 TargetIRAnalysis TIRA;
7787 if (TM)
7788 TIRA = TargetIRAnalysis(
7789 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7790 FAM.registerPass([&]() { return TIRA; });
7791
7792 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7794 ScalarEvolution &&SE = SEA.run(*F, FAM);
7796 DominatorTree &&DT = DTA.run(*F, FAM);
7797 LoopAnalysis LIA;
7798 LoopInfo &&LI = LIA.run(*F, FAM);
7800 AssumptionCache &&AC = ACT.run(*F, FAM);
7802
7803 for (Instruction *I : UIs)
7804 I->eraseFromParent();
7805
7806 Loop *L = LI.getLoopFor(CLI->getHeader());
7807 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7808
7810 L, SE, TTI,
7811 /*BlockFrequencyInfo=*/nullptr,
7812 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7813 /*UserThreshold=*/std::nullopt,
7814 /*UserAllowPartial=*/true,
7815 /*UserAllowRuntime=*/true,
7816 /*UserUpperBound=*/std::nullopt,
7817 /*UserFullUnrollMaxCount=*/std::nullopt);
7818
7819 UP.Force = true;
7820
7821 // Account for additional optimizations taking place before the LoopUnrollPass
7822 // would unroll the loop.
7825
7826 // Use normal unroll factors even if the rest of the code is optimized for
7827 // size.
7830
7831 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7832 << " Threshold=" << UP.Threshold << "\n"
7833 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7834 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7835 << " PartialOptSizeThreshold="
7836 << UP.PartialOptSizeThreshold << "\n");
7837
7838 // Disable peeling.
7841 /*UserAllowPeeling=*/false,
7842 /*UserAllowProfileBasedPeeling=*/false,
7843 /*UnrollingSpecficValues=*/false);
7844
7846 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7847
7848 // Assume that reads and writes to stack variables can be eliminated by
7849 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7850 // size.
7851 for (BasicBlock *BB : L->blocks()) {
7852 for (Instruction &I : *BB) {
7853 Value *Ptr;
7854 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7855 Ptr = Load->getPointerOperand();
7856 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7857 Ptr = Store->getPointerOperand();
7858 } else
7859 continue;
7860
7861 Ptr = Ptr->stripPointerCasts();
7862
7863 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7864 if (Alloca->getParent() == &F->getEntryBlock())
7865 EphValues.insert(&I);
7866 }
7867 }
7868 }
7869
7870 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7871
7872 // Loop is not unrollable if the loop contains certain instructions.
7873 if (!UCE.canUnroll()) {
7874 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7875 return 1;
7876 }
7877
7878 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7879 << "\n");
7880
7881 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7882 // be able to use it.
7883 int TripCount = 0;
7884 int MaxTripCount = 0;
7885 bool MaxOrZero = false;
7886 unsigned TripMultiple = 0;
7887
7888 unsigned Factor =
7889 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7890 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7891 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7892
7893 // This function returns 1 to signal to not unroll a loop.
7894 if (Factor == 0)
7895 return 1;
7896 return Factor;
7897}
7898
7900 int32_t Factor,
7901 CanonicalLoopInfo **UnrolledCLI) {
7902 assert(Factor >= 0 && "Unroll factor must not be negative");
7903
7904 Function *F = Loop->getFunction();
7905 LLVMContext &Ctx = F->getContext();
7906
7907 // If the unrolled loop is not used for another loop-associated directive, it
7908 // is sufficient to add metadata for the LoopUnrollPass.
7909 if (!UnrolledCLI) {
7910 SmallVector<Metadata *, 2> LoopMetadata;
7911 LoopMetadata.push_back(
7912 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7913
7914 if (Factor >= 1) {
7916 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7917 LoopMetadata.push_back(MDNode::get(
7918 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7919 }
7920
7921 addLoopMetadata(Loop, LoopMetadata);
7922 return;
7923 }
7924
7925 // Heuristically determine the unroll factor.
7926 if (Factor == 0)
7928
7929 // No change required with unroll factor 1.
7930 if (Factor == 1) {
7931 *UnrolledCLI = Loop;
7932 return;
7933 }
7934
7935 assert(Factor >= 2 &&
7936 "unrolling only makes sense with a factor of 2 or larger");
7937
7938 Type *IndVarTy = Loop->getIndVarType();
7939
7940 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7941 // unroll the inner loop.
7942 Value *FactorVal =
7943 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7944 /*isSigned=*/false));
7945 std::vector<CanonicalLoopInfo *> LoopNest =
7946 tileLoops(DL, {Loop}, {FactorVal});
7947 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7948 *UnrolledCLI = LoopNest[0];
7949 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7950
7951 // LoopUnrollPass can only fully unroll loops with constant trip count.
7952 // Unroll by the unroll factor with a fallback epilog for the remainder
7953 // iterations if necessary.
7955 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7957 InnerLoop,
7958 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7960 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7961
7962#ifndef NDEBUG
7963 (*UnrolledCLI)->assertOK();
7964#endif
7965}
7966
7969 llvm::Value *BufSize, llvm::Value *CpyBuf,
7970 llvm::Value *CpyFn, llvm::Value *DidIt) {
7971 if (!updateToLocation(Loc))
7972 return Loc.IP;
7973
7974 uint32_t SrcLocStrSize;
7975 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7976 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7977 Value *ThreadId = getOrCreateThreadID(Ident);
7978
7979 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7980
7981 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7982
7983 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7984 createRuntimeFunctionCall(Fn, Args);
7985
7986 return Builder.saveIP();
7987}
7988
7990 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7991 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7993
7994 if (!updateToLocation(Loc))
7995 return Loc.IP;
7996
7997 // If needed allocate and initialize `DidIt` with 0.
7998 // DidIt: flag variable: 1=single thread; 0=not single thread.
7999 llvm::Value *DidIt = nullptr;
8000 if (!CPVars.empty()) {
8001 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
8002 Builder.CreateStore(Builder.getInt32(0), DidIt);
8003 }
8004
8005 Directive OMPD = Directive::OMPD_single;
8006 uint32_t SrcLocStrSize;
8007 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8008 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8009 Value *ThreadId = getOrCreateThreadID(Ident);
8010 Value *Args[] = {Ident, ThreadId};
8011
8012 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8013 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8014
8015 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8016 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8017
8018 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8019 if (Error Err = FiniCB(IP))
8020 return Err;
8021
8022 // The thread that executes the single region must set `DidIt` to 1.
8023 // This is used by __kmpc_copyprivate, to know if the caller is the
8024 // single thread or not.
8025 if (DidIt)
8026 Builder.CreateStore(Builder.getInt32(1), DidIt);
8027
8028 return Error::success();
8029 };
8030
8031 // generates the following:
8032 // if (__kmpc_single()) {
8033 // .... single region ...
8034 // __kmpc_end_single
8035 // }
8036 // __kmpc_copyprivate
8037 // __kmpc_barrier
8038
8039 InsertPointOrErrorTy AfterIP =
8040 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8041 /*Conditional*/ true,
8042 /*hasFinalize*/ true);
8043 if (!AfterIP)
8044 return AfterIP.takeError();
8045
8046 if (DidIt) {
8047 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8048 // NOTE BufSize is currently unused, so just pass 0.
8050 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8051 CPFuncs[I], DidIt);
8052 // NOTE __kmpc_copyprivate already inserts a barrier
8053 } else if (!IsNowait) {
8054 InsertPointOrErrorTy AfterIP =
8056 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8057 /* CheckCancelFlag */ false);
8058 if (!AfterIP)
8059 return AfterIP.takeError();
8060 }
8061 return Builder.saveIP();
8062}
8063
8066 BodyGenCallbackTy BodyGenCB,
8067 FinalizeCallbackTy FiniCB, bool IsNowait) {
8068
8069 if (!updateToLocation(Loc))
8070 return Loc.IP;
8071
8072 // All threads execute the scope body — no conditional entry.
8073 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8074 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8075 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8076 /*IsCancellable=*/false);
8077 if (!AfterIP)
8078 return AfterIP.takeError();
8079
8080 Builder.restoreIP(*AfterIP);
8081 if (!IsNowait) {
8082 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8083 omp::Directive::OMPD_unknown,
8084 /*ForceSimpleCall=*/false,
8085 /*CheckCancelFlag=*/false);
8086 if (!AfterIP)
8087 return AfterIP.takeError();
8088 }
8089 return Builder.saveIP();
8090}
8091
8093 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8094 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8095
8096 if (!updateToLocation(Loc))
8097 return Loc.IP;
8098
8099 Directive OMPD = Directive::OMPD_critical;
8100 uint32_t SrcLocStrSize;
8101 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8102 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8103 Value *ThreadId = getOrCreateThreadID(Ident);
8104 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8105 Value *Args[] = {Ident, ThreadId, LockVar};
8106
8107 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8108 Function *RTFn = nullptr;
8109 if (HintInst) {
8110 // Add Hint to entry Args and create call
8111 EnterArgs.push_back(HintInst);
8112 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8113 } else {
8114 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8115 }
8116 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8117
8118 Function *ExitRTLFn =
8119 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8120 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8121
8122 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8123 /*Conditional*/ false, /*hasFinalize*/ true);
8124}
8125
8128 InsertPointTy AllocaIP, unsigned NumLoops,
8129 ArrayRef<llvm::Value *> StoreValues,
8130 const Twine &Name, bool IsDependSource) {
8131 assert(
8132 llvm::all_of(StoreValues,
8133 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8134 "OpenMP runtime requires depend vec with i64 type");
8135
8136 if (!updateToLocation(Loc))
8137 return Loc.IP;
8138
8139 // Allocate space for vector and generate alloc instruction.
8140 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8141 Builder.restoreIP(AllocaIP);
8142 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8143 ArgsBase->setAlignment(Align(8));
8145
8146 // Store the index value with offset in depend vector.
8147 for (unsigned I = 0; I < NumLoops; ++I) {
8148 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8149 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8150 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8151 STInst->setAlignment(Align(8));
8152 }
8153
8154 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8155 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8156
8157 uint32_t SrcLocStrSize;
8158 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8159 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8160 Value *ThreadId = getOrCreateThreadID(Ident);
8161 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8162
8163 Function *RTLFn = nullptr;
8164 if (IsDependSource)
8165 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8166 else
8167 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8168 createRuntimeFunctionCall(RTLFn, Args);
8169
8170 return Builder.saveIP();
8171}
8172
8174 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8175 FinalizeCallbackTy FiniCB, bool IsThreads) {
8176 if (!updateToLocation(Loc))
8177 return Loc.IP;
8178
8179 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8180 Instruction *EntryCall = nullptr;
8181 Instruction *ExitCall = nullptr;
8182
8183 if (IsThreads) {
8184 uint32_t SrcLocStrSize;
8185 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8186 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8187 Value *ThreadId = getOrCreateThreadID(Ident);
8188 Value *Args[] = {Ident, ThreadId};
8189
8190 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8191 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8192
8193 Function *ExitRTLFn =
8194 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8195 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8196 }
8197
8198 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8199 /*Conditional*/ false, /*hasFinalize*/ true);
8200}
8201
8202OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8203 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8204 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8205 bool HasFinalize, bool IsCancellable) {
8206
8207 if (HasFinalize)
8208 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8209
8210 // Create inlined region's entry and body blocks, in preparation
8211 // for conditional creation
8212 BasicBlock *EntryBB = Builder.GetInsertBlock();
8213 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8215 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8216 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8217 BasicBlock *FiniBB =
8218 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8219
8220 Builder.SetInsertPoint(EntryBB->getTerminator());
8221 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8222
8223 // generate body
8224 if (Error Err =
8225 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8226 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8227 return Err;
8228
8229 // emit exit call and do any needed finalization.
8230 auto FinIP = FiniBB->getFirstInsertionPt();
8231 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8232 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8233 "Unexpected control flow graph state!!");
8234 InsertPointOrErrorTy AfterIP =
8235 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8236 if (!AfterIP)
8237 return AfterIP.takeError();
8238
8239 // If we are skipping the region of a non conditional, remove the exit
8240 // block, and clear the builder's insertion point.
8241 assert(SplitPos->getParent() == ExitBB &&
8242 "Unexpected Insertion point location!");
8243 auto merged = MergeBlockIntoPredecessor(ExitBB);
8244 BasicBlock *ExitPredBB = SplitPos->getParent();
8245 auto InsertBB = merged ? ExitPredBB : ExitBB;
8247 SplitPos->eraseFromParent();
8248 Builder.SetInsertPoint(InsertBB);
8249
8250 return Builder.saveIP();
8251}
8252
8253OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8254 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8255 // if nothing to do, Return current insertion point.
8256 if (!Conditional || !EntryCall)
8257 return Builder.saveIP();
8258
8259 BasicBlock *EntryBB = Builder.GetInsertBlock();
8260 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8261 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8262 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8263
8264 // Emit thenBB and set the Builder's insertion point there for
8265 // body generation next. Place the block after the current block.
8266 Function *CurFn = EntryBB->getParent();
8267 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8268
8269 // Move Entry branch to end of ThenBB, and replace with conditional
8270 // branch (If-stmt)
8271 Instruction *EntryBBTI = EntryBB->getTerminator();
8272 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8273 EntryBBTI->removeFromParent();
8274 Builder.SetInsertPoint(UI);
8275 Builder.Insert(EntryBBTI);
8276 UI->eraseFromParent();
8277 Builder.SetInsertPoint(ThenBB->getTerminator());
8278
8279 // return an insertion point to ExitBB.
8280 return ExitBB->getFirstInsertionPt();
8281}
8282
8283OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8284 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8285 bool HasFinalize) {
8286
8287 Builder.restoreIP(FinIP);
8288
8289 // If there is finalization to do, emit it before the exit call
8290 if (HasFinalize) {
8291 assert(!FinalizationStack.empty() &&
8292 "Unexpected finalization stack state!");
8293
8294 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8295 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8296
8297 BasicBlock *FinBB = FinIP.getNodeParent();
8298 if (Error Err = Fi.mergeFiniBB(Builder, FinBB))
8299 return std::move(Err);
8300
8301 // Exit condition: insertion point is before the terminator of the new Fini
8302 // block
8303 Builder.SetInsertPoint(FinBB->getTerminator());
8304 }
8305
8306 if (!ExitCall)
8307 return Builder.saveIP();
8308
8309 // place the Exitcall as last instruction before Finalization block terminator
8310 ExitCall->removeFromParent();
8311 Builder.Insert(ExitCall);
8312
8313 return ExitCall->getIterator();
8314}
8315
8317 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8318 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8319 if (!IP.isValid())
8320 return IP;
8321
8323
8324 // creates the following CFG structure
8325 // OMP_Entry : (MasterAddr != PrivateAddr)?
8326 // F T
8327 // | \
8328 // | copin.not.master
8329 // | /
8330 // v /
8331 // copyin.not.master.end
8332 // |
8333 // v
8334 // OMP.Entry.Next
8335
8336 BasicBlock *OMP_Entry = IP.getNodeParent();
8337 Function *CurFn = OMP_Entry->getParent();
8338 BasicBlock *CopyBegin =
8339 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8340 BasicBlock *CopyEnd = nullptr;
8341
8342 // If entry block is terminated, split to preserve the branch to following
8343 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8345 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8346 "copyin.not.master.end");
8347 OMP_Entry->getTerminator()->eraseFromParent();
8348 } else {
8349 CopyEnd =
8350 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8351 }
8352
8353 Builder.SetInsertPoint(OMP_Entry);
8354 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8355 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8356 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8357 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8358
8359 Builder.SetInsertPoint(CopyBegin);
8360 if (BranchtoEnd)
8361 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8362
8363 return Builder.saveIP();
8364}
8365
8367 Value *Size, Value *Allocator,
8368 std::string Name) {
8370 if (!updateToLocation(Loc))
8371 return nullptr;
8372
8373 uint32_t SrcLocStrSize;
8374 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8375 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8376 Value *ThreadId = getOrCreateThreadID(Ident);
8377 Value *Args[] = {ThreadId, Size, Allocator};
8378
8379 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8380
8381 return createRuntimeFunctionCall(Fn, Args, Name);
8382}
8383
8385 Value *Align, Value *Size,
8386 Value *Allocator,
8387 std::string Name) {
8389 if (!updateToLocation(Loc))
8390 return nullptr;
8391
8392 uint32_t SrcLocStrSize;
8393 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8394 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8395 Value *ThreadId = getOrCreateThreadID(Ident);
8396 Value *Args[] = {ThreadId, Align, Size, Allocator};
8397
8398 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8399
8400 return Builder.CreateCall(Fn, Args, Name);
8401}
8402
8404 Value *Addr, Value *Allocator,
8405 std::string Name) {
8407 if (!updateToLocation(Loc))
8408 return nullptr;
8409
8410 uint32_t SrcLocStrSize;
8411 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8412 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8413 Value *ThreadId = getOrCreateThreadID(Ident);
8414 Value *Args[] = {ThreadId, Addr, Allocator};
8415 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8416 return createRuntimeFunctionCall(Fn, Args, Name);
8417}
8418
8420 Value *Size,
8421 const Twine &Name) {
8424
8425 Value *Args[] = {Size};
8426 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8427 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8429 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8430 return Call;
8431}
8432
8434 Type *VarType,
8435 const Twine &Name) {
8436 return createOMPAllocShared(
8437 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8438}
8439
8441 Value *Addr, Value *Size,
8442 const Twine &Name) {
8445
8446 Value *Args[] = {Addr, Size};
8447 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8448 return Builder.CreateCall(Fn, Args, Name);
8449}
8450
8452 Value *Addr, Type *VarType,
8453 const Twine &Name) {
8454 return createOMPFreeShared(
8455 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8456 Name);
8457}
8458
8460 const LocationDescription &Loc, Value *InteropVar,
8462 Value *DependenceAddress, bool HaveNowaitClause) {
8465
8466 uint32_t SrcLocStrSize;
8467 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8468 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8469 Value *ThreadId = getOrCreateThreadID(Ident);
8470 if (Device == nullptr)
8472 else if (Device->getType() != Int32)
8473 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8474 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8475 if (NumDependences == nullptr) {
8476 NumDependences = ConstantInt::get(Int32, 0);
8477 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8478 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8479 }
8480 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8481 Value *Args[] = {
8482 Ident, ThreadId, InteropVar, InteropTypeVal,
8483 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8484
8485 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8486
8487 return createRuntimeFunctionCall(Fn, Args);
8488}
8489
8491 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8492 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8495
8496 uint32_t SrcLocStrSize;
8497 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8498 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8499 Value *ThreadId = getOrCreateThreadID(Ident);
8500 if (Device == nullptr)
8502 else if (Device->getType() != Int32)
8503 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8504 if (NumDependences == nullptr) {
8505 NumDependences = ConstantInt::get(Int32, 0);
8506 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8507 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8508 }
8509 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8510 Value *Args[] = {
8511 Ident, ThreadId, InteropVar, Device,
8512 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8513
8514 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8515
8516 return createRuntimeFunctionCall(Fn, Args);
8517}
8518
8520 Value *InteropVar, Value *Device,
8521 Value *NumDependences,
8522 Value *DependenceAddress,
8523 bool HaveNowaitClause) {
8526 uint32_t SrcLocStrSize;
8527 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8528 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8529 Value *ThreadId = getOrCreateThreadID(Ident);
8530 if (Device == nullptr)
8532 else if (Device->getType() != Int32)
8533 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8534 if (NumDependences == nullptr) {
8535 NumDependences = ConstantInt::get(Int32, 0);
8536 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8537 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8538 }
8539 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8540 Value *Args[] = {
8541 Ident, ThreadId, InteropVar, Device,
8542 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8543
8544 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8545
8546 return createRuntimeFunctionCall(Fn, Args);
8547}
8548
8551 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8554
8555 uint32_t SrcLocStrSize;
8556 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8557 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8558 Value *ThreadId = getOrCreateThreadID(Ident);
8559 Constant *ThreadPrivateCache =
8560 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8561 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8562
8563 Function *Fn =
8564 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8565
8566 return createRuntimeFunctionCall(Fn, Args);
8567}
8568
8570 const LocationDescription &Loc,
8572 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8573 "expected num_threads and num_teams to be specified");
8574
8575 if (!updateToLocation(Loc))
8576 return nullptr;
8577
8578 uint32_t SrcLocStrSize;
8579 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8580 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8581 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8582 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8583 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8584 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8585 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8586 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8587
8588 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8589 Function *Kernel = DebugKernelWrapper;
8590
8591 // We need to strip the debug prefix to get the correct kernel name.
8592 StringRef KernelName = Kernel->getName();
8593 const std::string DebugPrefix = "_debug__";
8594 if (KernelName.ends_with(DebugPrefix)) {
8595 KernelName = KernelName.drop_back(DebugPrefix.length());
8596 Kernel = M.getFunction(KernelName);
8597 assert(Kernel && "Expected the real kernel to exist");
8598 }
8599
8600 // Manifest the launch configuration in the metadata matching the kernel
8601 // environment.
8602 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8603 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8604 Attrs.MaxTeams.front());
8605
8606 // Don't derive or write thread bounds for Bare kernels.
8607 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8608 if (Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_BARE) {
8609 // If MaxThreads is not set and needs adjustment, select the maximum
8610 // between the default workgroup size and the MinThreads value. This is
8611 // only meaningful for targets with a known grid value (i.e. GPUs); for
8612 // other targets (e.g. host kernels) leave it unset so the runtime falls
8613 // back to its own device-specific default.
8614 if (MaxThreadsVal < 0 && UseDefaultMaxThreads && hasGridValue(T))
8615 MaxThreadsVal =
8616 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8617 Attrs.MinThreads.front());
8618
8619 // Generic mode runs the main thread on a warp of its own, past
8620 // thread_limit. Reserve the widest warp any target has. Not on SPIR-V,
8621 // causes problems with Level Zero.
8622 if (MaxThreadsVal > 0 &&
8623 Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC && hasGridValue(T) &&
8624 !T.isSPIRV())
8625 MaxThreadsVal = int32_t(
8626 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8627 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8628
8629 if (MaxThreadsVal > 0)
8630 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8631 MaxThreadsVal);
8632 }
8633
8634 Constant *MinThreads =
8635 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8636 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8637 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8638 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8639 Constant *ReductionDataSize =
8640 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8641
8642 const DataLayout &DL = M.getDataLayout();
8643
8644 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8645 Constant *DynamicEnvironmentInitializer =
8646 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8647 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8648 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8649 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8650 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8651 DL.getDefaultGlobalsAddressSpace());
8652 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8653
8654 Constant *DynamicEnvironment =
8655 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8656 ? DynamicEnvironmentGV
8657 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8658 DynamicEnvironmentPtr);
8659
8660 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8661 ConfigurationEnvironment, {
8662 UseGenericStateMachineVal,
8663 MayUseNestedParallelismVal,
8664 IsSPMDVal,
8665 MinThreads,
8666 MaxThreads,
8667 MinTeams,
8668 MaxTeams,
8669 ReductionDataSize,
8670 });
8671 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8672 KernelEnvironment, {
8673 ConfigurationEnvironmentInitializer,
8674 Ident,
8675 DynamicEnvironment,
8676 });
8677 std::string KernelEnvironmentName =
8678 (KernelName + "_kernel_environment").str();
8679 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8680 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8681 KernelEnvironmentInitializer, KernelEnvironmentName,
8682 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8683 DL.getDefaultGlobalsAddressSpace());
8684 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8685
8686 return KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8687 ? KernelEnvironmentGV
8688 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8689 KernelEnvironmentPtr);
8690}
8691
8693 const LocationDescription &Loc,
8695 Constant *KernelEnvironment = emitKernelEnvironment(Loc, Attrs);
8696 if (!KernelEnvironment)
8697 return Loc.IP;
8698
8699 if (!updateToLocation(Loc))
8700 return Loc.IP;
8701
8702 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8704 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8705
8706 Value *KernelLaunchEnvironment =
8707 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8708 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8709 KernelLaunchEnvironment =
8710 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8711 ? KernelLaunchEnvironment
8712 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8713 KernelLaunchEnvParamTy);
8714 CallInst *ThreadKind = createRuntimeFunctionCall(
8715 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8716
8717 Value *ExecUserCode = Builder.CreateICmpEQ(
8718 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8719 "exec_user_code");
8720
8721 // ThreadKind = __kmpc_target_init(...)
8722 // if (ThreadKind == -1)
8723 // user_code
8724 // else
8725 // return;
8726
8727 auto *UI = Builder.CreateUnreachable();
8728 BasicBlock *CheckBB = UI->getParent();
8729 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8730
8731 BasicBlock *WorkerExitBB = BasicBlock::Create(
8732 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8733 Builder.SetInsertPoint(WorkerExitBB);
8734 Builder.CreateRetVoid();
8735
8736 auto *CheckBBTI = CheckBB->getTerminator();
8737 Builder.SetInsertPoint(CheckBBTI);
8738 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8739
8740 CheckBBTI->eraseFromParent();
8741 UI->eraseFromParent();
8742
8743 // Continue in the "user_code" block, see diagram above and in
8744 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8745 return UserCodeEntryBB->getFirstInsertionPt();
8746}
8747
8749 int32_t TeamsReductionDataSize) {
8750 if (!updateToLocation(Loc))
8751 return;
8752
8754 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8755
8757
8758 if (!TeamsReductionDataSize)
8759 return;
8760
8761 Function *Kernel = Builder.GetInsertBlock()->getParent();
8762 // We need to strip the debug prefix to get the correct kernel name.
8763 StringRef KernelName = Kernel->getName();
8764 const std::string DebugPrefix = "_debug__";
8765 if (KernelName.ends_with(DebugPrefix))
8766 KernelName = KernelName.drop_back(DebugPrefix.length());
8767 auto *KernelEnvironmentGV =
8768 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8769 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8770 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8771 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8772 KernelEnvironmentInitializer,
8773 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8774 KernelEnvironmentGV->setInitializer(NewInitializer);
8775}
8776
8777static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8778 bool Min) {
8779 if (Kernel.hasFnAttribute(Name)) {
8780 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8781 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8782 }
8783 Kernel.addFnAttr(Name, llvm::utostr(Value));
8784}
8785
8786std::pair<int32_t, int32_t>
8788 int32_t ThreadLimit =
8789 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8790
8791 if (T.isAMDGPU()) {
8792 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8793 if (!Attr.isValid() || !Attr.isStringAttribute())
8794 return {0, ThreadLimit};
8795 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8796 int32_t LB, UB;
8797 if (!llvm::to_integer(UBStr, UB, 10))
8798 return {0, ThreadLimit};
8799 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8800 if (!llvm::to_integer(LBStr, LB, 10))
8801 return {0, UB};
8802 return {LB, UB};
8803 }
8804
8805 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8806 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8807 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8808 }
8809 return {0, ThreadLimit};
8810}
8811
8813 Function &Kernel, int32_t LB,
8814 int32_t UB) {
8815 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8816
8817 if (T.isAMDGPU()) {
8818 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8819 llvm::utostr(LB) + "," + llvm::utostr(UB));
8820 return;
8821 }
8822
8824}
8825
8826std::pair<int32_t, int32_t>
8828 // TODO: Read from backend annotations if available.
8829 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8830}
8831
8833 int32_t LB, int32_t UB) {
8834 if (UB > 0) {
8835 if (T.isNVPTX())
8837 if (T.isAMDGPU())
8838 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8839 }
8840
8841 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8842}
8843
8844void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8845 Function *OutlinedFn) {
8846 if (Config.isTargetDevice()) {
8848 // TODO: Determine if DSO local can be set to true.
8849 OutlinedFn->setDSOLocal(false);
8851 if (T.isAMDGCN())
8853 else if (T.isNVPTX())
8855 else if (T.isSPIRV())
8857 }
8858}
8859
8860Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8861 StringRef EntryFnIDName) {
8862 if (Config.isTargetDevice()) {
8863 assert(OutlinedFn && "The outlined function must exist if embedded");
8864 return OutlinedFn;
8865 }
8866
8867 return new GlobalVariable(
8868 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8869 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8870}
8871
8872Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8873 StringRef EntryFnName) {
8874 if (OutlinedFn)
8875 return OutlinedFn;
8876
8877 assert(!M.getGlobalVariable(EntryFnName, true) &&
8878 "Named kernel already exists?");
8879 return new GlobalVariable(
8880 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8881 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8882}
8883
8885 TargetRegionEntryInfo &EntryInfo,
8886 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8887 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8888
8889 SmallString<64> EntryFnName;
8890 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8891
8892 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8893 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8894 if (!CBResult)
8895 return CBResult.takeError();
8896 OutlinedFn = *CBResult;
8897 } else {
8898 OutlinedFn = nullptr;
8899 }
8900
8901 // If this target outline function is not an offload entry, we don't need to
8902 // register it. This may be in the case of a false if clause, or if there are
8903 // no OpenMP targets.
8904 if (!IsOffloadEntry)
8905 return Error::success();
8906
8907 std::string EntryFnIDName =
8908 Config.isTargetDevice()
8909 ? std::string(EntryFnName)
8910 : createPlatformSpecificName({EntryFnName, "region_id"});
8911
8912 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8913 EntryFnName, EntryFnIDName);
8914 return Error::success();
8915}
8916
8918 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8919 StringRef EntryFnName, StringRef EntryFnIDName) {
8920 if (OutlinedFn)
8921 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8922 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8923 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8924 OffloadInfoManager.registerTargetRegionEntryInfo(
8925 EntryInfo, EntryAddr, OutlinedFnID,
8927 return OutlinedFnID;
8928}
8929
8931 const LocationDescription &Loc, InsertPointTy AllocaIP,
8932 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8933 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8934 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8935 omp::RuntimeFunction *MapperFunc,
8937 BodyGenTy BodyGenType)>
8938 BodyGenCB,
8939 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8940 if (!updateToLocation(Loc))
8941 return InsertPointTy();
8942
8943 Builder.restoreIP(CodeGenIP);
8944
8945 bool IsStandAlone = !BodyGenCB;
8946 MapInfosTy *MapInfo;
8947 // Generate the code for the opening of the data environment. Capture all the
8948 // arguments of the runtime call by reference because they are used in the
8949 // closing of the region.
8950 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8951 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8952 MapInfo = &GenMapInfoCB(Builder.saveIP());
8953 if (Error Err = emitOffloadingArrays(
8954 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8955 /*IsNonContiguous=*/true, DeviceAddrCB))
8956 return Err;
8957
8958 TargetDataRTArgs RTArgs;
8960
8961 // Emit the number of elements in the offloading arrays.
8962 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8963
8964 // Source location for the ident struct
8965 if (!SrcLocInfo) {
8966 uint32_t SrcLocStrSize;
8967 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8968 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8969 }
8970
8971 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8972 SrcLocInfo, DeviceID,
8973 PointerNum, RTArgs.BasePointersArray,
8974 RTArgs.PointersArray, RTArgs.SizesArray,
8975 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8976 RTArgs.MappersArray};
8977
8978 if (IsStandAlone) {
8979 assert(MapperFunc && "MapperFunc missing for standalone target data");
8980
8981 auto TaskBodyCB = [&](Value *, Value *,
8983 if (Info.HasNoWait) {
8984 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8988 }
8989
8991 OffloadingArgs);
8992
8993 if (Info.HasNoWait) {
8994 BasicBlock *OffloadContBlock =
8995 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8996 Function *CurFn = Builder.GetInsertBlock()->getParent();
8997 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8998 Builder.restoreIP(Builder.saveIP());
8999 }
9000 return Error::success();
9001 };
9002
9003 bool RequiresOuterTargetTask = Info.HasNoWait;
9004 if (!RequiresOuterTargetTask)
9005 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
9006 /*TargetTaskAllocaIP=*/{}));
9007 else
9008 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
9009 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
9010 } else {
9011 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
9012 omp::OMPRTL___tgt_target_data_begin_mapper);
9013
9014 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
9015
9016 for (auto DeviceMap : Info.DevicePtrInfoMap) {
9017 if (isa<AllocaInst>(DeviceMap.second.second)) {
9018 auto *LI =
9019 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
9020 Builder.CreateStore(LI, DeviceMap.second.second);
9021 }
9022 }
9023
9024 // If device pointer privatization is required, emit the body of the
9025 // region here. It will have to be duplicated: with and without
9026 // privatization.
9027 InsertPointOrErrorTy AfterIP =
9028 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9029 if (!AfterIP)
9030 return AfterIP.takeError();
9031 Builder.restoreIP(*AfterIP);
9032 }
9033 return Error::success();
9034 };
9035
9036 // If we need device pointer privatization, we need to emit the body of the
9037 // region with no privatization in the 'else' branch of the conditional.
9038 // Otherwise, we don't have to do anything.
9039 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9040 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9041 InsertPointOrErrorTy AfterIP =
9042 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9043 if (!AfterIP)
9044 return AfterIP.takeError();
9045 Builder.restoreIP(*AfterIP);
9046 return Error::success();
9047 };
9048
9049 // Generate code for the closing of the data region.
9050 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9051 ArrayRef<BasicBlock *> DeallocBlocks) {
9052 TargetDataRTArgs RTArgs;
9053 Info.EmitDebug = !MapInfo->Names.empty();
9054 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9055
9056 // Emit the number of elements in the offloading arrays.
9057 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9058
9059 // Source location for the ident struct
9060 if (!SrcLocInfo) {
9061 uint32_t SrcLocStrSize;
9062 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9063 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9064 }
9065
9066 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9067 PointerNum, RTArgs.BasePointersArray,
9068 RTArgs.PointersArray, RTArgs.SizesArray,
9069 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9070 RTArgs.MappersArray};
9071 Function *EndMapperFunc =
9072 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9073
9074 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9075 return Error::success();
9076 };
9077
9078 // We don't have to do anything to close the region if the if clause evaluates
9079 // to false.
9080 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9081 ArrayRef<BasicBlock *> DeallocBlocks) {
9082 return Error::success();
9083 };
9084
9085 Error Err = [&]() -> Error {
9086 if (BodyGenCB) {
9087 Error Err = [&]() {
9088 if (IfCond)
9089 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9090 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9091 }();
9092
9093 if (Err)
9094 return Err;
9095
9096 // If we don't require privatization of device pointers, we emit the body
9097 // in between the runtime calls. This avoids duplicating the body code.
9098 InsertPointOrErrorTy AfterIP =
9099 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9100 if (!AfterIP)
9101 return AfterIP.takeError();
9102 restoreIPandDebugLoc(Builder, *AfterIP);
9103
9104 if (IfCond)
9105 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9106 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9107 }
9108 if (IfCond)
9109 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9110 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9111 }();
9112
9113 if (Err)
9114 return Err;
9115
9116 return Builder.saveIP();
9117}
9118
9121 bool IsGPUDistribute) {
9122 assert((IVSize == 32 || IVSize == 64) &&
9123 "IV size is not compatible with the omp runtime");
9124 RuntimeFunction Name;
9125 if (IsGPUDistribute)
9126 Name = IVSize == 32
9127 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9128 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9129 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9130 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9131 else
9132 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9133 : omp::OMPRTL___kmpc_for_static_init_4u)
9134 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9135 : omp::OMPRTL___kmpc_for_static_init_8u);
9136
9137 return getOrCreateRuntimeFunction(M, Name);
9138}
9139
9141 bool IVSigned) {
9142 assert((IVSize == 32 || IVSize == 64) &&
9143 "IV size is not compatible with the omp runtime");
9144 RuntimeFunction Name = IVSize == 32
9145 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9146 : omp::OMPRTL___kmpc_dispatch_init_4u)
9147 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9148 : omp::OMPRTL___kmpc_dispatch_init_8u);
9149
9150 return getOrCreateRuntimeFunction(M, Name);
9151}
9152
9154 bool IVSigned) {
9155 assert((IVSize == 32 || IVSize == 64) &&
9156 "IV size is not compatible with the omp runtime");
9157 RuntimeFunction Name = IVSize == 32
9158 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9159 : omp::OMPRTL___kmpc_dispatch_next_4u)
9160 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9161 : omp::OMPRTL___kmpc_dispatch_next_8u);
9162
9163 return getOrCreateRuntimeFunction(M, Name);
9164}
9165
9167 bool IVSigned) {
9168 assert((IVSize == 32 || IVSize == 64) &&
9169 "IV size is not compatible with the omp runtime");
9170 RuntimeFunction Name = IVSize == 32
9171 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9172 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9173 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9174 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9175
9176 return getOrCreateRuntimeFunction(M, Name);
9177}
9178
9180 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9181}
9182
9184 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9185 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9186
9187 DISubprogram *NewSP = Func->getSubprogram();
9188 if (!NewSP)
9189 return;
9190
9192
9193 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9194 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9195 // Only use cached variable if the arg number matches. This is important
9196 // so that DIVariable created for privatized variables are not discarded.
9197 if (NewVar && (arg == NewVar->getArg()))
9198 return NewVar;
9199
9201 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9202 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9203 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9204 return NewVar;
9205 };
9206
9207 auto UpdateDebugRecord = [&](auto *DR) {
9208 DILocalVariable *OldVar = DR->getVariable();
9209 unsigned ArgNo = 0;
9210 for (auto Loc : DR->location_ops()) {
9211 auto Iter = ValueReplacementMap.find(Loc);
9212 if (Iter != ValueReplacementMap.end()) {
9213 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9214 ArgNo = std::get<1>(Iter->second) + 1;
9215 }
9216 }
9217 if (ArgNo != 0)
9218 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9219 };
9220
9222 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9223 if (DVR->getNumVariableLocationOps() != 1u) {
9224 DVR->setKillLocation();
9225 return;
9226 }
9227 Value *Loc = DVR->getVariableLocationOp(0u);
9228 BasicBlock *CurBB = DVR->getParent();
9229 BasicBlock *RequiredBB = nullptr;
9230
9231 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9232 RequiredBB = LocInst->getParent();
9233 else if (isa<llvm::Argument>(Loc))
9234 RequiredBB = &DVR->getFunction()->getEntryBlock();
9235
9236 if (RequiredBB && RequiredBB != CurBB) {
9237 assert(!RequiredBB->empty());
9238 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9239 RequiredBB->back().getIterator());
9240 DVRsToDelete.push_back(DVR);
9241 }
9242 };
9243
9244 // The location and scope of variable intrinsics and records still point to
9245 // the parent function of the target region. Update them.
9246 for (Instruction &I : instructions(Func)) {
9248 "Unexpected debug intrinsic");
9249 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9250 UpdateDebugRecord(&DVR);
9251 MoveDebugRecordToCorrectBlock(&DVR);
9252 }
9253 }
9254 for (auto *DVR : DVRsToDelete)
9255 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9256 // An extra argument is passed to the device. Create the debug data for it.
9257 if (OMPBuilder.Config.isTargetDevice()) {
9258 DICompileUnit *CU = NewSP->getUnit();
9259 Module *M = Func->getParent();
9260 DIBuilder DB(*M, true, CU);
9261 DIType *VoidPtrTy =
9262 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9263 unsigned ArgNo = Func->arg_size();
9264 DILocalVariable *Var = DB.createParameterVariable(
9265 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9266 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9267 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9268 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9269 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9270 &(*Func->begin()));
9271 }
9272}
9273
9275 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9276 return cast<Operator>(V)->getOperand(0);
9277 return V;
9278}
9279
9281 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9283 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9286 DebugLoc OutlinedFnLoc) {
9287 SmallVector<Type *> ParameterTypes;
9288 if (OMPBuilder.Config.isTargetDevice()) {
9289 // All parameters to target devices are passed as pointers
9290 // or i64. This assumes 64-bit address spaces/pointers.
9291 for (auto &Arg : Inputs)
9292 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9293 ? Arg->getType()
9294 : Type::getInt64Ty(Builder.getContext()));
9295 } else {
9296 for (auto &Arg : Inputs)
9297 ParameterTypes.push_back(Arg->getType());
9298 }
9299
9300 // The implicit dyn_ptr argument is always the last parameter on both host
9301 // and device so the argument counts match without runtime manipulation.
9302 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9303 ParameterTypes.push_back(PtrTy);
9304
9305 auto BB = Builder.GetInsertBlock();
9306 auto M = BB->getModule();
9307 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9308 /*isVarArg*/ false);
9309 auto Func =
9310 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9311
9312 // Forward target-cpu and target-features function attributes from the
9313 // original function to the new outlined function.
9314 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9315
9316 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9317 if (TargetCpuAttr.isStringAttribute())
9318 Func->addFnAttr(TargetCpuAttr);
9319
9320 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9321 if (TargetFeaturesAttr.isStringAttribute())
9322 Func->addFnAttr(TargetFeaturesAttr);
9323
9324 if (OMPBuilder.Config.isTargetDevice()) {
9325 Value *ExecMode =
9326 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9327 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9328 }
9329
9330 // Save insert point.
9331 IRBuilder<>::InsertPointGuard IPG(Builder);
9332 // We will generate the entries in the outlined function but the debug
9333 // location is still pointing to the parent function, which is the wrong
9334 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9335 // position scoped to the subprogram that will be attached to the outlined
9336 // function, so it is what everything emitted below needs.
9337 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9338
9339 // Generate the region into the function.
9340 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9341 Builder.SetInsertPoint(EntryBB);
9342
9343 // Insert target init call in the device compilation pass. On the host
9344 // (e.g. a non-GPU offload target), there is no runtime init/deinit
9345 // sequence, but the runtime still needs a '<kernel>_kernel_environment'
9346 // global to know how the kernel was configured, so emit it directly.
9347 if (OMPBuilder.Config.isTargetDevice())
9348 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9349 else
9350 OMPBuilder.emitKernelEnvironment(Builder, DefaultAttrs);
9351
9352 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9353
9354 // As we embed the user code in the middle of our target region after we
9355 // generate entry code, we must move what allocas we can into the entry
9356 // block to avoid possible breaking optimisations for device
9357 if (OMPBuilder.Config.isTargetDevice())
9359
9360 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9361 BasicBlock *OutlinedBodyBB =
9362 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9364 CBFunc(Builder.saveIP(), OutlinedBodyBB->begin(), ExitBB);
9365 if (!AfterIP)
9366 return AfterIP.takeError();
9367 Builder.SetInsertPoint(ExitBB);
9368 // The body callback builds the body with its own IRBuilder and cannot reach
9369 // this one directly. But a body holding another OpenMP construct, a nested
9370 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9371 // this Builder pointing at the wrong debug location, or at none at all. The
9372 // epilogue below belongs to the target construct rather than to whatever the
9373 // body emitted last, so re-establish the location the prologue was emitted
9374 // with.
9375 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9376
9377 // Insert target deinit call in the device compilation pass.
9378 if (OMPBuilder.Config.isTargetDevice())
9379 OMPBuilder.createTargetDeinit(Builder);
9380
9381 // Insert return instruction.
9382 Builder.CreateRetVoid();
9383
9384 // New Alloca IP at entry point of created device function.
9385 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9386 auto AllocaIP = Builder.saveIP();
9387
9388 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9389
9390 // Do not include the artificial dyn_ptr argument.
9391 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9392
9394
9395 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9396 // Things like GEP's can come in the form of Constants. Constants and
9397 // ConstantExpr's do not have access to the knowledge of what they're
9398 // contained in, so we must dig a little to find an instruction so we
9399 // can tell if they're used inside of the function we're outlining. We
9400 // also replace the original constant expression with a new instruction
9401 // equivalent; an instruction as it allows easy modification in the
9402 // following loop, as we can now know the constant (instruction) is
9403 // owned by our target function and replaceUsesOfWith can now be invoked
9404 // on it (cannot do this with constants it seems). A brand new one also
9405 // allows us to be cautious as it is perhaps possible the old expression
9406 // was used inside of the function but exists and is used externally
9407 // (unlikely by the nature of a Constant, but still).
9408 // NOTE: We cannot remove dead constants that have been rewritten to
9409 // instructions at this stage, we run the risk of breaking later lowering
9410 // by doing so as we could still be in the process of lowering the module
9411 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9412 // constants we have created rewritten versions of.
9413 if (auto *Const = dyn_cast<Constant>(Input))
9414 convertUsersOfConstantsToInstructions(Const, Func, false);
9415
9416 // Collect users before iterating over them to avoid invalidating the
9417 // iteration in case a user uses Input more than once (e.g. a call
9418 // instruction).
9419 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9420 // Collect all the instructions
9422 if (auto *Instr = dyn_cast<Instruction>(User))
9423 if (Instr->getFunction() == Func)
9424 Instr->replaceUsesOfWith(Input, InputCopy);
9425 };
9426
9427 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9428
9429 // Rewrite uses of input valus to parameters.
9430 for (auto InArg : zip(Inputs, ArgRange)) {
9431 Value *Input = std::get<0>(InArg);
9432 Argument &Arg = std::get<1>(InArg);
9433 Value *InputCopy = nullptr;
9434
9435 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9436 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(), ExitBB->begin());
9437 if (!AfterIP)
9438 return AfterIP.takeError();
9439 Builder.restoreIP(*AfterIP);
9440 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9441
9442 // In certain cases a Global may be set up for replacement, however, this
9443 // Global may be used in multiple arguments to the kernel, just segmented
9444 // apart, for example, if we have a global array, that is sectioned into
9445 // multiple mappings (technically not legal in OpenMP, but there is a case
9446 // in Fortran for Common Blocks where this is neccesary), we will end up
9447 // with GEP's into this array inside the kernel, that refer to the Global
9448 // but are technically separate arguments to the kernel for all intents and
9449 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9450 // index, it will fold into an referal to the Global, if we then encounter
9451 // this folded GEP during replacement all of the references to the
9452 // Global in the kernel will be replaced with the argument we have generated
9453 // that corresponds to it, including any other GEP's that refer to the
9454 // Global that may be other arguments. This will invalidate all of the other
9455 // preceding mapped arguments that refer to the same global that may be
9456 // separate segments. To prevent this, we defer global processing until all
9457 // other processing has been performed.
9460 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9461 continue;
9462 }
9463
9465 continue;
9466
9467 ReplaceValue(Input, InputCopy, Func);
9468 }
9469
9470 // Replace all of our deferred Input values, currently just Globals.
9471 for (auto Deferred : DeferredReplacement)
9472 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9473
9474 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9475 ValueReplacementMap);
9476 return Func;
9477}
9478/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9479/// of pointers containing shared data between the parent task and the created
9480/// task.
9482 IRBuilderBase &Builder,
9483 Value *TaskWithPrivates,
9484 Type *TaskWithPrivatesTy) {
9485
9486 Type *TaskTy = OMPIRBuilder.Task;
9487 LLVMContext &Ctx = Builder.getContext();
9488 Value *TaskT =
9489 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9490 Value *Shareds = TaskT;
9491 // TaskWithPrivatesTy can be one of the following
9492 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9493 // %struct.privates }
9494 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9495 //
9496 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9497 // its first member has to be the task descriptor. TaskTy is the type of the
9498 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9499 // first member of TaskT, gives us the pointer to shared data.
9500 if (TaskWithPrivatesTy != TaskTy)
9501 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9502 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9503}
9504/// Create an entry point for a target task with the following.
9505/// It'll have the following signature
9506/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9507/// This function is called from emitTargetTask once the
9508/// code to launch the target kernel has been outlined already.
9509/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9510/// into the task structure so that the deferred target task can access this
9511/// data even after the stack frame of the generating task has been rolled
9512/// back. Offloading arrays contain base pointers, pointers, sizes etc
9513/// of the data that the target kernel will access. These in effect are the
9514/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9516 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9517 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9518 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9519
9520 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9521 // This is because PrivatesTy is the type of the structure in which
9522 // we pass the offloading arrays to the deferred target task.
9523 assert((!NumOffloadingArrays || PrivatesTy) &&
9524 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9525 "to privatize");
9526
9527 Module &M = OMPBuilder.M;
9528 // KernelLaunchFunction is the target launch function, i.e.
9529 // the function that sets up kernel arguments and calls
9530 // __tgt_target_kernel to launch the kernel on the device.
9531 //
9532 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9533
9534 // StaleCI is the CallInst which is the call to the outlined
9535 // target kernel launch function. If there are local live-in values
9536 // that the outlined function uses then these are aggregated into a structure
9537 // which is passed as the second argument. If there are no local live-in
9538 // values or if all values used by the outlined kernel are global variables,
9539 // then there's only one argument, the threadID. So, StaleCI can be
9540 //
9541 // %structArg = alloca { ptr, ptr }, align 8
9542 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9543 // store ptr %20, ptr %gep_, align 8
9544 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9545 // store ptr %21, ptr %gep_8, align 8
9546 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9547 //
9548 // OR
9549 //
9550 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9551 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9552
9553 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9554 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9555 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9556
9557 auto ProxyFnTy =
9558 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9559 /* isVarArg */ false);
9560 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9561 ".omp_target_task_proxy_func", M);
9562 Value *ThreadId = ProxyFn->getArg(0);
9563 Value *TaskWithPrivates = ProxyFn->getArg(1);
9564 ThreadId->setName("thread.id");
9565 TaskWithPrivates->setName("task");
9566
9567 bool HasShareds = SharedArgsOperandNo > 0;
9568 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9569 IRBuilder<>::InsertPointGuard IPG(Builder);
9570 BasicBlock *EntryBB =
9571 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9572 Builder.SetInsertPoint(EntryBB);
9573 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9574
9575 SmallVector<Value *> KernelLaunchArgs;
9576 KernelLaunchArgs.reserve(StaleCI->arg_size());
9577 KernelLaunchArgs.push_back(ThreadId);
9578
9579 if (HasOffloadingArrays) {
9580 assert(TaskTy != TaskWithPrivatesTy &&
9581 "If there are offloading arrays to pass to the target"
9582 "TaskTy cannot be the same as TaskWithPrivatesTy");
9583 (void)TaskTy;
9584 Value *Privates =
9585 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9586 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9587 KernelLaunchArgs.push_back(
9588 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9589 }
9590
9591 if (HasShareds) {
9592 auto *ArgStructAlloca =
9593 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9594 assert(ArgStructAlloca &&
9595 "Unable to find the alloca instruction corresponding to arguments "
9596 "for extracted function");
9597 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9598 std::optional<TypeSize> ArgAllocSize =
9599 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9600 assert(ArgStructType && ArgAllocSize &&
9601 "Unable to determine size of arguments for extracted function");
9602 uint64_t StructSize = ArgAllocSize->getFixedValue();
9603
9604 AllocaInst *NewArgStructAlloca =
9605 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9606
9607 Value *SharedsSize = Builder.getInt64(StructSize);
9608
9610 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9611
9612 Builder.CreateMemCpy(
9613 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9614 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9615 KernelLaunchArgs.push_back(NewArgStructAlloca);
9616 }
9617 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9618 Builder.CreateRetVoid();
9619 return ProxyFn;
9620}
9622
9623 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9624 return GEP->getSourceElementType();
9625 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9626 return Alloca->getAllocatedType();
9627
9628 llvm_unreachable("Unhandled Instruction type");
9629 return nullptr;
9630}
9631// This function returns a struct that has at most two members.
9632// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9633// descriptor. The second member, if needed, is a struct containing arrays
9634// that need to be passed to the offloaded target kernel. For example,
9635// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9636// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9637// respectively, then the types created by this function are
9638//
9639// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9640// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9641// %struct.privates }
9642// %struct.task_with_privates is returned by this function.
9643// If there aren't any offloading arrays to pass to the target kernel,
9644// %struct.kmp_task_ompbuilder_t is returned.
9645static StructType *
9647 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9648
9649 if (OffloadingArraysToPrivatize.empty())
9650 return OMPIRBuilder.Task;
9651
9652 SmallVector<Type *, 4> StructFieldTypes;
9653 for (Value *V : OffloadingArraysToPrivatize) {
9654 assert(V->getType()->isPointerTy() &&
9655 "Expected pointer to array to privatize. Got a non-pointer value "
9656 "instead");
9657 Type *ArrayTy = getOffloadingArrayType(V);
9658 assert(ArrayTy && "ArrayType cannot be nullptr");
9659 StructFieldTypes.push_back(ArrayTy);
9660 }
9661 StructType *PrivatesStructTy =
9662 StructType::create(StructFieldTypes, "struct.privates");
9663 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9664 "struct.task_with_privates");
9665}
9667 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9668 TargetRegionEntryInfo &EntryInfo,
9670 Function *&OutlinedFn, Constant *&OutlinedFnID,
9674 DebugLoc OutlinedFnLoc) {
9675
9676 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9677 [&](StringRef EntryFnName) {
9678 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9679 EntryFnName, Inputs, CBFunc,
9680 ArgAccessorFuncCB, OutlinedFnLoc);
9681 };
9682
9683 return OMPBuilder.emitTargetRegionFunction(
9684 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9685 OutlinedFnID);
9686}
9687
9689 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9691 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9692 bool HasNoWait) {
9693
9694 // The following explains the code-gen scenario for the `target` directive. A
9695 // similar scneario is followed for other device-related directives (e.g.
9696 // `target enter data`) but in similar fashion since we only need to emit task
9697 // that encapsulates the proper runtime call.
9698 //
9699 // When we arrive at this function, the target region itself has been
9700 // outlined into the function OutlinedFn.
9701 // So at ths point, for
9702 // --------------------------------------------------------------
9703 // void user_code_that_offloads(...) {
9704 // omp target depend(..) map(from:a) map(to:b) private(i)
9705 // do i = 1, 10
9706 // a(i) = b(i) + n
9707 // }
9708 //
9709 // --------------------------------------------------------------
9710 //
9711 // we have
9712 //
9713 // --------------------------------------------------------------
9714 //
9715 // void user_code_that_offloads(...) {
9716 // %.offload_baseptrs = alloca [2 x ptr], align 8
9717 // %.offload_ptrs = alloca [2 x ptr], align 8
9718 // %.offload_mappers = alloca [2 x ptr], align 8
9719 // ;; target region has been outlined and now we need to
9720 // ;; offload to it via a target task.
9721 // }
9722 // void outlined_device_function(ptr a, ptr b, ptr n) {
9723 // n = *n_ptr;
9724 // do i = 1, 10
9725 // a(i) = b(i) + n
9726 // }
9727 //
9728 // We have to now do the following
9729 // (i) Make an offloading call to outlined_device_function using the OpenMP
9730 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9731 // emitted by emitKernelLaunch
9732 // (ii) Create a task entry point function that calls kernel_launch_function
9733 // and is the entry point for the target task. See
9734 // '@.omp_target_task_proxy_func in the pseudocode below.
9735 // (iii) Create a task with the task entry point created in (ii)
9736 //
9737 // That is we create the following
9738 // struct task_with_privates {
9739 // struct kmp_task_ompbuilder_t task_struct;
9740 // struct privates {
9741 // [2 x ptr] ; baseptrs
9742 // [2 x ptr] ; ptrs
9743 // [2 x i64] ; sizes
9744 // }
9745 // }
9746 // void user_code_that_offloads(...) {
9747 // %.offload_baseptrs = alloca [2 x ptr], align 8
9748 // %.offload_ptrs = alloca [2 x ptr], align 8
9749 // %.offload_sizes = alloca [2 x i64], align 8
9750 //
9751 // %structArg = alloca { ptr, ptr, ptr }, align 8
9752 // %strucArg[0] = a
9753 // %strucArg[1] = b
9754 // %strucArg[2] = &n
9755 //
9756 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9757 // sizeof(kmp_task_ompbuilder_t),
9758 // sizeof(structArg),
9759 // @.omp_target_task_proxy_func,
9760 // ...)
9761 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9762 // sizeof(structArg))
9763 // memcpy(target_task_with_privates->privates->baseptrs,
9764 // offload_baseptrs, sizeof(offload_baseptrs)
9765 // memcpy(target_task_with_privates->privates->ptrs,
9766 // offload_ptrs, sizeof(offload_ptrs)
9767 // memcpy(target_task_with_privates->privates->sizes,
9768 // offload_sizes, sizeof(offload_sizes)
9769 // dependencies_array = ...
9770 // ;; if nowait not present
9771 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9772 // call @__kmpc_omp_task_begin_if0(...)
9773 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9774 // %target_task_with_privates)
9775 // call @__kmpc_omp_task_complete_if0(...)
9776 // }
9777 //
9778 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9779 // ptr %task) {
9780 // %structArg = alloca {ptr, ptr, ptr}
9781 // %task_ptr = getelementptr(%task, 0, 0)
9782 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9783 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9784 //
9785 // %offloading_arrays = getelementptr(%task, 0, 1)
9786 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9787 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9788 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9789 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9790 // %offload_sizes, %structArg)
9791 // }
9792 //
9793 // We need the proxy function because the signature of the task entry point
9794 // expected by kmpc_omp_task is always the same and will be different from
9795 // that of the kernel_launch function.
9796 //
9797 // kernel_launch_function is generated by emitKernelLaunch and has the
9798 // always_inline attribute. For this example, it'll look like so:
9799 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9800 // %offload_sizes, %structArg) alwaysinline {
9801 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9802 // ; load aggregated data from %structArg
9803 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9804 // ; offload_sizes
9805 // call i32 @__tgt_target_kernel(...,
9806 // outlined_device_function,
9807 // ptr %kernel_args)
9808 // }
9809 // void outlined_device_function(ptr a, ptr b, ptr n) {
9810 // n = *n_ptr;
9811 // do i = 1, 10
9812 // a(i) = b(i) + n
9813 // }
9814 //
9815 BasicBlock *TargetTaskBodyBB =
9816 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9817 BasicBlock *TargetTaskAllocaBB =
9818 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9819
9820 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB->begin());
9821 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB->begin());
9822
9823 auto OI = std::make_unique<OutlineInfo>();
9824 OI->EntryBB = TargetTaskAllocaBB;
9825 OI->OuterAllocBB = AllocaIP.getNodeParent();
9826
9827 // Add the thread ID argument.
9829 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9830 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9831
9832 // Generate the task body which will subsequently be outlined.
9833 Builder.restoreIP(TargetTaskBodyIP);
9834 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9835 return Err;
9836
9837 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9838 // it is given. These blocks are enumerated by
9839 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9840 // to be outside the region. In other words, OI.ExitBlock is expected to be
9841 // the start of the region after the outlining. We used to set OI.ExitBlock
9842 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9843 // except when the task body is a single basic block. In that case,
9844 // OI.ExitBlock is set to the single task body block and will get left out of
9845 // the outlining process. So, simply create a new empty block to which we
9846 // uncoditionally branch from where TaskBodyCB left off
9847 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9848 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9849 /*IsFinished=*/true);
9850
9851 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9852 bool NeedsTargetTask = HasNoWait && DeviceID;
9853 if (NeedsTargetTask) {
9854 for (auto *V :
9855 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9856 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9857 RTArgs.SizesArray}) {
9859 OffloadingArraysToPrivatize.push_back(V);
9860 OI->ExcludeArgsFromAggregate.push_back(V);
9861 }
9862 }
9863 }
9864 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9865 DeviceID, OffloadingArraysToPrivatize](
9866 Function &OutlinedFn) mutable {
9867 assert(OutlinedFn.hasOneUse() &&
9868 "there must be a single user for the outlined function");
9869
9870 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9871
9872 // The first argument of StaleCI is always the thread id.
9873 // The next few arguments are the pointers to offloading arrays
9874 // if any. (see OffloadingArraysToPrivatize)
9875 // Finally, all other local values that are live-in into the outlined region
9876 // end up in a structure whose pointer is passed as the last argument. This
9877 // piece of data is passed in the "shared" field of the task structure. So,
9878 // we know we have to pass shareds to the task if the number of arguments is
9879 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9880 // thread id. Further, for safety, we assert that the number of arguments of
9881 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9882 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9883 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9884 assert((!HasShareds ||
9885 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9886 "Wrong number of arguments for StaleCI when shareds are present");
9887 int SharedArgOperandNo =
9888 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9889
9890 StructType *TaskWithPrivatesTy =
9891 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9892 StructType *PrivatesTy = nullptr;
9893
9894 if (!OffloadingArraysToPrivatize.empty())
9895 PrivatesTy =
9896 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9897
9899 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9900 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9901
9902 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9903 << "\n");
9904
9905 Builder.SetInsertPoint(StaleCI);
9906
9907 // Gather the arguments for emitting the runtime call.
9908 uint32_t SrcLocStrSize;
9909 Constant *SrcLocStr =
9911 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9912
9913 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9914 //
9915 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9916 // the DeviceID to the deferred task and also since
9917 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9918 Function *TaskAllocFn =
9919 !NeedsTargetTask
9920 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9922 OMPRTL___kmpc_omp_target_task_alloc);
9923
9924 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9925 // call.
9926 Value *ThreadID = getOrCreateThreadID(Ident);
9927
9928 // Argument - `sizeof_kmp_task_t` (TaskSize)
9929 // Tasksize refers to the size in bytes of kmp_task_t data structure
9930 // plus any other data to be passed to the target task, if any, which
9931 // is packed into a struct. kmp_task_t and the struct so created are
9932 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9933 Value *TaskSize = Builder.getInt64(
9934 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9935
9936 // Argument - `sizeof_shareds` (SharedsSize)
9937 // SharedsSize refers to the shareds array size in the kmp_task_t data
9938 // structure.
9939 Value *SharedsSize = Builder.getInt64(0);
9940 if (HasShareds) {
9941 auto *ArgStructAlloca =
9942 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9943 assert(ArgStructAlloca &&
9944 "Unable to find the alloca instruction corresponding to arguments "
9945 "for extracted function");
9946 std::optional<TypeSize> ArgAllocSize =
9947 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9948 assert(ArgAllocSize &&
9949 "Unable to determine size of arguments for extracted function");
9950 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9951 }
9952
9953 // Argument - `flags`
9954 // Task is tied iff (Flags & 1) == 1.
9955 // Task is untied iff (Flags & 1) == 0.
9956 // Task is final iff (Flags & 2) == 2.
9957 // Task is not final iff (Flags & 2) == 0.
9958 // A target task is not final and is untied.
9959 Value *Flags = Builder.getInt32(0);
9960
9961 // Emit the @__kmpc_omp_task_alloc runtime call
9962 // The runtime call returns a pointer to an area where the task captured
9963 // variables must be copied before the task is run (TaskData)
9964 CallInst *TaskData = nullptr;
9965
9966 SmallVector<llvm::Value *> TaskAllocArgs = {
9967 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9968 /*flags=*/Flags,
9969 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9970 /*task_func=*/ProxyFn};
9971
9972 if (NeedsTargetTask) {
9973 assert(DeviceID && "Expected non-empty device ID.");
9974 TaskAllocArgs.push_back(DeviceID);
9975 }
9976
9977 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9978
9979 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9980 if (HasShareds) {
9981 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9983 *this, Builder, TaskData, TaskWithPrivatesTy);
9984 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9985 SharedsSize);
9986 }
9987 if (!OffloadingArraysToPrivatize.empty()) {
9988 Value *Privates =
9989 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9990 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9991 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9992 [[maybe_unused]] Type *ArrayType =
9993 getOffloadingArrayType(PtrToPrivatize);
9994 assert(ArrayType && "ArrayType cannot be nullptr");
9995
9996 Type *ElementType = PrivatesTy->getElementType(i);
9997 assert(ElementType == ArrayType &&
9998 "ElementType should match ArrayType");
9999 (void)ArrayType;
10000
10001 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
10002 Builder.CreateMemCpy(
10003 Dst, Alignment, PtrToPrivatize, Alignment,
10004 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
10005 }
10006 }
10007
10008 Value *DepArray = nullptr;
10009 Value *NumDeps = nullptr;
10010 if (Dependencies.DepArray) {
10011 DepArray = Dependencies.DepArray;
10012 NumDeps = Dependencies.NumDeps;
10013 } else if (!Dependencies.Deps.empty()) {
10014 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
10015 NumDeps = Builder.getInt32(Dependencies.Deps.size());
10016 }
10017
10018 // ---------------------------------------------------------------
10019 // V5.2 13.8 target construct
10020 // If the nowait clause is present, execution of the target task
10021 // may be deferred. If the nowait clause is not present, the target task is
10022 // an included task.
10023 // ---------------------------------------------------------------
10024 // The above means that the lack of a nowait on the target construct
10025 // translates to '#pragma omp task if(0)'
10026 if (!NeedsTargetTask) {
10027 if (DepArray) {
10028 Function *TaskWaitFn =
10029 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10031 TaskWaitFn,
10032 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10033 /*ndeps=*/NumDeps,
10034 /*dep_list=*/DepArray,
10035 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10036 /*noalias_dep_list=*/
10038 }
10039 // Included task.
10040 Function *TaskBeginFn =
10041 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10042 Function *TaskCompleteFn =
10043 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10044 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10045 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10046 CI->setDebugLoc(StaleCI->getDebugLoc());
10047 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10048 } else if (DepArray) {
10049 // HasNoWait - meaning the task may be deferred. Call
10050 // __kmpc_omp_task_with_deps if there are dependencies,
10051 // else call __kmpc_omp_task
10052 Function *TaskFn =
10053 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10055 TaskFn,
10056 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10057 ConstantInt::get(Builder.getInt32Ty(), 0),
10059 } else {
10060 // Emit the @__kmpc_omp_task runtime call to spawn the task
10061 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10062 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10063 }
10064
10065 Builder.ClearInsertionPoint();
10066 StaleCI->eraseFromParent();
10067 for (Instruction *I : llvm::reverse(ToBeDeleted))
10068 I->eraseFromParent();
10069 };
10070 addOutlineInfo(std::move(OI));
10071
10072 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10073 << *(Builder.GetInsertBlock()) << "\n");
10074 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10075 << *(Builder.GetInsertBlock()->getParent()->getParent())
10076 << "\n");
10077 return Builder.saveIP();
10078}
10079
10081 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10082 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10083 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10084 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10085 if (Error Err =
10086 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10087 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10088 return Err;
10089 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10090 return Error::success();
10091}
10092
10093static void emitTargetCall(
10094 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride,
10099 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10103 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10104 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10105 // Generate a function call to the host fallback implementation of the target
10106 // region. This is called by the host when no offload entry was generated for
10107 // the target region and when the offloading call fails at runtime.
10108 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10110 Builder.restoreIP(IP);
10111 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10112 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10113 FallbackArgs.push_back(
10114 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10115 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10116 return Builder.saveIP();
10117 };
10118
10119 bool HasDependencies = !Dependencies.empty();
10120 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10121
10123
10124 auto TaskBodyCB =
10125 [&](Value *DeviceID, Value *RTLoc,
10126 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10127 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10128 // produce any.
10130 // emitKernelLaunch makes the necessary runtime call to offload the
10131 // kernel. We then outline all that code into a separate function
10132 // ('kernel_launch_function' in the pseudo code above). This function is
10133 // then called by the target task proxy function (see
10134 // '@.omp_target_task_proxy_func' in the pseudo code above)
10135 // "@.omp_target_task_proxy_func' is generated by
10136 // emitTargetTaskProxyFunction.
10137 if (OutlinedFnID && DeviceID)
10138 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10139 EmitTargetCallFallbackCB, KArgs,
10140 DeviceID, RTLoc, TargetTaskAllocaIP);
10141
10142 // We only need to do the outlining if `DeviceID` is set to avoid calling
10143 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10144 // generating the `else` branch of an `if` clause.
10145 //
10146 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10147 // In this case, we execute the host implementation directly.
10148 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10149 }());
10150
10151 OMPBuilder.Builder.restoreIP(AfterIP);
10152 return Error::success();
10153 };
10154
10155 auto &&EmitTargetCallElse =
10156 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10158 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10159 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10160 // produce any.
10162 if (RequiresOuterTargetTask) {
10163 // Arguments that are intended to be directly forwarded to an
10164 // emitKernelLaunch call are pased as nullptr, since
10165 // OutlinedFnID=nullptr results in that call not being done.
10167 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10168 /*RTLoc=*/nullptr, AllocaIP,
10169 Dependencies, EmptyRTArgs, HasNoWait);
10170 }
10171 return EmitTargetCallFallbackCB(Builder.saveIP());
10172 }());
10173
10174 Builder.restoreIP(AfterIP);
10175 return Error::success();
10176 };
10177
10178 auto &&EmitTargetCallThen =
10179 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10181 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10182 Info.HasNoWait = HasNoWait;
10183 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10184
10186 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10187 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10188 /*IsNonContiguous=*/true,
10189 /*ForEndCall=*/false))
10190 return Err;
10191
10192 SmallVector<Value *, 3> NumTeamsC;
10193 for (auto [DefaultVal, RuntimeVal] :
10194 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10195 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10196 : Builder.getInt32(DefaultVal));
10197
10198 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10199 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10200 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10201 if (Clause)
10202 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10203 /*isSigned=*/false);
10204 return Clause;
10205 };
10206 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10207 if (Clause)
10208 Result =
10209 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10210 Result, Clause)
10211 : Clause;
10212 };
10213
10214 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10215 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10216 SmallVector<Value *, 3> NumThreadsC;
10217 Value *MaxThreadsClause =
10218 RuntimeAttrs.TeamsThreadLimit.size() == 1
10219 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10220 : nullptr;
10221
10222 for (auto [TeamsVal, TargetVal] : zip_equal(
10223 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10224 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10225 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10226
10227 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10228 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10229
10230 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10231 }
10232
10233 unsigned NumTargetItems = Info.NumberOfPtrs;
10234 Value *RTLoc = RTLocOverride;
10235 if (!RTLoc) {
10236 uint32_t SrcLocStrSize;
10237 Constant *SrcLocStr =
10238 OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10239 RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10240 llvm::omp::IdentFlag(0), 0);
10241 }
10242
10243 Value *TripCount = RuntimeAttrs.LoopTripCount
10244 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10245 Builder.getInt64Ty(),
10246 /*isSigned=*/false)
10247 : Builder.getInt64(0);
10248
10249 // Request zero groupprivate bytes by default.
10250 if (!DynCGroupMem)
10251 DynCGroupMem = Builder.getInt32(0);
10252
10254 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10255 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10256 DynCGroupMemFallback);
10257
10258 // Assume no error was returned because TaskBodyCB and
10259 // EmitTargetCallFallbackCB don't produce any.
10261 // The presence of certain clauses on the target directive require the
10262 // explicit generation of the target task.
10263 if (RequiresOuterTargetTask)
10264 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10265 RTLoc, AllocaIP, Dependencies,
10266 KArgs.RTArgs, Info.HasNoWait);
10267
10268 return OMPBuilder.emitKernelLaunch(
10269 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10270 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10271 }());
10272
10273 Builder.restoreIP(AfterIP);
10274 return Error::success();
10275 };
10276
10277 // If we don't have an ID for the target region, it means an offload entry
10278 // wasn't created. In this case we just run the host fallback directly and
10279 // ignore any potential 'if' clauses.
10280 if (!OutlinedFnID) {
10281 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10282 return;
10283 }
10284
10285 // If there's no 'if' clause, only generate the kernel launch code path.
10286 if (!IfCond) {
10287 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10288 return;
10289 }
10290
10291 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10292 EmitTargetCallElse, AllocaIP));
10293}
10294
10296 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10297 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10298 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10299 const TargetKernelDefaultAttrs &DefaultAttrs,
10300 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10301 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10304 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10305 bool HasNowait, Value *DynCGroupMem,
10306 OMPDynGroupprivateFallbackType DynCGroupMemFallback, DebugLoc OutlinedFnLoc,
10307 Value *RTLocOverride) {
10308
10309 if (!updateToLocation(Loc))
10310 return InsertPointTy();
10311
10312 Builder.restoreIP(CodeGenIP);
10313
10314 Function *OutlinedFn;
10315 Constant *OutlinedFnID = nullptr;
10316 // The target region is outlined into its own function. The LLVM IR for
10317 // the target region itself is generated using the callbacks CBFunc
10318 // and ArgAccessorFuncCB
10320 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10321 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10322 return Err;
10323
10324 // If we are not on the target device, then we need to generate code
10325 // to make a remote call (offload) to the previously outlined function
10326 // that represents the target region. Do that now.
10327 if (!Config.isTargetDevice())
10328 emitTargetCall(*this, Builder, RTLocOverride, AllocaIP, DeallocBlocks, Info,
10329 DefaultAttrs, RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID,
10330 Inputs, GenMapInfoCB, CustomMapperCB, Dependencies,
10331 HasNowait, DynCGroupMem, DynCGroupMemFallback);
10332 return Builder.saveIP();
10333}
10334
10335std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10336 StringRef FirstSeparator,
10337 StringRef Separator) {
10338 SmallString<128> Buffer;
10339 llvm::raw_svector_ostream OS(Buffer);
10340 StringRef Sep = FirstSeparator;
10341 for (StringRef Part : Parts) {
10342 OS << Sep << Part;
10343 Sep = Separator;
10344 }
10345 return OS.str().str();
10346}
10347
10348std::string
10350 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10351 Config.separator());
10352}
10353
10355 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10356 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10357 if (Elem.second) {
10358 assert(Elem.second->getValueType() == Ty &&
10359 "OMP internal variable has different type than requested");
10360 } else {
10361 // TODO: investigate the appropriate linkage type used for the global
10362 // variable for possibly changing that to internal or private, or maybe
10363 // create different versions of the function for different OMP internal
10364 // variables.
10365 const DataLayout &DL = M.getDataLayout();
10366 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10367 // default global AS is 1.
10368 // See double-target-call-with-declare-target.f90 and
10369 // declare-target-vars-in-target-region.f90 libomptarget
10370 // tests.
10371 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10372 : M.getTargetTriple().isAMDGPU()
10373 ? 0
10374 : DL.getDefaultGlobalsAddressSpace();
10375 auto Linkage = this->M.getTargetTriple().isWasm()
10378 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10379 Constant::getNullValue(Ty), Elem.first(),
10380 /*InsertBefore=*/nullptr,
10381 GlobalValue::NotThreadLocal, AddressSpaceVal);
10382 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10383 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10384 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10385 Elem.second = GV;
10386 }
10387
10388 return Elem.second;
10389}
10390
10391Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10392 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10393 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10394 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10395}
10396
10398 LLVMContext &Ctx = Builder.getContext();
10399 Value *Null =
10400 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10401 Value *SizeGep =
10402 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10403 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10404 return SizePtrToInt;
10405}
10406
10409 std::string VarName) {
10410 llvm::Constant *MaptypesArrayInit =
10411 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10412 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10413 M, MaptypesArrayInit->getType(),
10414 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10415 VarName);
10416 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10417 return MaptypesArrayGlobal;
10418}
10419
10421 InsertPointTy AllocaIP,
10422 unsigned NumOperands,
10423 struct MapperAllocas &MapperAllocas) {
10424 if (!updateToLocation(Loc))
10425 return;
10426
10427 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10428 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10429 Builder.restoreIP(AllocaIP);
10430 AllocaInst *ArgsBase = Builder.CreateAlloca(
10431 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10432 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10433 ".offload_ptrs");
10434 AllocaInst *ArgSizes = Builder.CreateAlloca(
10435 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10437 MapperAllocas.ArgsBase = ArgsBase;
10438 MapperAllocas.Args = Args;
10439 MapperAllocas.ArgSizes = ArgSizes;
10440}
10441
10443 Function *MapperFunc, Value *SrcLocInfo,
10444 Value *MaptypesArg, Value *MapnamesArg,
10446 int64_t DeviceID, unsigned NumOperands) {
10447 if (!updateToLocation(Loc))
10448 return;
10449
10450 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10451 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10452 Value *ArgsBaseGEP =
10453 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10454 {Builder.getInt32(0), Builder.getInt32(0)});
10455 Value *ArgsGEP =
10456 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10457 {Builder.getInt32(0), Builder.getInt32(0)});
10458 Value *ArgSizesGEP =
10459 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10460 {Builder.getInt32(0), Builder.getInt32(0)});
10461 Value *NullPtr =
10462 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10463 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10464 Builder.getInt32(NumOperands),
10465 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10466 MaptypesArg, MapnamesArg, NullPtr});
10467}
10468
10470 TargetDataRTArgs &RTArgs,
10471 TargetDataInfo &Info,
10472 bool ForEndCall) {
10473 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10474 "expected region end call to runtime only when end call is separate");
10475 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10476 auto VoidPtrTy = UnqualPtrTy;
10477 auto VoidPtrPtrTy = UnqualPtrTy;
10478 auto Int64Ty = Type::getInt64Ty(M.getContext());
10479 auto Int64PtrTy = UnqualPtrTy;
10480
10481 if (!Info.NumberOfPtrs) {
10482 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10483 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10484 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10485 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10486 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10487 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10488 return;
10489 }
10490
10491 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10492 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10493 Info.RTArgs.BasePointersArray,
10494 /*Idx0=*/0, /*Idx1=*/0);
10495 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10496 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10497 /*Idx0=*/0,
10498 /*Idx1=*/0);
10499 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10500 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10501 /*Idx0=*/0, /*Idx1=*/0);
10502 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10503 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10504 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10505 : Info.RTArgs.MapTypesArray,
10506 /*Idx0=*/0,
10507 /*Idx1=*/0);
10508
10509 // Only emit the mapper information arrays if debug information is
10510 // requested.
10511 if (!Info.EmitDebug)
10512 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10513 else
10514 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10515 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10516 /*Idx0=*/0,
10517 /*Idx1=*/0);
10518 // If there is no user-defined mapper, set the mapper array to nullptr to
10519 // avoid an unnecessary data privatization
10520 if (!Info.HasMapper)
10521 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10522 else
10523 RTArgs.MappersArray =
10524 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10525}
10526
10528 InsertPointTy CodeGenIP,
10529 MapInfosTy &CombinedInfo,
10530 TargetDataInfo &Info) {
10532 CombinedInfo.NonContigInfo;
10533
10534 // Build an array of struct descriptor_dim and then assign it to
10535 // offload_args.
10536 //
10537 // struct descriptor_dim {
10538 // uint64_t offset;
10539 // uint64_t count;
10540 // uint64_t stride
10541 // };
10542 Type *Int64Ty = Builder.getInt64Ty();
10544 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10545 "struct.descriptor_dim");
10546
10547 enum { OffsetFD = 0, CountFD, StrideFD };
10548 // We need two index variable here since the size of "Dims" is the same as
10549 // the size of Components, however, the size of offset, count, and stride is
10550 // equal to the size of base declaration that is non-contiguous.
10551 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10552 // Skip emitting ir if dimension size is 1 since it cannot be
10553 // non-contiguous.
10554 if (NonContigInfo.Dims[I] == 1)
10555 continue;
10556 Builder.restoreIP(AllocaIP);
10557 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10558 AllocaInst *DimsAddr =
10559 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10560 Builder.restoreIP(CodeGenIP);
10561 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10562 unsigned RevIdx = EE - II - 1;
10563 Value *DimsLVal = Builder.CreateInBoundsGEP(
10564 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10565 // Offset
10566 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10567 Builder.CreateAlignedStore(
10568 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10569 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10570 // Count
10571 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10572 Builder.CreateAlignedStore(
10573 NonContigInfo.Counts[L][RevIdx], CountLVal,
10574 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10575 // Stride
10576 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10577 Builder.CreateAlignedStore(
10578 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10579 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10580 }
10581 // args[I] = &dims
10582 Builder.restoreIP(CodeGenIP);
10583 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10584 DimsAddr, Builder.getPtrTy());
10585 Value *P = Builder.CreateConstInBoundsGEP2_32(
10586 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10587 Info.RTArgs.PointersArray, 0, I);
10588 Builder.CreateAlignedStore(
10589 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10590 ++L;
10591 }
10592}
10593
10594void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10595 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10596 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10597 BasicBlock *ExitBB, bool IsInit) {
10598 StringRef Prefix = IsInit ? ".init" : ".del";
10599
10600 // Evaluate if this is an array section.
10602 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10603 Value *IsArray =
10604 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10605 Value *DeleteBit = Builder.CreateAnd(
10606 MapType,
10607 Builder.getInt64(
10608 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10609 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10610 Value *DeleteCond;
10611 Value *Cond;
10612 if (IsInit) {
10613 // base != begin?
10614 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10615 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10616 DeleteCond = Builder.CreateIsNull(
10617 DeleteBit,
10618 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10619 } else {
10620 Cond = IsArray;
10621 DeleteCond = Builder.CreateIsNotNull(
10622 DeleteBit,
10623 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10624 }
10625 Cond = Builder.CreateAnd(Cond, DeleteCond);
10626 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10627
10628 emitBlock(BodyBB, MapperFn);
10629 // Get the array size by multiplying element size and element number (i.e., \p
10630 // Size).
10631 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10632 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10633 // memory allocation/deletion purpose only.
10634 Value *MapTypeArg = Builder.CreateAnd(
10635 MapType,
10636 Builder.getInt64(
10637 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10638 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10639 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10640 MapTypeArg = Builder.CreateOr(
10641 MapTypeArg,
10642 Builder.getInt64(
10643 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10644 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10645
10646 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10647 // data structure.
10648 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10649 ArraySize, MapTypeArg, MapName};
10651 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10652 OffloadingArgs);
10653}
10654
10657 llvm::Value *BeginArg)>
10658 GenMapInfoCB,
10659 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10660 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10661 SmallVector<Type *> Params;
10662 Params.emplace_back(Builder.getPtrTy());
10663 Params.emplace_back(Builder.getPtrTy());
10664 Params.emplace_back(Builder.getPtrTy());
10665 Params.emplace_back(Builder.getInt64Ty());
10666 Params.emplace_back(Builder.getInt64Ty());
10667 Params.emplace_back(Builder.getPtrTy());
10668
10669 auto *FnTy =
10670 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10671
10672 SmallString<64> TyStr;
10673 raw_svector_ostream Out(TyStr);
10674 Function *MapperFn =
10676 MapperFn->addFnAttr(Attribute::NoInline);
10677 MapperFn->addFnAttr(Attribute::NoUnwind);
10678 MapperFn->addParamAttr(0, Attribute::NoUndef);
10679 MapperFn->addParamAttr(1, Attribute::NoUndef);
10680 MapperFn->addParamAttr(2, Attribute::NoUndef);
10681 MapperFn->addParamAttr(3, Attribute::NoUndef);
10682 MapperFn->addParamAttr(4, Attribute::NoUndef);
10683 MapperFn->addParamAttr(5, Attribute::NoUndef);
10684
10685 // Start the mapper function code generation.
10686 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10688 Builder.SetInsertPoint(EntryBB);
10689 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10690
10691 Value *MapperHandle = MapperFn->getArg(0);
10692 Value *BaseIn = MapperFn->getArg(1);
10693 Value *BeginIn = MapperFn->getArg(2);
10694 Value *Size = MapperFn->getArg(3);
10695 Value *MapType = MapperFn->getArg(4);
10696 Value *MapName = MapperFn->getArg(5);
10697
10698 // Compute the starting and end addresses of array elements.
10699 // Prepare common arguments for array initiation and deletion.
10700 // Convert the size in bytes into the number of array elements.
10701 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10702 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10703 Value *PtrBegin = BeginIn;
10704 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10705
10706 // Emit array initiation if this is an array section and \p MapType indicates
10707 // that memory allocation is required.
10708 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10709 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10710 MapType, MapName, ElementSize, HeadBB,
10711 /*IsInit=*/true);
10712
10713 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10714
10715 // Emit the loop header block.
10716 emitBlock(HeadBB, MapperFn);
10717 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10718 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10719 // Evaluate whether the initial condition is satisfied.
10720 Value *IsEmpty =
10721 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10722 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10723
10724 // Emit the loop body block.
10725 emitBlock(BodyBB, MapperFn);
10726 BasicBlock *LastBB = BodyBB;
10727 PHINode *PtrPHI =
10728 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10729 PtrPHI->addIncoming(PtrBegin, HeadBB);
10730
10731 // Get map clause information. Fill up the arrays with all mapped variables.
10732 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10733 if (!Info)
10734 return Info.takeError();
10735
10736 // Call the runtime API __tgt_mapper_num_components to get the number of
10737 // pre-existing components.
10738 Value *OffloadingArgs[] = {MapperHandle};
10739 Value *PreviousSize = createRuntimeFunctionCall(
10740 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10741 OffloadingArgs);
10742 Value *ShiftedPreviousSize =
10743 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10744
10745 // Fill up the runtime mapper handle for all components.
10746 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10747 Value *CurBaseArg = Info->BasePointers[I];
10748 Value *CurBeginArg = Info->Pointers[I];
10749 Value *CurSizeArg = Info->Sizes[I];
10750 Value *CurNameArg = Info->Names.size()
10751 ? Info->Names[I]
10752 : Constant::getNullValue(Builder.getPtrTy());
10753
10754 Value *OriMapType = Builder.getInt64(
10755 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10756 Info->Types[I]));
10757 auto RawType =
10758 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10759 Info->Types[I]);
10760 constexpr uint64_t MemberOfMask =
10761 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10762 constexpr uint64_t AttachBit =
10763 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10764 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10765
10766 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10767 // current array element (N = __tgt_mapper_num_components() at loop body
10768 // start).
10769 //
10770 // Example 1:
10771 // struct S { int x; int *p; };
10772 //
10773 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10774 // use: S arr[2]; ... map(arr)
10775 // entries per element:
10776 //
10777 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10778 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10779 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10780 //
10781 // Example 2:
10782 // struct S1 { int x; int y; };
10783 // struct S2 { int z; S1 *s1p; };
10784 //
10785 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10786 // s2.s1p->y)
10787 // use: S2 arr[2]; ... map(arr)
10788 // entries per element:
10789 //
10790 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10791 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10792 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10793 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10794 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10795 //
10796 // x/y carry inner MEMBER_OF(2)
10797 // which is shifted by N to become MEMBER_OF(N+2).
10798 //
10799 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10800 // the combined ALLOC entry for the s1p->x..y block, and the individual
10801 // x/y entries that are MEMBER_OF that block, all describe storage
10802 // reached through the attach ptr arr[i].s1p.
10803 //
10804 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10805 // linking them to the parent struct:
10806 //
10807 // * (*) Entries with HasAttachPtr: they represent pointee data that
10808 // occupies a different storage block than the struct being mapped, so
10809 // they are not a member of it. They may still be MEMBER_OF an entry
10810 // within that pointee block, in which case those pre-existing bits are
10811 // shifted -- see (***).
10812 // * (**) ATTACH entries: they are not a member of anything — they just
10813 // link a ptr to its ptee.
10814 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10815 // its pre-shaped entries already carry their final MEMBER_OF bits.
10816 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10817 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10818 // it.
10819 //
10820 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10821 // s1p->x/y entries above), those bits are still shifted by N.
10822 Value *MemberMapType;
10823 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10824 Info->HasAttachPtr[I]) {
10825 if (RawType & MemberOfMask)
10826 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10827 else
10828 MemberMapType = OriMapType;
10829 } else {
10830 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10831 }
10832
10833 // Combine the map type inherited from user-defined mapper with that
10834 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10835 // bits of the \a MapType, which is the input argument of the mapper
10836 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10837 // bits of MemberMapType.
10838 // [OpenMP 5.0], 1.2.6. map-type decay.
10839 // | alloc | to | from | tofrom | release | delete
10840 // ----------------------------------------------------------
10841 // alloc | alloc | alloc | alloc | alloc | release | delete
10842 // to | alloc | to | alloc | to | release | delete
10843 // from | alloc | alloc | from | from | release | delete
10844 // tofrom | alloc | to | from | tofrom | release | delete
10845 Value *LeftToFrom = Builder.CreateAnd(
10846 MapType,
10847 Builder.getInt64(
10848 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10849 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10850 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10851 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10852 BasicBlock *AllocElseBB =
10853 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10854 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10855 BasicBlock *ToElseBB =
10856 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10857 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10858 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10859 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10860 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10861 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10862 emitBlock(AllocBB, MapperFn);
10863 Value *AllocMapType = Builder.CreateAnd(
10864 MemberMapType,
10865 Builder.getInt64(
10866 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10867 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10868 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10869 Builder.CreateBr(EndBB);
10870 emitBlock(AllocElseBB, MapperFn);
10871 Value *IsTo = Builder.CreateICmpEQ(
10872 LeftToFrom,
10873 Builder.getInt64(
10874 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10875 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10876 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10877 // In case of to, clear OMP_MAP_FROM.
10878 emitBlock(ToBB, MapperFn);
10879 Value *ToMapType = Builder.CreateAnd(
10880 MemberMapType,
10881 Builder.getInt64(
10882 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10883 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10884 Builder.CreateBr(EndBB);
10885 emitBlock(ToElseBB, MapperFn);
10886 Value *IsFrom = Builder.CreateICmpEQ(
10887 LeftToFrom,
10888 Builder.getInt64(
10889 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10890 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10891 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10892 // In case of from, clear OMP_MAP_TO.
10893 emitBlock(FromBB, MapperFn);
10894 Value *FromMapType = Builder.CreateAnd(
10895 MemberMapType,
10896 Builder.getInt64(
10897 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10898 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10899 // In case of tofrom, do nothing.
10900 emitBlock(EndBB, MapperFn);
10901 LastBB = EndBB;
10902 PHINode *CurMapType =
10903 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10904 CurMapType->addIncoming(AllocMapType, AllocBB);
10905 CurMapType->addIncoming(ToMapType, ToBB);
10906 CurMapType->addIncoming(FromMapType, FromBB);
10907 CurMapType->addIncoming(MemberMapType, ToElseBB);
10908
10909 // Propagate map-type-modifying bits from the outer map clause to each map
10910 // inserted by the mapper.
10911 //
10912 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10913 // list item from the map clause and to apply the clauses specified in the
10914 // declared mapper to the construct on which the map clause appears...
10915 // If any modifier with the map-type-modifying property appears in the map
10916 // clause then the effect is as if that modifier appears in each map clause
10917 // specified in the declared mapper.
10918 //
10919 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10920 //
10921 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10922 //
10923 // PRESENT is propagated only to entries that have an attach ptr
10924 // (HasAttachPtr): the pointee data, which occupies a different storage
10925 // block than the struct being mapped and so is not covered by the
10926 // present-check on the struct's own storage. A present modifier on the
10927 // outer clause must still require that pointee to be present on the device.
10928 //
10929 // This is gated on \p PropagatePresentToPointee (set by callers only for
10930 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10931 // applying to the pointee: the spec committee confirmed the divergence
10932 // between the present "motion" modifier (to/from) and the present map-type
10933 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10934 // so for 5.2 present is ignored for the pointee for both map and to/from.
10935 //
10936 // TODO: PRESENT should also be propagated to the struct's own members
10937 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10938 // member triggers the present-check. We cannot do that yet: while pointer
10939 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10940 // the whole struct (including the pointer's storage), so propagating
10941 // PRESENT to it would wrongly require the pointer's pointee to be present.
10942 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10943 // attach-style maps throughout.
10944 uint64_t ModifierBits =
10945 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10946 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10947 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10948 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10949 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10950 ModifierBits |=
10951 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10952 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10953 Value *ImportedModifierBits =
10954 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10955 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10956 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10957
10958 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10959 // reserved for the attach(always) map-type modifier, and other modifier
10960 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10961 Value *FinalMapType =
10962 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10963
10964 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10965 CurSizeArg, FinalMapType, CurNameArg};
10966
10967 auto ChildMapperFn = CustomMapperCB(I);
10968 if (!ChildMapperFn)
10969 return ChildMapperFn.takeError();
10970 if (*ChildMapperFn) {
10971 // Call the corresponding mapper function.
10972 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10973 ->setDoesNotThrow();
10974 } else {
10975 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10976 // data structure.
10978 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10979 OffloadingArgs);
10980 }
10981 }
10982
10983 // Update the pointer to point to the next element that needs to be mapped,
10984 // and check whether we have mapped all elements.
10985 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10986 "omp.arraymap.next");
10987 PtrPHI->addIncoming(PtrNext, LastBB);
10988 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10989 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10990 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10991
10992 emitBlock(ExitBB, MapperFn);
10993 // Emit array deletion if this is an array section and \p MapType indicates
10994 // that deletion is required.
10995 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10996 MapType, MapName, ElementSize, DoneBB,
10997 /*IsInit=*/false);
10998
10999 // Emit the function exit block.
11000 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
11001
11002 Builder.CreateRetVoid();
11003 return MapperFn;
11004}
11005
11007 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
11008 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
11009 bool IsNonContiguous,
11010 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
11011
11012 // Reset the array information.
11013 Info.clearArrayInfo();
11014 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
11015
11016 if (Info.NumberOfPtrs == 0)
11017 return Error::success();
11018
11019 Builder.restoreIP(AllocaIP);
11020 // Detect if we have any capture size requiring runtime evaluation of the
11021 // size so that a constant array could be eventually used.
11022 ArrayType *PointerArrayType =
11023 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
11024
11025 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
11026 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
11027
11028 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11029 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11030 AllocaInst *MappersArray = Builder.CreateAlloca(
11031 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11032 Info.RTArgs.MappersArray = MappersArray;
11033
11034 // If we don't have any VLA types or other types that require runtime
11035 // evaluation, we can use a constant array for the map sizes, otherwise we
11036 // need to fill up the arrays as we do for the pointers.
11037 Type *Int64Ty = Builder.getInt64Ty();
11038 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11039 ConstantInt::get(Int64Ty, 0));
11040 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11041 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11042 bool IsNonContigEntry =
11043 IsNonContiguous &&
11044 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11045 CombinedInfo.Types[I] &
11046 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11047 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11048 // descriptor_dim records), not the byte size.
11049 if (IsNonContigEntry) {
11050 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11051 "Index must be in-bounds for NON_CONTIG Dims array");
11052 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11053 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11054 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11055 continue;
11056 }
11057 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11058 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11059 ConstSizes[I] = CI;
11060 continue;
11061 }
11062 }
11063 RuntimeSizes.set(I);
11064 }
11065
11066 if (RuntimeSizes.all()) {
11067 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11068 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11069 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11070 restoreIPandDebugLoc(Builder, CodeGenIP);
11071 } else {
11072 auto *SizesArrayInit = ConstantArray::get(
11073 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11074 std::string Name = createPlatformSpecificName({"offload_sizes"});
11075 auto *SizesArrayGbl =
11076 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11077 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11078 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11079
11080 if (!RuntimeSizes.any()) {
11081 Info.RTArgs.SizesArray = SizesArrayGbl;
11082 } else {
11083 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11084 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11085 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11086 AllocaInst *Buffer = Builder.CreateAlloca(
11087 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11088 Buffer->setAlignment(OffloadSizeAlign);
11089 restoreIPandDebugLoc(Builder, CodeGenIP);
11090 Builder.CreateMemCpy(
11091 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11092 SizesArrayGbl, OffloadSizeAlign,
11093 Builder.getIntN(
11094 IndexSize,
11095 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11096
11097 Info.RTArgs.SizesArray = Buffer;
11098 }
11099 restoreIPandDebugLoc(Builder, CodeGenIP);
11100 }
11101
11102 // The map types are always constant so we don't need to generate code to
11103 // fill arrays. Instead, we create an array constant.
11105 for (auto mapFlag : CombinedInfo.Types)
11106 Mapping.push_back(
11107 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11108 mapFlag));
11109 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11110 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11111 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11112
11113 // The information types are only built if provided.
11114 if (!CombinedInfo.Names.empty()) {
11115 auto *MapNamesArrayGbl = createOffloadMapnames(
11116 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11117 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11118 Info.EmitDebug = true;
11119 } else {
11120 Info.RTArgs.MapNamesArray =
11122 Info.EmitDebug = false;
11123 }
11124
11125 // If there's a present map type modifier, it must not be applied to the end
11126 // of a region, so generate a separate map type array in that case.
11127 if (Info.separateBeginEndCalls()) {
11128 bool EndMapTypesDiffer = false;
11129 for (uint64_t &Type : Mapping) {
11130 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11131 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11132 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11133 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11134 EndMapTypesDiffer = true;
11135 }
11136 }
11137 if (EndMapTypesDiffer) {
11138 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11139 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11140 }
11141 }
11142
11143 PointerType *PtrTy = Builder.getPtrTy();
11144 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11145 Value *BPVal = CombinedInfo.BasePointers[I];
11146 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11147 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11148 0, I);
11149 Builder.CreateAlignedStore(BPVal, BP,
11150 M.getDataLayout().getPrefTypeAlign(PtrTy));
11151
11152 if (Info.requiresDevicePointerInfo()) {
11153 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11154 CodeGenIP = Builder.saveIP();
11155 Builder.restoreIP(AllocaIP);
11156 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11157 restoreIPandDebugLoc(Builder, CodeGenIP);
11158 if (DeviceAddrCB)
11159 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11160 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11161 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11162 if (DeviceAddrCB)
11163 DeviceAddrCB(I, BP);
11164 }
11165 }
11166
11167 Value *PVal = CombinedInfo.Pointers[I];
11168 Value *P = Builder.CreateConstInBoundsGEP2_32(
11169 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11170 I);
11171 // TODO: Check alignment correct.
11172 Builder.CreateAlignedStore(PVal, P,
11173 M.getDataLayout().getPrefTypeAlign(PtrTy));
11174
11175 if (RuntimeSizes.test(I)) {
11176 Value *S = Builder.CreateConstInBoundsGEP2_32(
11177 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11178 /*Idx0=*/0,
11179 /*Idx1=*/I);
11180 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11181 Int64Ty,
11182 /*isSigned=*/true),
11183 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11184 }
11185 // Fill up the mapper array.
11186 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11187 Value *MFunc = ConstantPointerNull::get(PtrTy);
11188
11189 auto CustomMFunc = CustomMapperCB(I);
11190 if (!CustomMFunc)
11191 return CustomMFunc.takeError();
11192 if (*CustomMFunc)
11193 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11194
11195 Value *MAddr = Builder.CreateInBoundsGEP(
11196 PointerArrayType, MappersArray,
11197 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11198 Builder.CreateAlignedStore(
11199 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11200 }
11201
11202 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11203 Info.NumberOfPtrs == 0)
11204 return Error::success();
11205 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11206 return Error::success();
11207}
11208
11210 BasicBlock *CurBB = Builder.GetInsertBlock();
11211
11212 if (!CurBB || CurBB->hasTerminator()) {
11213 // If there is no insert point or the previous block is already
11214 // terminated, don't touch it.
11215 } else {
11216 // Otherwise, create a fall-through branch.
11217 Builder.CreateBr(Target);
11218 }
11219
11220 Builder.ClearInsertionPoint();
11221}
11222
11224 bool IsFinished) {
11225 BasicBlock *CurBB = Builder.GetInsertBlock();
11226
11227 // Fall out of the current block (if necessary).
11228 emitBranch(BB);
11229
11230 if (IsFinished && BB->use_empty()) {
11231 BB->eraseFromParent();
11232 return;
11233 }
11234
11235 // Place the block after the current block, if possible, or else at
11236 // the end of the function.
11237 if (CurBB && CurBB->getParent())
11238 CurFn->insert(std::next(CurBB->getIterator()), BB);
11239 else
11240 CurFn->insert(CurFn->end(), BB);
11241 Builder.SetInsertPoint(BB);
11242}
11243
11245 BodyGenCallbackTy ElseGen,
11246 InsertPointTy AllocaIP,
11247 ArrayRef<BasicBlock *> DeallocBlocks) {
11248 // If the condition constant folds and can be elided, try to avoid emitting
11249 // the condition and the dead arm of the if/else.
11250 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11251 auto CondConstant = CI->getSExtValue();
11252 if (CondConstant)
11253 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11254
11255 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11256 }
11257
11258 Function *CurFn = Builder.GetInsertBlock()->getParent();
11259
11260 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11261 // emit the conditional branch.
11262 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11263 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11264 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11265 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11266 // Emit the 'then' code.
11267 emitBlock(ThenBlock, CurFn);
11268 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11269 return Err;
11270 emitBranch(ContBlock);
11271 // Emit the 'else' code if present.
11272 // There is no need to emit line number for unconditional branch.
11273 emitBlock(ElseBlock, CurFn);
11274 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11275 return Err;
11276 // There is no need to emit line number for unconditional branch.
11277 emitBranch(ContBlock);
11278 // Emit the continuation block for code after the if.
11279 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11280 return Error::success();
11281}
11282
11283bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11284 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11287 "Unexpected Atomic Ordering.");
11288
11289 bool Flush = false;
11291
11292 switch (AK) {
11293 case Read:
11296 FlushAO = AtomicOrdering::Acquire;
11297 Flush = true;
11298 }
11299 break;
11300 case Write:
11301 case Compare:
11302 case Update:
11305 FlushAO = AtomicOrdering::Release;
11306 Flush = true;
11307 }
11308 break;
11309 case Capture:
11310 switch (AO) {
11312 FlushAO = AtomicOrdering::Acquire;
11313 Flush = true;
11314 break;
11316 FlushAO = AtomicOrdering::Release;
11317 Flush = true;
11318 break;
11322 Flush = true;
11323 break;
11324 default:
11325 // do nothing - leave silently.
11326 break;
11327 }
11328 }
11329
11330 if (Flush) {
11331 // Currently Flush RT call still doesn't take memory_ordering, so for when
11332 // that happens, this tries to do the resolution of which atomic ordering
11333 // to use with but issue the flush call
11334 // TODO: pass `FlushAO` after memory ordering support is added
11335 (void)FlushAO;
11336 emitFlush(Loc);
11337 }
11338
11339 // for AO == AtomicOrdering::Monotonic and all other case combinations
11340 // do nothing
11341 return Flush;
11342}
11343
11347 AtomicOrdering AO, InsertPointTy AllocaIP) {
11348 if (!updateToLocation(Loc))
11349 return Loc.IP;
11350
11351 assert(X.Var->getType()->isPointerTy() &&
11352 "OMP Atomic expects a pointer to target memory");
11353 Type *XElemTy = X.ElemTy;
11354 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11355 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11356 "OMP atomic read expected a scalar type");
11357
11358 Value *XRead = nullptr;
11359
11360 if (XElemTy->isIntegerTy()) {
11361 LoadInst *XLD =
11362 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11363 XLD->setAtomic(AO);
11364 XRead = cast<Value>(XLD);
11365 } else if (XElemTy->isStructTy()) {
11366 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11367 // target does not support `atomicrmw` of the size of the struct
11368 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11369 OldVal->setAtomic(AO);
11370 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11371 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11372 OpenMPIRBuilder::AtomicInfo atomicInfo(
11373 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11374 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11375 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11376 XRead = AtomicLoadRes.first;
11377 OldVal->eraseFromParent();
11378 } else {
11379 // We need to perform atomic op as integer
11380 IntegerType *IntCastTy =
11381 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11382 LoadInst *XLoad =
11383 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11384 XLoad->setAtomic(AO);
11385 if (XElemTy->isFloatingPointTy()) {
11386 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11387 } else {
11388 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11389 }
11390 }
11391 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11392 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11393 return Builder.saveIP();
11394}
11395
11398 AtomicOpValue &X, Value *Expr,
11399 AtomicOrdering AO, InsertPointTy AllocaIP) {
11400 if (!updateToLocation(Loc))
11401 return Loc.IP;
11402
11403 assert(X.Var->getType()->isPointerTy() &&
11404 "OMP Atomic expects a pointer to target memory");
11405 Type *XElemTy = X.ElemTy;
11406 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11407 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11408 "OMP atomic write expected a scalar type");
11409
11410 if (XElemTy->isIntegerTy()) {
11411 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11412 XSt->setAtomic(AO);
11413 } else if (XElemTy->isStructTy()) {
11414 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11415 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11416 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11417 OpenMPIRBuilder::AtomicInfo atomicInfo(
11418 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11419 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11420 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11421 OldVal->eraseFromParent();
11422 } else {
11423 // We need to bitcast and perform atomic op as integers
11424 IntegerType *IntCastTy =
11425 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11426 Value *ExprCast =
11427 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11428 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11429 XSt->setAtomic(AO);
11430 }
11431
11432 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11433 return Builder.saveIP();
11434}
11435
11438 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11439 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11440 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11441 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11442 if (!updateToLocation(Loc))
11443 return Loc.IP;
11444
11445 LLVM_DEBUG({
11446 Type *XTy = X.Var->getType();
11447 assert(XTy->isPointerTy() &&
11448 "OMP Atomic expects a pointer to target memory");
11449 Type *XElemTy = X.ElemTy;
11450 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11451 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11452 "OMP atomic update expected a scalar or struct type");
11453 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11454 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11455 "OpenMP atomic does not support LT or GT operations");
11456 });
11457
11458 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11459 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11460 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11461 if (!AtomicResult)
11462 return AtomicResult.takeError();
11463 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11464 return Builder.saveIP();
11465}
11466
11467// FIXME: Duplicating AtomicExpand
11468Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11469 AtomicRMWInst::BinOp RMWOp) {
11470 switch (RMWOp) {
11471 case AtomicRMWInst::Add:
11472 return Builder.CreateAdd(Src1, Src2);
11473 case AtomicRMWInst::Sub:
11474 return Builder.CreateSub(Src1, Src2);
11475 case AtomicRMWInst::And:
11476 return Builder.CreateAnd(Src1, Src2);
11478 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11479 case AtomicRMWInst::Or:
11480 return Builder.CreateOr(Src1, Src2);
11481 case AtomicRMWInst::Xor:
11482 return Builder.CreateXor(Src1, Src2);
11487 case AtomicRMWInst::Max:
11488 case AtomicRMWInst::Min:
11501 llvm_unreachable("Unsupported atomic update operation");
11502 }
11503 llvm_unreachable("Unsupported atomic update operation");
11504}
11505
11507 // Loads cannot use Release or AcquireRelease ordering. This load is
11508 // just the initial value for the cmpxchg loop; the cmpxchg itself
11509 // retains the original ordering.
11510 AtomicOrdering LoadAO = AO;
11511
11512 if (AO == AtomicOrdering::Release) {
11514 } else if (AO == AtomicOrdering::AcquireRelease) {
11515 LoadAO = AtomicOrdering::Acquire;
11516 }
11517
11518 return LoadAO;
11519}
11520
11521Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11522 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11524 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11525 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11526 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11527 bool emitRMWOp = false;
11528 switch (RMWOp) {
11529 case AtomicRMWInst::Add:
11530 case AtomicRMWInst::And:
11532 case AtomicRMWInst::Or:
11533 case AtomicRMWInst::Xor:
11535 emitRMWOp = XElemTy;
11536 break;
11537 case AtomicRMWInst::Sub:
11538 emitRMWOp = (IsXBinopExpr && XElemTy);
11539 break;
11540 default:
11541 emitRMWOp = false;
11542 }
11543 emitRMWOp &= XElemTy->isIntegerTy();
11544
11545 std::pair<Value *, Value *> Res;
11546 if (emitRMWOp) {
11547 AtomicRMWInst *RMWInst =
11548 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11549 if (IsIgnoreDenormalMode)
11550 RMWInst->setMetadata(llvm::LLVMContext::MD_atomic_ignore_denormal_mode,
11551 llvm::MDNode::get(Builder.getContext(), {}));
11552 if (T.isAMDGPU()) {
11553 if (!IsFineGrainedMemory)
11554 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11555 llvm::MDNode::get(Builder.getContext(), {}));
11556 if (!IsRemoteMemory)
11557 RMWInst->setMetadata("amdgpu.no.remote.memory",
11558 llvm::MDNode::get(Builder.getContext(), {}));
11559 }
11560 Res.first = RMWInst;
11561 // not needed except in case of postfix captures. Generate anyway for
11562 // consistency with the else part. Will be removed with any DCE pass.
11563 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11564 if (RMWOp == AtomicRMWInst::Xchg)
11565 Res.second = Res.first;
11566 else
11567 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11568 } else if (XElemTy->isStructTy()) {
11569 LoadInst *OldVal =
11570 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11572 OldVal->setAtomic(LoadAO);
11573 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11574 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11575
11576 OpenMPIRBuilder::AtomicInfo atomicInfo(
11577 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11578 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11579 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11580 BasicBlock *CurBB = Builder.GetInsertBlock();
11581 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11582 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11583 BasicBlock *ExitBB =
11584 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11585 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11586 X->getName() + ".atomic.cont");
11587 ContBB->getTerminator()->eraseFromParent();
11588 Builder.restoreIP(AllocaIP);
11589 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11590 NewAtomicAddr->setName(X->getName() + "x.new.val");
11591 Builder.SetInsertPoint(ContBB);
11592 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11593 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11594 Value *OldExprVal = PHI;
11595 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11596 if (!CBResult)
11597 return CBResult.takeError();
11598 Value *Upd = *CBResult;
11599 Builder.CreateStore(Upd, NewAtomicAddr);
11602 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11603 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11604 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11605 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11606 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11607 OldVal->eraseFromParent();
11608 Res.first = OldExprVal;
11609 Res.second = Upd;
11610
11611 if (UnreachableInst *ExitTI =
11613 CurBBTI->eraseFromParent();
11614 Builder.SetInsertPoint(ExitBB);
11615 } else {
11616 Builder.SetInsertPoint(ExitTI);
11617 }
11618 } else {
11619 IntegerType *IntCastTy =
11620 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11621 LoadInst *OldVal =
11622 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11624 OldVal->setAtomic(LoadAO);
11625 // CurBB
11626 // | /---\
11627 // ContBB |
11628 // | \---/
11629 // ExitBB
11630 BasicBlock *CurBB = Builder.GetInsertBlock();
11631 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11632 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11633 BasicBlock *ExitBB =
11634 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11635 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11636 X->getName() + ".atomic.cont");
11637 ContBB->getTerminator()->eraseFromParent();
11638 Builder.restoreIP(AllocaIP);
11639 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11640 NewAtomicAddr->setName(X->getName() + "x.new.val");
11641 Builder.SetInsertPoint(ContBB);
11642 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11643 PHI->addIncoming(OldVal, CurBB);
11644 bool IsIntTy = XElemTy->isIntegerTy();
11645 Value *OldExprVal = PHI;
11646 if (!IsIntTy) {
11647 if (XElemTy->isFloatingPointTy()) {
11648 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11649 X->getName() + ".atomic.fltCast");
11650 } else {
11651 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11652 X->getName() + ".atomic.ptrCast");
11653 }
11654 }
11655
11656 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11657 if (!CBResult)
11658 return CBResult.takeError();
11659 Value *Upd = *CBResult;
11660 Builder.CreateStore(Upd, NewAtomicAddr);
11661 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11664 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11665 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11666 Result->setVolatile(VolatileX);
11667 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11668 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11669 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11670 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11671
11672 Res.first = OldExprVal;
11673 Res.second = Upd;
11674
11675 // set Insertion point in exit block
11676 if (UnreachableInst *ExitTI =
11678 CurBBTI->eraseFromParent();
11679 Builder.SetInsertPoint(ExitBB);
11680 } else {
11681 Builder.SetInsertPoint(ExitTI);
11682 }
11683 }
11684
11685 return Res;
11686}
11687
11690 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11691 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11692 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11693 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11694 if (!updateToLocation(Loc))
11695 return Loc.IP;
11696
11697 LLVM_DEBUG({
11698 Type *XTy = X.Var->getType();
11699 assert(XTy->isPointerTy() &&
11700 "OMP Atomic expects a pointer to target memory");
11701 Type *XElemTy = X.ElemTy;
11702 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11703 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11704 "OMP atomic capture expected a scalar or struct type");
11705 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11706 "OpenMP atomic does not support LT or GT operations");
11707 });
11708
11709 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11710 // 'x' is simply atomically rewritten with 'expr'.
11711 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11712 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11713 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11714 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11715 if (!AtomicResult)
11716 return AtomicResult.takeError();
11717 Value *CapturedVal =
11718 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11719 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11720
11721 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11722 return Builder.saveIP();
11723}
11724
11728 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11729 bool IsFailOnly, bool IsWeak) {
11730
11732 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11733 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11734}
11735
11739 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11740 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11741
11742 if (!updateToLocation(Loc))
11743 return Loc.IP;
11744
11745 assert(X.Var->getType()->isPointerTy() &&
11746 "OMP atomic expects a pointer to target memory");
11747 // compare capture
11748 if (V.Var) {
11749 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11750 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11751 }
11752
11753 bool IsInteger = E->getType()->isIntegerTy();
11754
11755 if (Op == OMPAtomicCompareOp::EQ) {
11756 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11757 // R.Var handling.
11758 Value *OldValue = nullptr;
11759 Value *SuccessOrFail = nullptr;
11760
11761 if (!IsInteger && HandleFPNegZero) {
11762 // IEEE 754 special cases for cmpxchg (which is bitwise):
11763 // 1. -0.0 == +0.0 but they have different bit patterns.
11764 // 2. NaN != NaN but identical NaN bit patterns would match.
11765 //
11766 // CurBB:
11767 // %e_int = bitcast E to intN
11768 // %d_int = bitcast D to intN
11769 // %x_curr = load atomic intN, X
11770 // %x_fp = bitcast %x_curr to FP
11771 // %e_is_nan = fcmp uno E, E
11772 // %x_is_nan = fcmp uno %x_fp, %x_fp
11773 // %either_nan = or %e_is_nan, %x_is_nan
11774 // br %either_nan, NaNBB, NotNaNBB
11775 // NaNBB: ; NaN == anything is always false
11776 // br ExitBB
11777 // NotNaNBB:
11778 // %x_is_zero = fcmp oeq %x_fp, 0.0
11779 // %e_is_zero = fcmp oeq E, 0.0
11780 // %both_zero = and %x_is_zero, %e_is_zero
11781 // br %both_zero, ZeroBB, NormalBB
11782 // ZeroBB: ; both ±0.0 → x = d
11783 // cmpxchg X, %x_curr, %d_int
11784 // br ExitBB
11785 // NormalBB: ; original path
11786 // cmpxchg X, %e_int, %d_int
11787 // br ExitBB
11788 // ExitBB:
11789 // phi merge
11790 IntegerType *IntCastTy =
11791 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11792 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11793 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11794
11795 // Load X atomically.
11796 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11797 X.Var->getName() + ".atomic.load");
11799 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11800
11801 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11802 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11803 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11804 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11805 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11806
11807 BasicBlock *CurBB = Builder.GetInsertBlock();
11808 Function *F = CurBB->getParent();
11809 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11810 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11811 BasicBlock *ExitBB =
11812 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11814 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11815 BasicBlock *NotNaNBB = BasicBlock::Create(
11816 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11818 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11819 BasicBlock *NormalBB = BasicBlock::Create(
11820 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11821
11822 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11823 CurBB->getTerminator()->eraseFromParent();
11824 Builder.SetInsertPoint(CurBB);
11825 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11826
11827 // NaNBB: NaN == anything is always false; skip cmpxchg.
11828 Builder.SetInsertPoint(NaNBB);
11829 Builder.CreateBr(ExitBB);
11830
11831 // NotNaNBB: check both X and E for ±0.0.
11832 Builder.SetInsertPoint(NotNaNBB);
11833 Value *XIsZero =
11834 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11835 X.Var->getName() + ".atomic.xiszero");
11836 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11837 "atomic.e.iszero");
11838 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11839 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11840
11841 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11842 Builder.SetInsertPoint(ZeroBB);
11843 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11844 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11845 ResZero->setWeak(IsWeak);
11846 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11847 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11848 Builder.CreateBr(ExitBB);
11849
11850 // NormalBB: original bitwise cmpxchg.
11851 Builder.SetInsertPoint(NormalBB);
11852 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11853 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11854 ResNormal->setWeak(IsWeak);
11855 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11856 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11857 Builder.CreateBr(ExitBB);
11858
11859 // ExitBB: merge results from NaN, Zero, and Normal paths.
11860 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11861 PHINode *OldIntPHI =
11862 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11863 OldIntPHI->addIncoming(XCurr, NaNBB);
11864 OldIntPHI->addIncoming(OldZero, ZeroBB);
11865 OldIntPHI->addIncoming(OldNormal, NormalBB);
11866 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11867 X.Var->getName() + ".atomic.ok");
11868 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11869 SuccessPHI->addIncoming(OkZero, ZeroBB);
11870 SuccessPHI->addIncoming(OkNormal, NormalBB);
11871
11872 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11873 CurBBTI->eraseFromParent();
11874 Builder.SetInsertPoint(ExitBB);
11875 } else {
11876 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11877 }
11878
11879 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11880 X.Var->getName() + ".atomic.old.fp");
11881 SuccessOrFail = SuccessPHI;
11882 } else {
11883 AtomicCmpXchgInst *Result = nullptr;
11884 if (!IsInteger) {
11885 IntegerType *IntCastTy =
11886 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11887 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11888 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11889 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11890 MaybeAlign(), AO, Failure);
11891 } else {
11892 Result =
11893 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11894 }
11895 Result->setWeak(IsWeak);
11896
11897 if (V.Var) {
11898 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11899 if (!IsInteger)
11900 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11901 assert(OldValue->getType() == V.ElemTy &&
11902 "OldValue and V must be of same type");
11903 if (IsPostfixUpdate) {
11904 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11905 } else {
11906 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11907 if (IsFailOnly) {
11908 BasicBlock *CurBB = Builder.GetInsertBlock();
11909 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11910 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11911 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11912 CurBBTI, X.Var->getName() + ".atomic.exit");
11913 BasicBlock *ContBB = CurBB->splitBasicBlock(
11914 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11915 ContBB->getTerminator()->eraseFromParent();
11916 CurBB->getTerminator()->eraseFromParent();
11917
11918 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11919
11920 Builder.SetInsertPoint(ContBB);
11921 Builder.CreateStore(OldValue, V.Var);
11922 Builder.CreateBr(ExitBB);
11923
11924 if (UnreachableInst *ExitTI =
11926 CurBBTI->eraseFromParent();
11927 Builder.SetInsertPoint(ExitBB);
11928 } else {
11929 Builder.SetInsertPoint(ExitTI);
11930 }
11931 } else {
11932 Value *CapturedValue =
11933 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11934 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11935 }
11936 }
11937 }
11938 // The comparison result has to be stored.
11939 if (R.Var) {
11940 assert(R.Var->getType()->isPointerTy() &&
11941 "r.var must be of pointer type");
11942 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11943
11944 Value *SuccessFailureVal =
11945 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11946 Value *ResultCast =
11947 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11948 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11949 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11950 }
11951 }
11952
11953 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11954 // pre-computed OldValue and SuccessOrFail.
11955 if (HandleFPNegZero && !IsInteger) {
11956 if (V.Var) {
11957 assert(OldValue->getType() == V.ElemTy &&
11958 "OldValue and V must be of same type");
11959 if (IsPostfixUpdate) {
11960 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11961 } else {
11962 if (IsFailOnly) {
11963 BasicBlock *CurBB = Builder.GetInsertBlock();
11964 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11965 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11966 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11967 CurBBTI, X.Var->getName() + ".atomic.exit");
11968 BasicBlock *ContBB = CurBB->splitBasicBlock(
11969 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11970 ContBB->getTerminator()->eraseFromParent();
11971 CurBB->getTerminator()->eraseFromParent();
11972
11973 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11974
11975 Builder.SetInsertPoint(ContBB);
11976 Builder.CreateStore(OldValue, V.Var);
11977 Builder.CreateBr(ExitBB);
11978
11979 if (UnreachableInst *ExitTI =
11981 CurBBTI->eraseFromParent();
11982 Builder.SetInsertPoint(ExitBB);
11983 } else {
11984 Builder.SetInsertPoint(ExitTI);
11985 }
11986 } else {
11987 Value *CapturedValue =
11988 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11989 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11990 }
11991 }
11992 }
11993 // The comparison result has to be stored.
11994 if (R.Var) {
11995 assert(R.Var->getType()->isPointerTy() &&
11996 "r.var must be of pointer type");
11997 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11998
11999 Value *ResultCast = R.IsSigned
12000 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
12001 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
12002 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12003 }
12004 }
12005 } else {
12006 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
12007 "Op should be either max or min at this point");
12008 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
12009
12010 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
12011 // Let's take max as example.
12012 // OpenMP form:
12013 // x = x > expr ? expr : x;
12014 // LLVM form:
12015 // *ptr = *ptr > val ? *ptr : val;
12016 // We need to transform to LLVM form.
12017 // x = x <= expr ? x : expr;
12019 if (IsXBinopExpr) {
12020 if (IsInteger) {
12021 if (X.IsSigned)
12022 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
12024 else
12025 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
12027 } else {
12028 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12030 }
12031 } else {
12032 if (IsInteger) {
12033 if (X.IsSigned)
12034 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12036 else
12037 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12039 } else {
12040 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12042 }
12043 }
12044
12045 AtomicRMWInst *OldValue =
12046 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12047 if (V.Var) {
12048 Value *CapturedValue = nullptr;
12049 if (IsPostfixUpdate) {
12050 CapturedValue = OldValue;
12051 } else {
12052 CmpInst::Predicate Pred;
12053 switch (NewOp) {
12054 case AtomicRMWInst::Max:
12055 Pred = CmpInst::ICMP_SGT;
12056 break;
12058 Pred = CmpInst::ICMP_UGT;
12059 break;
12061 Pred = CmpInst::FCMP_OGT;
12062 break;
12063 case AtomicRMWInst::Min:
12064 Pred = CmpInst::ICMP_SLT;
12065 break;
12067 Pred = CmpInst::ICMP_ULT;
12068 break;
12070 Pred = CmpInst::FCMP_OLT;
12071 break;
12072 default:
12073 llvm_unreachable("unexpected comparison op");
12074 }
12075 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12076 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12077 }
12078 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12079 }
12080 }
12081
12082 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12083
12084 return Builder.saveIP();
12085}
12086
12089 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12090 Value *NumTeamsUpper, Value *ThreadLimit,
12091 Value *IfExpr) {
12092 if (!updateToLocation(Loc))
12093 return InsertPointTy();
12094
12095 uint32_t SrcLocStrSize;
12096 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12097 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12098 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12099
12100 // Outer allocation basicblock is the entry block of the current function.
12101 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12102 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12103 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12104 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12105 }
12106
12107 // The current basic block is split into four basic blocks. After outlining,
12108 // they will be mapped as follows:
12109 // ```
12110 // def current_fn() {
12111 // current_basic_block:
12112 // br label %teams.exit
12113 // teams.exit:
12114 // ; instructions after teams
12115 // }
12116 //
12117 // def outlined_fn() {
12118 // teams.alloca:
12119 // br label %teams.body
12120 // teams.body:
12121 // ; instructions within teams body
12122 // }
12123 // ```
12124 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12125 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12126 BasicBlock *AllocaBB =
12127 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12128
12129 bool SubClausesPresent =
12130 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12131 // Push num_teams
12132 if (!Config.isTargetDevice() && SubClausesPresent) {
12133 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12134 "if lowerbound is non-null, then upperbound must also be non-null "
12135 "for bounds on num_teams");
12136
12137 if (NumTeamsUpper == nullptr)
12138 NumTeamsUpper = Builder.getInt32(0);
12139
12140 if (NumTeamsLower == nullptr)
12141 NumTeamsLower = NumTeamsUpper;
12142
12143 if (IfExpr) {
12144 assert(IfExpr->getType()->isIntegerTy() &&
12145 "argument to if clause must be an integer value");
12146
12147 // upper = ifexpr ? upper : 1
12148 if (IfExpr->getType() != Int1)
12149 IfExpr = Builder.CreateICmpNE(IfExpr,
12150 ConstantInt::get(IfExpr->getType(), 0));
12151 NumTeamsUpper = Builder.CreateSelect(
12152 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12153
12154 // lower = ifexpr ? lower : 1
12155 NumTeamsLower = Builder.CreateSelect(
12156 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12157 }
12158
12159 if (ThreadLimit == nullptr)
12160 ThreadLimit = Builder.getInt32(0);
12161
12162 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12163 // truncate or sign extend the passed values to match the int32 parameters.
12164 Value *NumTeamsLowerInt32 =
12165 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12166 Value *NumTeamsUpperInt32 =
12167 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12168 Value *ThreadLimitInt32 =
12169 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12170
12171 Value *ThreadNum = getOrCreateThreadID(Ident);
12172
12174 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12175 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12176 ThreadLimitInt32});
12177 }
12178 // Generate the body of teams.
12179 InsertPointTy AllocaIP(AllocaBB->begin());
12180 InsertPointTy CodeGenIP(BodyBB->begin());
12181 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12182 return Err;
12183
12184 auto OI = std::make_unique<OutlineInfo>();
12185 OI->EntryBB = AllocaBB;
12186 OI->ExitBB = ExitBB;
12187 OI->OuterAllocBB = &OuterAllocaBB;
12188
12189 // Insert fake values for global tid and bound tid.
12191 InsertPointTy OuterAllocaIP(OuterAllocaBB.begin());
12192 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12193 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12194 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12195 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12196
12197 auto HostPostOutlineCB = [this, Ident,
12198 ToBeDeleted](Function &OutlinedFn) mutable {
12199 // The stale call instruction will be replaced with a new call instruction
12200 // for runtime call with the outlined function.
12201
12202 assert(OutlinedFn.hasOneUse() &&
12203 "there must be a single user for the outlined function");
12204 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12205 ToBeDeleted.push_back(StaleCI);
12206
12207 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12208 "Outlined function must have two or three arguments only");
12209
12210 bool HasShared = OutlinedFn.arg_size() == 3;
12211
12212 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12213 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12214 if (HasShared)
12215 OutlinedFn.getArg(2)->setName("data");
12216
12217 // Call to the runtime function for teams in the current function.
12218 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12219 "outlined function.");
12220 Builder.SetInsertPoint(StaleCI);
12221 SmallVector<Value *> Args = {
12222 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12223 if (HasShared)
12224 Args.push_back(StaleCI->getArgOperand(2));
12227 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12228 Args);
12229
12230 Builder.ClearInsertionPoint();
12231 for (Instruction *I : llvm::reverse(ToBeDeleted))
12232 I->eraseFromParent();
12233 };
12234
12235 if (!Config.isTargetDevice())
12236 OI->PostOutlineCB = HostPostOutlineCB;
12237
12238 addOutlineInfo(std::move(OI));
12239
12240 Builder.SetInsertPoint(ExitBB);
12241
12242 return Builder.saveIP();
12243}
12244
12246 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12247 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12248 if (!updateToLocation(Loc))
12249 return InsertPointTy();
12250
12251 BasicBlock *OuterAllocaBB = OuterAllocIP.getNodeParent();
12252
12253 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12254 BasicBlock *BodyBB =
12255 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12256 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12257 }
12258 BasicBlock *ExitBB =
12259 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12260 BasicBlock *BodyBB =
12261 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12262 BasicBlock *AllocaBB =
12263 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12264
12265 // Generate the body of distribute clause
12266 InsertPointTy AllocaIP(AllocaBB->begin());
12267 InsertPointTy CodeGenIP(BodyBB->begin());
12268 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12269 return Err;
12270
12271 // When using target we use different runtime functions which require a
12272 // callback.
12273 if (Config.isTargetDevice()) {
12274 auto OI = std::make_unique<OutlineInfo>();
12275 OI->OuterAllocBB = OuterAllocIP.getNodeParent();
12276 OI->EntryBB = AllocaBB;
12277 OI->ExitBB = ExitBB;
12278 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12279 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12280
12281 addOutlineInfo(std::move(OI));
12282 }
12283 Builder.SetInsertPoint(ExitBB);
12284
12285 return Builder.saveIP();
12286}
12287
12290 std::string VarName) {
12291 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12293 Names.size()),
12294 Names);
12295 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12296 M, MapNamesArrayInit->getType(),
12297 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12298 VarName);
12299 return MapNamesArrayGlobal;
12300}
12301
12302// Create all simple and struct types exposed by the runtime and remember
12303// the llvm::PointerTypes of them for easy access later.
12304void OpenMPIRBuilder::initializeTypes(Module &M) {
12305 LLVMContext &Ctx = M.getContext();
12306 StructType *T;
12307 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12308 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12309#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12310#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12311 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12312 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12313#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12314 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12315 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12316#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12317 T = StructType::getTypeByName(Ctx, StructName); \
12318 if (!T) \
12319 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12320 VarName = T; \
12321 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12322#include "llvm/Frontend/OpenMP/OMPKinds.def"
12323}
12324
12327 SmallVectorImpl<BasicBlock *> &BlockVector) {
12329 BlockSet.insert(EntryBB);
12330 BlockSet.insert(ExitBB);
12331
12332 Worklist.push_back(EntryBB);
12333 while (!Worklist.empty()) {
12334 BasicBlock *BB = Worklist.pop_back_val();
12335 BlockVector.push_back(BB);
12336 for (BasicBlock *SuccBB : successors(BB))
12337 if (BlockSet.insert(SuccBB).second)
12338 Worklist.push_back(SuccBB);
12339 }
12340}
12341
12342std::unique_ptr<CodeExtractor>
12344 bool ArgsInZeroAddressSpace,
12345 Twine Suffix) {
12346 return std::make_unique<CodeExtractor>(
12347 Blocks, /* DominatorTree */ nullptr,
12348 /* AggregateArgs */ true,
12349 /* BlockFrequencyInfo */ nullptr,
12350 /* BranchProbabilityInfo */ nullptr,
12351 /* AssumptionCache */ nullptr,
12352 /* AllowVarArgs */ true,
12353 /* AllowAlloca */ true,
12354 /* AllocationBlock*/ OuterAllocBB,
12355 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12356 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12357}
12358
12359std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12360 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12361 return std::make_unique<DeviceSharedMemCodeExtractor>(
12362 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12363 /* AggregateArgs */ true,
12364 /* BlockFrequencyInfo */ nullptr,
12365 /* BranchProbabilityInfo */ nullptr,
12366 /* AssumptionCache */ nullptr,
12367 /* AllowVarArgs */ true,
12368 /* AllowAlloca */ true,
12369 /* AllocationBlock*/ OuterAllocBB,
12370 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12372 : OuterDeallocBBs,
12373 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12374}
12375
12377 uint64_t Size, int32_t Flags,
12379 StringRef Name) {
12380 if (!Config.isGPU()) {
12383 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12384 return;
12385 }
12386 // TODO: Add support for global variables on the device after declare target
12387 // support.
12388 Function *Fn = dyn_cast<Function>(Addr);
12389 if (!Fn)
12390 return;
12391
12392 // Add a function attribute for the kernel.
12393 Fn->addFnAttr("kernel");
12394 if (T.isAMDGCN())
12395 Fn->addFnAttr("uniform-work-group-size");
12396 Fn->addFnAttr(Attribute::MustProgress);
12397}
12398
12399// We only generate metadata for function that contain target regions.
12402
12403 // If there are no entries, we don't need to do anything.
12404 if (OffloadInfoManager.empty())
12405 return;
12406
12407 LLVMContext &C = M.getContext();
12410 16>
12411 OrderedEntries(OffloadInfoManager.size());
12412
12413 // Auxiliary methods to create metadata values and strings.
12414 auto &&GetMDInt = [this](unsigned V) {
12415 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12416 };
12417
12418 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12419
12420 // Create the offloading info metadata node.
12421 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12422 auto &&TargetRegionMetadataEmitter =
12423 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12424 const TargetRegionEntryInfo &EntryInfo,
12426 // Generate metadata for target regions. Each entry of this metadata
12427 // contains:
12428 // - Entry 0 -> Kind of this type of metadata (0).
12429 // - Entry 1 -> Device ID of the file where the entry was identified.
12430 // - Entry 2 -> File ID of the file where the entry was identified.
12431 // - Entry 3 -> Mangled name of the function where the entry was
12432 // identified.
12433 // - Entry 4 -> Line in the file where the entry was identified.
12434 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12435 // - Entry 6 -> Order the entry was created.
12436 // The first element of the metadata node is the kind.
12437 Metadata *Ops[] = {
12438 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12439 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12440 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12441 GetMDInt(E.getOrder())};
12442
12443 // Save this entry in the right position of the ordered entries array.
12444 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12445
12446 // Add metadata to the named metadata node.
12447 MD->addOperand(MDNode::get(C, Ops));
12448 };
12449
12450 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12451
12452 // Create function that emits metadata for each device global variable entry;
12453 auto &&DeviceGlobalVarMetadataEmitter =
12454 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12455 StringRef MangledName,
12457 // Generate metadata for global variables. Each entry of this metadata
12458 // contains:
12459 // - Entry 0 -> Kind of this type of metadata (1).
12460 // - Entry 1 -> Mangled name of the variable.
12461 // - Entry 2 -> Declare target kind.
12462 // - Entry 3 -> Order the entry was created.
12463 // The first element of the metadata node is the kind.
12464 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12465 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12466
12467 // Save this entry in the right position of the ordered entries array.
12468 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12469 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12470
12471 // Add metadata to the named metadata node.
12472 MD->addOperand(MDNode::get(C, Ops));
12473 };
12474
12475 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12476 DeviceGlobalVarMetadataEmitter);
12477
12478 for (const auto &E : OrderedEntries) {
12479 assert(E.first && "All ordered entries must exist!");
12480 if (const auto *CE =
12482 E.first)) {
12483 if (!CE->getID() || !CE->getAddress()) {
12484 // Do not blame the entry if the parent funtion is not emitted.
12485 TargetRegionEntryInfo EntryInfo = E.second;
12486 StringRef FnName = EntryInfo.ParentName;
12487 if (!M.getNamedValue(FnName))
12488 continue;
12489 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12490 continue;
12491 }
12492 createOffloadEntry(CE->getID(), CE->getAddress(),
12493 /*Size=*/0, CE->getFlags(),
12495 } else if (const auto *CE = dyn_cast<
12497 E.first)) {
12500 CE->getFlags());
12501 switch (Flags) {
12504 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12505 continue;
12506 if (!CE->getAddress()) {
12507 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12508 continue;
12509 }
12510 // The vaiable has no definition - no need to add the entry.
12511 if (CE->getVarSize() == 0)
12512 continue;
12513 break;
12515 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12516 (!Config.isTargetDevice() && CE->getAddress())) &&
12517 "Declaret target link address is set.");
12518 if (Config.isTargetDevice())
12519 continue;
12520 if (!CE->getAddress()) {
12522 continue;
12523 }
12524 break;
12527 if (!CE->getAddress()) {
12528 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12529 continue;
12530 }
12531 break;
12532 default:
12533 break;
12534 }
12535
12536 // Hidden or internal symbols on the device are not externally visible.
12537 // We should not attempt to register them by creating an offloading
12538 // entry. Indirect variables are handled separately on the device.
12539 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12540 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12541 (Flags !=
12543 Flags != OffloadEntriesInfoManager::
12544 OMPTargetGlobalVarEntryIndirectVTable))
12545 continue;
12546
12547 // Indirect globals need to use a special name that doesn't match the name
12548 // of the associated host global.
12550 Flags ==
12552 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12553 Flags, CE->getLinkage(), CE->getVarName());
12554 else
12555 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12556 Flags, CE->getLinkage());
12557
12558 } else {
12559 llvm_unreachable("Unsupported entry kind.");
12560 }
12561 }
12562
12563 // Emit requires directive globals to a special entry so the runtime can
12564 // register them when the device image is loaded.
12565 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12566 // entries should be redesigned to better suit this use-case.
12567 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12571 ".requires", /*Size=*/0,
12573 Config.getRequiresFlags());
12574}
12575
12578 unsigned FileID, unsigned Line, unsigned Count) {
12579 raw_svector_ostream OS(Name);
12580 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12581 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12582 if (Count)
12583 OS << "_" << Count;
12584}
12585
12587 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12588 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12590 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12591 EntryInfo.Line, NewCount);
12592}
12593
12596 vfs::FileSystem &VFS,
12597 StringRef ParentName) {
12598 sys::fs::UniqueID ID(0xdeadf17e, 0);
12599 auto FileIDInfo = CallBack();
12600 uint64_t FileID = 0;
12601 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12602 ID = Status->getUniqueID();
12603 FileID = Status->getUniqueID().getFile();
12604 } else {
12605 // If the inode ID could not be determined, create a hash value
12606 // the current file name and use that as an ID.
12607 FileID = hash_value(std::get<0>(FileIDInfo));
12608 }
12609
12610 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12611 std::get<1>(FileIDInfo));
12612}
12613
12615 unsigned Offset = 0;
12616 for (uint64_t Remain =
12617 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12619 !(Remain & 1); Remain = Remain >> 1)
12620 Offset++;
12621 return Offset;
12622}
12623
12626 // Rotate by getFlagMemberOffset() bits.
12627 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12628 << getFlagMemberOffset());
12629}
12630
12633 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12634 // If the entry is PTR_AND_OBJ but has not been marked with the special
12635 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12636 // marked as MEMBER_OF.
12637 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12639 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12642 return;
12643
12644 // Entries with ATTACH are not members-of anything. They are handled
12645 // separately by the runtime after other maps have been handled.
12646 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12648 return;
12649
12650 // Reset the placeholder value to prepare the flag for the assignment of the
12651 // proper MEMBER_OF value.
12652 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12653 Flags |= MemberOfFlag;
12654}
12655
12659 bool IsDeclaration, bool IsExternallyVisible,
12660 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12661 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12662 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12663 std::function<Constant *()> GlobalInitializer,
12664 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12665 // TODO: convert this to utilise the IRBuilder Config rather than
12666 // a passed down argument.
12667 if (OpenMPSIMD)
12668 return nullptr;
12669
12672 CaptureClause ==
12674 Config.hasRequiresUnifiedSharedMemory())) {
12675 SmallString<64> PtrName;
12676 {
12677 raw_svector_ostream OS(PtrName);
12678 OS << MangledName;
12679 if (!IsExternallyVisible)
12680 OS << format("_%x", EntryInfo.FileID);
12681 OS << "_decl_tgt_ref_ptr";
12682 }
12683
12684 Value *Ptr = M.getNamedValue(PtrName);
12685
12686 if (!Ptr) {
12687 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12688 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12689
12690 auto *GV = cast<GlobalVariable>(Ptr);
12691 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12692
12693 if (!Config.isTargetDevice()) {
12694 if (GlobalInitializer)
12695 GV->setInitializer(GlobalInitializer());
12696 else
12697 GV->setInitializer(GlobalValue);
12698 }
12699
12701 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12702 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12703 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12704 }
12705
12706 return cast<Constant>(Ptr);
12707 }
12708
12709 return nullptr;
12710}
12711
12715 bool IsDeclaration, bool IsExternallyVisible,
12716 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12717 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12718 std::vector<Triple> TargetTriple,
12719 std::function<Constant *()> GlobalInitializer,
12720 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12721 Constant *Addr) {
12723 (TargetTriple.empty() && !Config.isTargetDevice()))
12724 return;
12725
12727 StringRef VarName;
12728 int64_t VarSize;
12730
12732 CaptureClause ==
12734 !Config.hasRequiresUnifiedSharedMemory()) {
12736 VarName = MangledName;
12737 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12738
12739 if (!IsDeclaration)
12740 VarSize = divideCeil(
12741 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12742 else
12743 VarSize = 0;
12744 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12745
12746 // This is a workaround carried over from Clang which prevents undesired
12747 // optimisation of internal variables.
12748 if (Config.isTargetDevice() &&
12749 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12750 // Do not create a "ref-variable" if the original is not also available
12751 // on the host.
12752 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12753 return;
12754
12755 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12756
12757 if (!M.getNamedValue(RefName)) {
12758 Constant *AddrRef =
12759 getOrCreateInternalVariable(Addr->getType(), RefName);
12760 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12761 GvAddrRef->setConstant(true);
12762 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12763 GvAddrRef->setInitializer(Addr);
12764 GeneratedRefs.push_back(GvAddrRef);
12765 }
12766 }
12767 } else {
12770 else
12772
12773 if (Config.isTargetDevice()) {
12774 VarName = (Addr) ? Addr->getName() : "";
12775 Addr = nullptr;
12776 } else {
12778 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12779 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12780 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12781 VarName = (Addr) ? Addr->getName() : "";
12782 }
12783 VarSize = M.getDataLayout().getPointerSize();
12785 }
12786
12787 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12788 Flags, Linkage);
12789}
12790
12791/// Loads all the offload entries information from the host IR
12792/// metadata.
12794 // If we are in target mode, load the metadata from the host IR. This code has
12795 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12796
12797 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12798 if (!MD)
12799 return;
12800
12801 for (MDNode *MN : MD->operands()) {
12802 auto &&GetMDInt = [MN](unsigned Idx) {
12803 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12804 return cast<ConstantInt>(V->getValue())->getZExtValue();
12805 };
12806
12807 auto &&GetMDString = [MN](unsigned Idx) {
12808 auto *V = cast<MDString>(MN->getOperand(Idx));
12809 return V->getString();
12810 };
12811
12812 switch (GetMDInt(0)) {
12813 default:
12814 llvm_unreachable("Unexpected metadata!");
12815 break;
12816 case OffloadEntriesInfoManager::OffloadEntryInfo::
12817 OffloadingEntryInfoTargetRegion: {
12818 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12819 /*DeviceID=*/GetMDInt(1),
12820 /*FileID=*/GetMDInt(2),
12821 /*Line=*/GetMDInt(4),
12822 /*Count=*/GetMDInt(5));
12823 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12824 /*Order=*/GetMDInt(6));
12825 break;
12826 }
12827 case OffloadEntriesInfoManager::OffloadEntryInfo::
12828 OffloadingEntryInfoDeviceGlobalVar:
12829 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12830 /*MangledName=*/GetMDString(1),
12832 /*Flags=*/GetMDInt(2)),
12833 /*Order=*/GetMDInt(3));
12834 break;
12835 }
12836 }
12837}
12838
12840 StringRef HostFilePath) {
12841 if (HostFilePath.empty())
12842 return;
12843
12844 auto Buf = VFS.getBufferForFile(HostFilePath);
12845 if (std::error_code Err = Buf.getError()) {
12846 report_fatal_error(("error opening host file from host file path inside of "
12847 "OpenMPIRBuilder: " +
12848 Err.message())
12849 .c_str());
12850 }
12851
12852 LLVMContext Ctx;
12854 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12855 if (std::error_code Err = M.getError()) {
12857 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12858 .c_str());
12859 }
12860
12861 loadOffloadInfoMetadata(*M.get());
12862}
12863
12866 llvm::StringRef Name) {
12867 Builder.restoreIP(Loc.IP);
12868
12869 BasicBlock *CurBB = Builder.GetInsertBlock();
12870 assert(CurBB &&
12871 "expected a valid insertion block for creating an iterator loop");
12872 Function *F = CurBB->getParent();
12873
12874 InsertPointTy SplitIP = Builder.saveIP();
12875 if (SplitIP == CurBB->end())
12876 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12877 SplitIP = Terminator->getIterator();
12878
12879 BasicBlock *ContBB =
12880 splitBB(SplitIP, /*CreateBranch=*/false,
12881 Builder.getCurrentDebugLocation(), "omp.it.cont");
12882
12883 CanonicalLoopInfo *CLI =
12884 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12885 /*PreInsertBefore=*/ContBB,
12886 /*PostInsertBefore=*/ContBB, Name);
12887
12888 // Enter loop from original block.
12889 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12890
12891 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12892 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12893 T->eraseFromParent();
12894
12895 InsertPointTy BodyIP = CLI->getBodyIP();
12896 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12897 return Err;
12898
12899 // Body must either fallthrough to the latch or branch directly to it.
12900 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12901 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12902 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12904 "iterator bodygen must terminate the canonical body with an "
12905 "unconditional branch to the loop latch",
12907 }
12908 } else {
12909 // Ensure we end the loop body by jumping to the latch.
12910 Builder.SetInsertPoint(CLI->getBody());
12911 Builder.CreateBr(CLI->getLatch());
12912 }
12913
12914 // Link After -> ContBB
12915 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12916 if (!CLI->getAfter()->hasTerminator())
12917 Builder.CreateBr(ContBB);
12918
12919 return ContBB->begin();
12920}
12921
12922/// Mangle the parameter part of the vector function name according to
12923/// their OpenMP classification. The mangling function is defined in
12924/// section 4.5 of the AAVFABI(2021Q1).
12925static std::string mangleVectorParameters(
12927 SmallString<256> Buffer;
12928 llvm::raw_svector_ostream Out(Buffer);
12929 for (const auto &ParamAttr : ParamAttrs) {
12930 switch (ParamAttr.Kind) {
12932 Out << 'l';
12933 break;
12935 Out << 'R';
12936 break;
12938 Out << 'U';
12939 break;
12941 Out << 'L';
12942 break;
12944 Out << 'u';
12945 break;
12947 Out << 'v';
12948 break;
12949 }
12950 if (ParamAttr.HasVarStride)
12951 Out << "s" << ParamAttr.StrideOrArg;
12952 else if (ParamAttr.Kind ==
12954 ParamAttr.Kind ==
12956 ParamAttr.Kind ==
12958 ParamAttr.Kind ==
12960 // Don't print the step value if it is not present or if it is
12961 // equal to 1.
12962 if (ParamAttr.StrideOrArg < 0)
12963 Out << 'n' << -ParamAttr.StrideOrArg;
12964 else if (ParamAttr.StrideOrArg != 1)
12965 Out << ParamAttr.StrideOrArg;
12966 }
12967
12968 if (!!ParamAttr.Alignment)
12969 Out << 'a' << ParamAttr.Alignment;
12970 }
12971
12972 return std::string(Out.str());
12973}
12974
12976 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12978 struct ISADataTy {
12979 char ISA;
12980 unsigned VecRegSize;
12981 };
12982 ISADataTy ISAData[] = {
12983 {'b', 128}, // SSE
12984 {'c', 256}, // AVX
12985 {'d', 256}, // AVX2
12986 {'e', 512}, // AVX512
12987 };
12989 switch (Branch) {
12991 Masked.push_back('N');
12992 Masked.push_back('M');
12993 break;
12995 Masked.push_back('N');
12996 break;
12998 Masked.push_back('M');
12999 break;
13000 }
13001 for (char Mask : Masked) {
13002 for (const ISADataTy &Data : ISAData) {
13004 llvm::raw_svector_ostream Out(Buffer);
13005 Out << "_ZGV" << Data.ISA << Mask;
13006 if (!VLENVal) {
13007 assert(NumElts && "Non-zero simdlen/cdtsize expected");
13008 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
13009 } else {
13010 Out << VLENVal;
13011 }
13012 Out << mangleVectorParameters(ParamAttrs);
13013 Out << '_' << Fn->getName();
13014 Fn->addFnAttr(Out.str());
13015 }
13016 }
13017}
13018
13019// Function used to add the attribute. The parameter `VLEN` is templated to
13020// allow the use of `x` when targeting scalable functions for SVE.
13021template <typename T>
13022static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
13023 char ISA, StringRef ParSeq,
13024 StringRef MangledName, bool OutputBecomesInput,
13025 llvm::Function *Fn) {
13026 SmallString<256> Buffer;
13027 llvm::raw_svector_ostream Out(Buffer);
13028 Out << Prefix << ISA << LMask << VLEN;
13029 if (OutputBecomesInput)
13030 Out << 'v';
13031 Out << ParSeq << '_' << MangledName;
13032 Fn->addFnAttr(Out.str());
13033}
13034
13035// Helper function to generate the Advanced SIMD names depending on the value
13036// of the NDS when simdlen is not present.
13037static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13038 StringRef Prefix, char ISA,
13039 StringRef ParSeq, StringRef MangledName,
13040 bool OutputBecomesInput,
13041 llvm::Function *Fn) {
13042 switch (NDS) {
13043 case 8:
13044 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13045 OutputBecomesInput, Fn);
13046 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13047 OutputBecomesInput, Fn);
13048 break;
13049 case 16:
13050 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13051 OutputBecomesInput, Fn);
13052 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13053 OutputBecomesInput, Fn);
13054 break;
13055 case 32:
13056 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13057 OutputBecomesInput, Fn);
13058 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13059 OutputBecomesInput, Fn);
13060 break;
13061 case 64:
13062 case 128:
13063 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13064 OutputBecomesInput, Fn);
13065 break;
13066 default:
13067 llvm_unreachable("Scalar type is too wide.");
13068 }
13069}
13070
13071/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13073 llvm::Function *Fn, unsigned UserVLEN,
13075 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13076 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13077
13078 // Sort out parameter sequence.
13079 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13080 StringRef Prefix = "_ZGV";
13081 StringRef MangledName = Fn->getName();
13082
13083 // Generate simdlen from user input (if any).
13084 if (UserVLEN) {
13085 if (ISA == 's') {
13086 // SVE generates only a masked function.
13087 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13088 OutputBecomesInput, Fn);
13089 return;
13090 }
13091
13092 switch (Branch) {
13094 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13095 OutputBecomesInput, Fn);
13096 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13097 OutputBecomesInput, Fn);
13098 break;
13100 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13101 OutputBecomesInput, Fn);
13102 break;
13104 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13105 OutputBecomesInput, Fn);
13106 break;
13107 }
13108 return;
13109 }
13110
13111 if (ISA == 's') {
13112 // SVE, section 3.4.1, item 1.
13113 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13114 OutputBecomesInput, Fn);
13115 return;
13116 }
13117
13118 switch (Branch) {
13120 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13121 MangledName, OutputBecomesInput, Fn);
13122 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13123 MangledName, OutputBecomesInput, Fn);
13124 break;
13126 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13127 MangledName, OutputBecomesInput, Fn);
13128 break;
13130 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13131 MangledName, OutputBecomesInput, Fn);
13132 break;
13133 }
13134}
13135
13136//===----------------------------------------------------------------------===//
13137// OffloadEntriesInfoManager
13138//===----------------------------------------------------------------------===//
13139
13141 return OffloadEntriesTargetRegion.empty() &&
13142 OffloadEntriesDeviceGlobalVar.empty();
13143}
13144
13145unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13146 const TargetRegionEntryInfo &EntryInfo) const {
13147 auto It = OffloadEntriesTargetRegionCount.find(
13148 getTargetRegionEntryCountKey(EntryInfo));
13149 if (It == OffloadEntriesTargetRegionCount.end())
13150 return 0;
13151 return It->second;
13152}
13153
13154void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13155 const TargetRegionEntryInfo &EntryInfo) {
13156 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13157 EntryInfo.Count + 1;
13158}
13159
13160/// Initialize target region entry.
13162 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13163 OffloadEntriesTargetRegion[EntryInfo] =
13164 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13166 ++OffloadingEntriesNum;
13167}
13168
13170 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13172 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13173
13174 // Update the EntryInfo with the next available count for this location.
13175 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13176
13177 // If we are emitting code for a target, the entry is already initialized,
13178 // only has to be registered.
13179 if (OMPBuilder->Config.isTargetDevice()) {
13180 // This could happen if the device compilation is invoked standalone.
13181 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13182 return;
13183 }
13184 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13185 Entry.setAddress(Addr);
13186 Entry.setID(ID);
13187 Entry.setFlags(Flags);
13188 } else {
13190 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13191 return;
13192 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13193 "Target region entry already registered!");
13194 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13195 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13196 ++OffloadingEntriesNum;
13197 }
13198 incrementTargetRegionEntryInfoCount(EntryInfo);
13199}
13200
13202 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13203
13204 // Update the EntryInfo with the next available count for this location.
13205 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13206
13207 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13208 if (It == OffloadEntriesTargetRegion.end()) {
13209 return false;
13210 }
13211 // Fail if this entry is already registered.
13212 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13213 return false;
13214 return true;
13215}
13216
13218 const OffloadTargetRegionEntryInfoActTy &Action) {
13219 // Scan all target region entries and perform the provided action.
13220 for (const auto &It : OffloadEntriesTargetRegion) {
13221 Action(It.first, It.second);
13222 }
13223}
13224
13226 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13227 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13228 ++OffloadingEntriesNum;
13229}
13230
13232 StringRef VarName, Constant *Addr, int64_t VarSize,
13234 if (OMPBuilder->Config.isTargetDevice()) {
13235 // This could happen if the device compilation is invoked standalone.
13236 if (!hasDeviceGlobalVarEntryInfo(VarName))
13237 return;
13238 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13239 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13240 if (Entry.getVarSize() == 0) {
13241 Entry.setVarSize(VarSize);
13242 Entry.setLinkage(Linkage);
13243 }
13244 return;
13245 }
13246 Entry.setVarSize(VarSize);
13247 Entry.setLinkage(Linkage);
13248 Entry.setAddress(Addr);
13249 } else {
13250 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13251 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13252 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13253 "Entry not initialized!");
13254 if (Entry.getVarSize() == 0) {
13255 Entry.setVarSize(VarSize);
13256 Entry.setLinkage(Linkage);
13257 }
13258 return;
13259 }
13261 Flags ==
13263 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13264 Addr, VarSize, Flags, Linkage,
13265 VarName.str());
13266 else
13267 OffloadEntriesDeviceGlobalVar.try_emplace(
13268 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13269 ++OffloadingEntriesNum;
13270 }
13271}
13272
13275 // Scan all target region entries and perform the provided action.
13276 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13277 Action(E.getKey(), E.getValue());
13278}
13279
13280//===----------------------------------------------------------------------===//
13281// CanonicalLoopInfo
13282//===----------------------------------------------------------------------===//
13283
13284void CanonicalLoopInfo::collectControlBlocks(
13286 // We only count those BBs as control block for which we do not need to
13287 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13288 // flow. For consistency, this also means we do not add the Body block, which
13289 // is just the entry to the body code.
13290 BBs.reserve(BBs.size() + 6);
13291 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13292}
13293
13295 assert(isValid() && "Requires a valid canonical loop");
13296 for (BasicBlock *Pred : predecessors(Header)) {
13297 if (Pred != Latch)
13298 return Pred;
13299 }
13300 llvm_unreachable("Missing preheader");
13301}
13302
13303void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13304 assert(isValid() && "Requires a valid canonical loop");
13305
13306 Instruction *CmpI = &getCond()->front();
13307 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13308 CmpI->setOperand(1, TripCount);
13309
13310#ifndef NDEBUG
13311 assertOK();
13312#endif
13313}
13314
13315void CanonicalLoopInfo::mapIndVar(
13316 llvm::function_ref<Value *(Instruction *)> Updater) {
13317 assert(isValid() && "Requires a valid canonical loop");
13318
13319 Instruction *OldIV = getIndVar();
13320
13321 // Record all uses excluding those introduced by the updater. Uses by the
13322 // CanonicalLoopInfo itself to keep track of the number of iterations are
13323 // excluded.
13324 SmallVector<Use *> ReplacableUses;
13325 for (Use &U : OldIV->uses()) {
13326 auto *User = dyn_cast<Instruction>(U.getUser());
13327 if (!User)
13328 continue;
13329 if (User->getParent() == getCond())
13330 continue;
13331 if (User->getParent() == getLatch())
13332 continue;
13333 ReplacableUses.push_back(&U);
13334 }
13335
13336 // Run the updater that may introduce new uses
13337 Value *NewIV = Updater(OldIV);
13338
13339 // Replace the old uses with the value returned by the updater.
13340 for (Use *U : ReplacableUses)
13341 U->set(NewIV);
13342
13343#ifndef NDEBUG
13344 assertOK();
13345#endif
13346}
13347
13349#ifndef NDEBUG
13350 // No constraints if this object currently does not describe a loop.
13351 if (!isValid())
13352 return;
13353
13354 BasicBlock *Preheader = getPreheader();
13355 BasicBlock *Body = getBody();
13356 BasicBlock *After = getAfter();
13357
13358 // Verify standard control-flow we use for OpenMP loops.
13359 assert(Preheader);
13360 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13361 "Preheader must terminate with unconditional branch");
13362 assert(Preheader->getSingleSuccessor() == Header &&
13363 "Preheader must jump to header");
13364
13365 assert(Header);
13366 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13367 "Header must terminate with unconditional branch");
13368 assert(Header->getSingleSuccessor() == Cond &&
13369 "Header must jump to exiting block");
13370
13371 assert(Cond);
13372 assert(Cond->getSinglePredecessor() == Header &&
13373 "Exiting block only reachable from header");
13374
13375 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13376 "Exiting block must terminate with conditional branch");
13377 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13378 "Exiting block's first successor jump to the body");
13379 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13380 "Exiting block's second successor must exit the loop");
13381
13382 assert(Body);
13383 assert(Body->getSinglePredecessor() == Cond &&
13384 "Body only reachable from exiting block");
13385 assert(!isa<PHINode>(Body->front()));
13386
13387 assert(Latch);
13388 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13389 "Latch must terminate with unconditional branch");
13390 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13391 // TODO: To support simple redirecting of the end of the body code that has
13392 // multiple; introduce another auxiliary basic block like preheader and after.
13393 assert(Latch->getSinglePredecessor() != nullptr);
13394 assert(!isa<PHINode>(Latch->front()));
13395
13396 assert(Exit);
13397 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13398 "Exit block must terminate with unconditional branch");
13399 assert(Exit->getSingleSuccessor() == After &&
13400 "Exit block must jump to after block");
13401
13402 assert(After);
13403 assert(After->getSinglePredecessor() == Exit &&
13404 "After block only reachable from exit block");
13405 assert(After->empty() || !isa<PHINode>(After->front()));
13406
13407 Instruction *IndVar = getIndVar();
13408 assert(IndVar && "Canonical induction variable not found?");
13409 assert(isa<IntegerType>(IndVar->getType()) &&
13410 "Induction variable must be an integer");
13411 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13412 "Induction variable must be a PHI in the loop header");
13413 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13414 assert(
13415 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13416 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13417
13418 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13419 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13420 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13421 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13422 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13423 ->isOne());
13424
13425 Value *TripCount = getTripCount();
13426 assert(TripCount && "Loop trip count not found?");
13427 assert(IndVar->getType() == TripCount->getType() &&
13428 "Trip count and induction variable must have the same type");
13429
13430 auto *CmpI = cast<CmpInst>(&Cond->front());
13431 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13432 "Exit condition must be a signed less-than comparison");
13433 assert(CmpI->getOperand(0) == IndVar &&
13434 "Exit condition must compare the induction variable");
13435 assert(CmpI->getOperand(1) == TripCount &&
13436 "Exit condition must compare with the trip count");
13437#endif
13438}
13439
13441 Header = nullptr;
13442 Cond = nullptr;
13443 Latch = nullptr;
13444 Exit = nullptr;
13445}
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
static cl::opt< unsigned > TileSize("fuse-matrix-tile-size", cl::init(4), cl::Hidden, cl::desc("Tile size for matrix instruction fusion using square-shaped tiles."))
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file contains the declarations for metadata subclasses.
#define T
uint64_t IntrinsicInst * II
#define OMP_KERNEL_ARG_VERSION
Provides definitions for Target specific Grid Values.
static 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:640
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:169
const BasicBlock & getEntryBlock() const
Definition Function.h:794
Argument * arg_iterator
Definition Function.h:73
bool empty() const
Definition Function.h:844
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:212
void removeFromParent()
removeFromParent - This method unlinks 'this' from the containing module, but does not delete it.
Definition Function.cpp:447
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:765
DISubprogram * getSubprogram() const
Get the attached subprogram.
AttributeList getAttributes() const
Return the attribute list for this Function.
Definition Function.h:329
const Function & getFunction() const
Definition Function.h:167
iterator begin()
Definition Function.h:838
arg_iterator arg_begin()
Definition Function.h:853
void setAttributes(AttributeList Attrs)
Set the attribute list for this Function.
Definition Function.h:332
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:356
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
adds the attribute to the list of attributes for the given arg.
Definition Function.cpp:668
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition Function.h:740
size_t arg_size() const
Definition Function.h:886
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:217
iterator end()
Definition Function.h:840
void setCallingConv(CallingConv::ID CC)
Definition Function.h:277
Argument * getArg(unsigned i) const
Definition Function.h:871
bool hasMetadata() const
Return true if this GlobalObject has any metadata attached to it.
LLVM_ABI void addMetadata(unsigned KindID, MDNode &MD)
Add a metadata attachment.
LinkageTypes getLinkage() const
void setLinkage(LinkageTypes LT)
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
PointerType * getType() const
Global values are always pointers.
@ HiddenVisibility
The GV is hidden.
Definition GlobalValue.h:69
@ ProtectedVisibility
The GV is protected.
Definition GlobalValue.h:70
void setVisibility(VisibilityTypes V)
LinkageTypes
An enumeration for the kinds of linkage for global values.
Definition GlobalValue.h:52
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
@ CommonLinkage
Tentative definitions.
Definition GlobalValue.h:63
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ WeakODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:58
@ WeakAnyLinkage
Keep one copy of named function when linking (weak)
Definition GlobalValue.h:57
@ AppendingLinkage
Special purpose, only applies to global arrays.
Definition GlobalValue.h:59
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:56
Type * getValueType() const
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
Common base class shared among various IRBuilders.
Definition IRBuilder.h:114
InsertPoint saveIP() const
Returns the current insert point.
Definition IRBuilder.h:248
BasicBlock::iterator InsertPoint
InsertPoint - A saved insertion point.
Definition IRBuilder.h:245
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:258
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2901
LLVM_ABI const DebugLoc & getStableDebugLoc() const
Fetch the debug location for this node, unless this is a debug intrinsic, in which case fetch the deb...
LLVM_ABI void removeFromParent()
This method unlinks 'this' from the containing basic block, but does not delete it.
LLVM_ABI unsigned getNumSuccessors() const LLVM_READONLY
Return the number of successors that this instruction has.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
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.
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:68
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:594
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h: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 applyWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP, bool NeedsBarrier, llvm::omp::ScheduleKind SchedKind=llvm::omp::OMP_SCHEDULE_Default, Value *ChunkSize=nullptr, bool HasSimdModifier=false, bool HasMonotonicModifier=false, bool HasNonmonotonicModifier=false, bool HasOrderedClause=false, omp::WorksharingLoopType LoopType=omp::WorksharingLoopType::ForStaticLoop, bool NoLoop=false, bool HasDistSchedule=false, Value *DistScheduleChunkSize=nullptr)
Modifies the canonical loop to be a workshare loop.
LLVM_ABI InsertPointOrErrorTy createAtomicCapture(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, AtomicOpValue &V, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: — Only Scalar data types V = X; X = X BinOp Expr ,...
LLVM_ABI CanonicalLoopInfo * createLoopSkeleton(DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore, BasicBlock *PostInsertBefore, const Twine &Name={}, bool IsCollapsed=false)
Create the control flow structure of a canonical OpenMP loop.
LLVM_ABI void createOffloadEntriesAndInfoMetadata(EmitMetadataErrorReportFunctionTy &ErrorReportFunction)
LLVM_ABI void applySimd(CanonicalLoopInfo *Loop, MapVector< Value *, Value * > AlignedVars, Value *IfCond, omp::OrderKind Order, ConstantInt *Simdlen, ConstantInt *Safelen)
Add metadata to simd-ize a loop.
SmallVector< std::unique_ptr< OutlineInfo >, 16 > OutlineInfos
Collection of regions that need to be outlined during finalization.
LLVM_ABI InsertPointOrErrorTy createAtomicUpdate(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: X = X BinOp Expr ,or X = Expr BinOp X For complex Operations: X = ...
std::function< std::tuple< std::string, uint64_t >()> FileIdentifierInfoCallbackTy
bool isLastFinalizationInfoCancellable(omp::Directive DK)
Return true if the last entry in the finalization stack is of kind DK and cancellable.
LLVM_ABI InsertPointTy emitTargetKernel(const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return, Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads, Value *HostPtr, ArrayRef< Value * > KernelArgs)
Generate a target region entry call.
LLVM_ABI GlobalVariable * createOffloadMaptypes(SmallVectorImpl< uint64_t > &Mappings, std::string VarName)
Create the global variable holding the offload mappings information.
LLVM_ABI Expected< Function * > emitUserDefinedMapper(function_ref< MapInfosOrErrorTy(InsertPointTy CodeGenIP, llvm::Value *PtrPHI, llvm::Value *BeginArg)> PrivAndGenMapInfoCB, llvm::Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB, bool PreserveMemberOfFlags=false, bool PropagatePresentToPointee=false)
Emit the user-defined mapper function.
LLVM_ABI CallInst * createCachedThreadPrivate(const LocationDescription &Loc, llvm::Value *Pointer, llvm::ConstantInt *Size, const llvm::Twine &Name=Twine(""))
Create a runtime call for kmpc_threadprivate_cached.
IRBuilder Builder
The LLVM-IR Builder used to create IR.
LLVM_ABI GlobalValue * createGlobalFlag(unsigned Value, StringRef Name)
Create a hidden global flag Name in the module with initial value Value.
LLVM_ABI void emitOffloadingArraysArgument(IRBuilderBase &Builder, OpenMPIRBuilder::TargetDataRTArgs &RTArgs, OpenMPIRBuilder::TargetDataInfo &Info, bool ForEndCall=false)
Emit the arguments to be passed to the runtime library based on the arrays of base pointers,...
LLVM_ABI InsertPointOrErrorTy createMasked(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, Value *Filter)
Generator for 'omp masked'.
LLVM_ABI Expected< CanonicalLoopInfo * > createCanonicalLoop(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *TripCount, const Twine &Name="loop")
Generator for the control flow structure of an OpenMP canonical loop.
function_ref< Expected< InsertPointTy >( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value *DestPtr, Value *SrcPtr)> TaskDupCallbackTy
Callback type for task duplication function code generation.
LLVM_ABI Value * getSizeInBytes(Value *BasePtr)
Computes the size of type in bytes.
llvm::function_ref< llvm::Error( InsertPointTy BodyIP, llvm::Value *LinearIV)> IteratorBodyGenTy
LLVM_ABI FunctionCallee createDispatchDeinitFunction()
Returns __kmpc_dispatch_deinit runtime function.
LLVM_ABI void registerTargetGlobalVariable(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy, Constant *Addr)
Registers a target variable for device or host.
LLVM_ABI void createTargetDeinit(const LocationDescription &Loc, int32_t TeamsReductionDataSize=0)
Create a runtime call for kmpc_target_deinit.
BodyGenTy
Type of BodyGen to use for region codegen.
LLVM_ABI CanonicalLoopInfo * fuseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops)
Fuse a sequence of loops.
LLVM_ABI void emitX86DeclareSimdFunction(llvm::Function *Fn, unsigned NumElements, const llvm::APSInt &VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch)
Emit x86 vector-function ABI attributes for a declare simd function.
SmallVector< llvm::Function *, 16 > ConstantAllocaRaiseCandidates
A collection of candidate target functions that's constant allocas will attempt to be raised on a cal...
OffloadEntriesInfoManager OffloadInfoManager
Info manager to keep track of target regions.
static LLVM_ABI std::pair< int32_t, int32_t > readTeamBoundsForKernel(const Triple &T, Function &Kernel)
Read/write a bounds on teams for Kernel.
const std::string ompOffloadInfoName
OMP Offload Info Metadata name string.
Expected< InsertPointTy > InsertPointOrErrorTy
Type used to represent an insertion point or an error value.
LLVM_ABI InsertPointTy createCopyPrivate(const LocationDescription &Loc, llvm::Value *BufSize, llvm::Value *CpyBuf, llvm::Value *CpyFn, llvm::Value *DidIt)
Generator for __kmpc_copyprivate.
LLVM_ABI InsertPointOrErrorTy createSections(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< StorableBodyGenCallbackTy > SectionCBs, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait)
Generator for 'omp sections'.
std::function< void(EmitMetadataErrorKind, TargetRegionEntryInfo)> EmitMetadataErrorReportFunctionTy
Callback function type.
function_ref< InsertPointOrErrorTy( Argument &Arg, Value *Input, Value *&RetVal, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< InsertPointTy > DeallocIPs)> TargetGenArgAccessorsCallbackTy
LLVM_ABI Expected< ScanInfo * > scanInfoInitialize()
Creates a ScanInfo object, allocates and returns the pointer.
LLVM_ABI InsertPointOrErrorTy emitTargetTask(TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc, OpenMPIRBuilder::InsertPointTy AllocaIP, const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs, bool HasNoWait)
Generate a target-task for the target construct.
LLVM_ABI InsertPointTy createAtomicRead(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic Read for : V = X — Only Scalar data types.
function_ref< Error(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> BodyGenCallbackTy
Callback type for body (=inner region) code generation.
bool updateToLocation(const LocationDescription &Loc)
Update the internal location to Loc.
LLVM_ABI void createFlush(const LocationDescription &Loc)
Generator for 'omp flush'.
LLVM_ABI 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:1140
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1200
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1214
bool isSystemZ() const
Tests whether the target is SystemZ.
Definition Triple.h:1197
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:395
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto successors(const MachineBasicBlock *BB)
@ Load
The value being inserted comes from a load (InsertElement only).
@ Store
The extracted value is stored (ExtractElement only).
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
constexpr from_range_t from_range
auto dyn_cast_if_present(const Y &Val)
dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a null (or none in the case ...
Definition Casting.h:732
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
LLVM_ABI BasicBlock * splitBB(IRBuilderBase::InsertPoint IP, bool CreateBranch, DebugLoc DL, llvm::Twine Name={})
Split a BasicBlock at an InsertPoint, even if the block is degenerate (missing the terminator).
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h: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,...
static auto filterDbgVars(iterator_range< simple_ilist< DbgRecord >::iterator > R)
Filter the DbgRecord range to DbgVariableRecord types only and downcast.
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
static LLVM_ABI void collectEphemeralValues(const Loop *L, AssumptionCache *AC, SmallPtrSetImpl< const Value * > &EphValues)
Collect a loop's ephemeral values (those used only by an assume or similar intrinsics in the loop).
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.
Definition Alignment.h:106
A struct to pack the relevant information for an OpenMP affinity clause.
a struct to pack relevant information while generating atomic Ops
A struct to pack the relevant information for an OpenMP depend clause.
omp::RTLDependenceKindTy DepKind
A struct to pack static and dynamic dependency information for a task.
LLVM_ABI Error mergeFiniBB(IRBuilderBase &Builder, BasicBlock *ExistingFiniBB)
For cases where there is an unavoidable existing finalization block (e.g.
LLVM_ABI Expected< BasicBlock * > getFiniBB(IRBuilderBase &Builder)
The basic block to which control should be transferred to implement the FiniCB.
Description of a LLVM-IR insertion point (IP) and a debug/source location (filename,...
This structure contains combined information generated for mappable clauses, including base pointers,...
MapDeviceInfoArrayTy DevicePointers
StructNonContiguousInfo NonContigInfo
Helper that contains information about regions we need to outline during finalization.
void collectBlocks(SmallPtrSetImpl< BasicBlock * > &BlockSet, SmallVectorImpl< BasicBlock * > &BlockVector)
Collect all blocks in between EntryBB and ExitBB in both the given vector and set.
virtual std::unique_ptr< CodeExtractor > createCodeExtractor(ArrayRef< BasicBlock * > Blocks, bool ArgsInZeroAddressSpace, Twine Suffix=Twine(""))
Create a CodeExtractor instance based on the information stored in this structure,...
Information about an OpenMP reduction.
EvalKind EvaluationKind
Reduction evaluation kind - scalar, complex or aggregate.
ReductionGenAtomicCBTy AtomicReductionGen
Callback for generating the atomic reduction body, may be null.
ReductionGenCBTy ReductionGen
Callback for generating the reduction body.
Value * Variable
Reduction variable of pointer type.
Value * PrivateVariable
Thread-private partial reduction variable.
ReductionGenClangCBTy ReductionGenClang
Clang callback for generating the reduction body.
Type * ElementType
Reduction element type, must match pointee type of variable.
ReductionGenDataPtrPtrCBTy DataPtrPtrGen
Container for the arguments used to pass data to the runtime library.
Value * SizesArray
The array of sizes passed to the runtime library.
Value * PointersArray
The array of section pointers passed to the runtime library.
Value * MappersArray
The array of user-defined mappers passed to the runtime library.
Value * MapTypesArrayEnd
The array of map types passed to the runtime library for the end of the region, or nullptr if there a...
Value * BasePointersArray
The array of base pointer passed to the runtime library.
Value * MapTypesArray
The array of map types passed to the runtime library for the beginning of the region or for the entir...
Value * MapNamesArray
The array of original declaration names of mapped pointers sent to the runtime library for debugging.
Data structure that contains the needed information to construct the kernel args vector.
bool StrictBlocks
True if the kernel strictly requires the number of blocks and threads above to run.
ArrayRef< Value * > NumThreads
The number of threads.
TargetDataRTArgs RTArgs
Arguments passed to the runtime library.
Value * NumIterations
The number of iterations.
Value * DynCGroupMem
The size of the dynamic shared memory.
unsigned NumTargetItems
Number of arguments passed to the runtime library.
bool HasNoWait
True if the kernel has 'no wait' clause.
ArrayRef< Value * > NumTeams
The number of teams.
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback
The fallback mechanism for the shared memory.
Container to pass the default attributes with which a kernel must be launched, used to set kernel att...
Container to pass LLVM IR runtime values or constants related to the number of teams and threads with...
Value * DeviceID
Device ID value used in the kernel launch.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic kernel.
SmallVector< Value * > MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Data structure to contain the information needed to uniquely identify a target entry.
static LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, StringRef ParentName, unsigned DeviceID, unsigned FileID, unsigned Line, unsigned Count)
static constexpr const char * KernelNamePrefix
The prefix used for kernel names.
static LLVM_ABI const Target * lookupTarget(const Triple &TheTriple, std::string &Error)
lookupTarget - Lookup a target based on a target triple.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
bool Force
Apply loop unroll on any kind of loop (mainly to loops that fail runtime unrolling).
unsigned PartialOptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size, like OptSizeThreshold,...
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
unsigned OptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size (set to UINT_MAX to disable).
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...