LLVM 24.0.0git
OMPIRBuilder.cpp
Go to the documentation of this file.
1//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// This file implements the OpenMPIRBuilder class, which is used as a
11/// convenient way to create LLVM instructions for OpenMP directives.
12///
13//===----------------------------------------------------------------------===//
14
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/StringRef.h"
31#include "llvm/IR/Attributes.h"
32#include "llvm/IR/BasicBlock.h"
33#include "llvm/IR/CFG.h"
34#include "llvm/IR/CallingConv.h"
35#include "llvm/IR/Constant.h"
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/DIBuilder.h"
40#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
45#include "llvm/IR/LLVMContext.h"
46#include "llvm/IR/MDBuilder.h"
47#include "llvm/IR/Metadata.h"
49#include "llvm/IR/PassManager.h"
51#include "llvm/IR/Value.h"
54#include "llvm/Support/Error.h"
65
66#include <cstdint>
67#include <optional>
68
69#define DEBUG_TYPE "openmp-ir-builder"
70
71using namespace llvm;
72using namespace omp;
73
74static cl::opt<bool>
75 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
76 cl::desc("Use optimistic attributes describing "
77 "'as-if' properties of runtime calls."),
78 cl::init(false));
79
81 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
82 cl::desc("Factor for the unroll threshold to account for code "
83 "simplifications still taking place"),
84 cl::init(1.5));
85
87 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
88 cl::desc("Use a default max threads if none is provided."), cl::init(true));
89
90#ifndef NDEBUG
91/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
92/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
93/// an InsertPoint stores the instruction before something is inserted. For
94/// instance, if both point to the same instruction, two IRBuilders alternating
95/// creating instruction will cause the instructions to be interleaved.
98 if (!IP1.isSet() || !IP2.isSet())
99 return false;
100 return IP1.getBlock() == IP2.getBlock() && IP1.getPoint() == IP2.getPoint();
101}
102
104 // Valid ordered/unordered and base algorithm combinations.
105 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
106 case OMPScheduleType::UnorderedStaticChunked:
107 case OMPScheduleType::UnorderedStatic:
108 case OMPScheduleType::UnorderedDynamicChunked:
109 case OMPScheduleType::UnorderedGuidedChunked:
110 case OMPScheduleType::UnorderedRuntime:
111 case OMPScheduleType::UnorderedAuto:
112 case OMPScheduleType::UnorderedTrapezoidal:
113 case OMPScheduleType::UnorderedGreedy:
114 case OMPScheduleType::UnorderedBalanced:
115 case OMPScheduleType::UnorderedGuidedIterativeChunked:
116 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
117 case OMPScheduleType::UnorderedSteal:
118 case OMPScheduleType::UnorderedStaticBalancedChunked:
119 case OMPScheduleType::UnorderedGuidedSimd:
120 case OMPScheduleType::UnorderedRuntimeSimd:
121 case OMPScheduleType::OrderedStaticChunked:
122 case OMPScheduleType::OrderedStatic:
123 case OMPScheduleType::OrderedDynamicChunked:
124 case OMPScheduleType::OrderedGuidedChunked:
125 case OMPScheduleType::OrderedRuntime:
126 case OMPScheduleType::OrderedAuto:
127 case OMPScheduleType::OrderdTrapezoidal:
128 case OMPScheduleType::NomergeUnorderedStaticChunked:
129 case OMPScheduleType::NomergeUnorderedStatic:
130 case OMPScheduleType::NomergeUnorderedDynamicChunked:
131 case OMPScheduleType::NomergeUnorderedGuidedChunked:
132 case OMPScheduleType::NomergeUnorderedRuntime:
133 case OMPScheduleType::NomergeUnorderedAuto:
134 case OMPScheduleType::NomergeUnorderedTrapezoidal:
135 case OMPScheduleType::NomergeUnorderedGreedy:
136 case OMPScheduleType::NomergeUnorderedBalanced:
137 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
138 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
139 case OMPScheduleType::NomergeUnorderedSteal:
140 case OMPScheduleType::NomergeOrderedStaticChunked:
141 case OMPScheduleType::NomergeOrderedStatic:
142 case OMPScheduleType::NomergeOrderedDynamicChunked:
143 case OMPScheduleType::NomergeOrderedGuidedChunked:
144 case OMPScheduleType::NomergeOrderedRuntime:
145 case OMPScheduleType::NomergeOrderedAuto:
146 case OMPScheduleType::NomergeOrderedTrapezoidal:
147 case OMPScheduleType::OrderedDistributeChunked:
148 case OMPScheduleType::OrderedDistribute:
149 break;
150 default:
151 return false;
152 }
153
154 // Must not set both monotonicity modifiers at the same time.
155 OMPScheduleType MonotonicityFlags =
156 SchedType & OMPScheduleType::MonotonicityMask;
157 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
158 return false;
159
160 return true;
161}
162#endif
163
164/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
165/// debug location when the insert point is at the end of a block. It picks a
166/// location scoped to the current function: the block's last instruction
167/// location if the block is non-empty, otherwise a location synthesized from
168/// the function's subprogram (when the function has debug info).
171 Builder.restoreIP(IP);
172 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
173 // set the debug location from that instruction, so leave it alone.
174 llvm::BasicBlock *BB = Builder.GetInsertBlock();
175 if (Builder.GetInsertPoint() != BB->end())
176 return;
177
178 // At the end of a block, pick a location guaranteed to belong to the current
179 // insertion function's subprogram. Prefer the block's own last instruction;
180 // otherwise synthesize a location from the function's subprogram.
181 if (!BB->empty())
182 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());
183 else if (llvm::DISubprogram *FSP =
184 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
185 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
186 Builder.SetCurrentDebugLocation(
187 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
188 }
189}
190
191static bool hasGridValue(const Triple &T) {
192 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
193}
194
195static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
196 if (T.isAMDGPU()) {
197 StringRef Features =
198 Kernel->getFnAttribute("target-features").getValueAsString();
199 if (Features.count("+wavefrontsize64"))
202 }
203 if (T.isNVPTX())
205 if (T.isSPIRV())
207 llvm_unreachable("No grid value available for this architecture!");
208}
209
210/// Determine which scheduling algorithm to use, determined from schedule clause
211/// arguments.
212static OMPScheduleType
213getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
214 bool HasSimdModifier, bool HasDistScheduleChunks) {
215 // Currently, the default schedule it static.
216 switch (ClauseKind) {
217 case OMP_SCHEDULE_Default:
218 case OMP_SCHEDULE_Static:
219 return HasChunks ? OMPScheduleType::BaseStaticChunked
220 : OMPScheduleType::BaseStatic;
221 case OMP_SCHEDULE_Dynamic:
222 return OMPScheduleType::BaseDynamicChunked;
223 case OMP_SCHEDULE_Guided:
224 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
225 : OMPScheduleType::BaseGuidedChunked;
226 case OMP_SCHEDULE_Auto:
228 case OMP_SCHEDULE_Runtime:
229 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
230 : OMPScheduleType::BaseRuntime;
231 case OMP_SCHEDULE_Distribute:
232 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
233 : OMPScheduleType::BaseDistribute;
234 }
235 llvm_unreachable("unhandled schedule clause argument");
236}
237
238/// Adds ordering modifier flags to schedule type.
239static OMPScheduleType
241 bool HasOrderedClause) {
242 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
243 OMPScheduleType::None &&
244 "Must not have ordering nor monotonicity flags already set");
245
246 OMPScheduleType OrderingModifier = HasOrderedClause
247 ? OMPScheduleType::ModifierOrdered
248 : OMPScheduleType::ModifierUnordered;
249 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
250
251 // Unsupported combinations
252 if (OrderingScheduleType ==
253 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
254 return OMPScheduleType::OrderedGuidedChunked;
255 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
256 OMPScheduleType::ModifierOrdered))
257 return OMPScheduleType::OrderedRuntime;
258
259 return OrderingScheduleType;
260}
261
262/// Adds monotonicity modifier flags to schedule type.
263static OMPScheduleType
265 bool HasSimdModifier, bool HasMonotonic,
266 bool HasNonmonotonic, bool HasOrderedClause) {
267 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
268 OMPScheduleType::None &&
269 "Must not have monotonicity flags already set");
270 assert((!HasMonotonic || !HasNonmonotonic) &&
271 "Monotonic and Nonmonotonic are contradicting each other");
272
273 if (HasMonotonic) {
274 return ScheduleType | OMPScheduleType::ModifierMonotonic;
275 } else if (HasNonmonotonic) {
276 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
277 } else {
278 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
279 // If the static schedule kind is specified or if the ordered clause is
280 // specified, and if the nonmonotonic modifier is not specified, the
281 // effect is as if the monotonic modifier is specified. Otherwise, unless
282 // the monotonic modifier is specified, the effect is as if the
283 // nonmonotonic modifier is specified.
284 OMPScheduleType BaseScheduleType =
285 ScheduleType & ~OMPScheduleType::ModifierMask;
286 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
287 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
288 HasOrderedClause) {
289 // The monotonic is used by default in openmp runtime library, so no need
290 // to set it.
291 return ScheduleType;
292 } else {
293 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
294 }
295 }
296}
297
298/// Determine the schedule type using schedule and ordering clause arguments.
299static OMPScheduleType
300computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
301 bool HasSimdModifier, bool HasMonotonicModifier,
302 bool HasNonmonotonicModifier, bool HasOrderedClause,
303 bool HasDistScheduleChunks) {
305 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
306 OMPScheduleType OrderedSchedule =
307 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
309 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
310 HasNonmonotonicModifier, HasOrderedClause);
311
313 return Result;
314}
315
316/// Given a function, if it represents the entry point of a target kernel, this
317/// returns the execution mode flags associated with that kernel.
318static std::optional<omp::OMPTgtExecModeFlags>
320 CallInst *TargetInitCall = nullptr;
321 for (Instruction &Inst : Kernel.getEntryBlock()) {
322 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
323 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
324 TargetInitCall = Call;
325 break;
326 }
327 }
328 }
329
330 if (!TargetInitCall)
331 return std::nullopt;
332
333 // Get the kernel mode information from the global variable associated to the
334 // first argument to the call to __kmpc_target_init. Refer to
335 // createTargetInit() to see how this is initialized.
336 Value *InitOperand = TargetInitCall->getArgOperand(0);
337 GlobalVariable *KernelEnv = nullptr;
338 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
339 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
340 else
341 KernelEnv = cast<GlobalVariable>(InitOperand);
342 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
343 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
344 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
345 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
346}
347
348static bool isGenericKernel(Function &Fn) {
349 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
351 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
352}
353
354/// Make \p Source branch to \p Target.
355///
356/// Handles two situations:
357/// * \p Source already has an unconditional branch.
358/// * \p Source is a degenerate block (no terminator because the BB is
359/// the current head of the IR construction).
361 if (Instruction *Term = Source->getTerminatorOrNull()) {
362 auto *Br = cast<UncondBrInst>(Term);
363 BasicBlock *Succ = Br->getSuccessor();
364 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
365 Br->setSuccessor(Target);
366 return;
367 }
368
369 auto *NewBr = UncondBrInst::Create(Target, Source);
370 NewBr->setDebugLoc(DL);
371}
372
374 bool CreateBranch, DebugLoc DL) {
375 assert(New->getFirstInsertionPt() == New->begin() &&
376 "Target BB must not have PHI nodes");
377
378 // Move instructions to new block.
379 BasicBlock *Old = IP.getBlock();
380 // If the `Old` block is empty then there are no instructions to move. But in
381 // the new debug scheme, it could have trailing debug records which will be
382 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
383 // reasons:
384 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
385 // 2. Even if `New` is not empty, the rationale to move those records to `New`
386 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
387 // assumes that `Old` is optimized out and is going away. This is not the case
388 // here. The `Old` block is still being used e.g. a branch instruction is
389 // added to it later in this function.
390 // So we call `BasicBlock::splice` only when `Old` is not empty.
391 if (!Old->empty())
392 New->splice(New->begin(), Old, IP.getPoint(), Old->end());
393
394 if (CreateBranch) {
395 auto *NewBr = UncondBrInst::Create(New, Old);
396 NewBr->setDebugLoc(DL);
397 }
398}
399
400void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
401 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
402 BasicBlock *Old = Builder.GetInsertBlock();
403
404 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
405 if (CreateBranch)
406 Builder.SetInsertPoint(Old->getTerminator());
407 else
408 Builder.SetInsertPoint(Old);
409
410 // SetInsertPoint also updates the Builder's debug location, but we want to
411 // keep the one the Builder was configured to use.
412 Builder.SetCurrentDebugLocation(DebugLoc);
413}
414
416 DebugLoc DL, llvm::Twine Name) {
417 BasicBlock *Old = IP.getBlock();
419 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
420 Old->getParent(), Old->getNextNode());
421 spliceBB(IP, New, CreateBranch, DL);
422 New->replaceSuccessorsPhiUsesWith(Old, New);
423 return New;
424}
425
426BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
427 llvm::Twine Name) {
428 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
429 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
430 if (CreateBranch)
431 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
432 else
433 Builder.SetInsertPoint(Builder.GetInsertBlock());
434 // SetInsertPoint also updates the Builder's debug location, but we want to
435 // keep the one the Builder was configured to use.
436 Builder.SetCurrentDebugLocation(DebugLoc);
437 return New;
438}
439
440BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
441 llvm::Twine Name) {
442 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
443 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
444 if (CreateBranch)
445 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
446 else
447 Builder.SetInsertPoint(Builder.GetInsertBlock());
448 // SetInsertPoint also updates the Builder's debug location, but we want to
449 // keep the one the Builder was configured to use.
450 Builder.SetCurrentDebugLocation(DebugLoc);
451 return New;
452}
453
455 llvm::Twine Suffix) {
456 BasicBlock *Old = Builder.GetInsertBlock();
457 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
458}
459
460// This function creates a fake integer value and a fake use for the integer
461// value. It returns the fake value created. This is useful in modeling the
462// extra arguments to the outlined functions.
464 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
466 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
467 const Twine &Name = "", bool AsPtr = true,
468 bool Is64Bit = false) {
469 Builder.restoreIP(OuterAllocaIP);
470 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
471 Instruction *FakeVal;
472 AllocaInst *FakeValAddr =
473 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
474 ToBeDeleted.push_back(FakeValAddr);
475
476 if (AsPtr) {
477 FakeVal = FakeValAddr;
478 // 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 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.getBlock()->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.getBlock();
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, 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.getBlock()->end() == IP.getPoint()) {
2039 Builder.restoreIP(IP);
2040 Instruction *I = Builder.CreateBr(PRegExitBB);
2041 IP = InsertPointTy(I->getParent(), I->getIterator());
2042 }
2043 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
2044 IP.getBlock()->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, 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.getBlock()->getParent() &&
2192 "Dealloc block must be in the allocation's function to reuse "
2193 "its debug location");
2195 {InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2196 Builder.getCurrentDebugLocation()},
2197 Ptr, V.getType());
2198 }
2199 } else {
2200 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2201 V.getName() + ".reloaded");
2202 }
2203
2204 // Store to stack at end of the block that currently branches to the entry
2205 // block of the to-be-outlined region.
2206 Builder.SetInsertPoint(InsertBB,
2207 InsertBB->getTerminator()->getIterator());
2208 Builder.CreateStore(&V, Ptr);
2209
2210 // Load back next to allocations in the to-be-outlined region.
2211 Builder.restoreIP(InnerAllocaIP);
2212 Inner = Builder.CreateLoad(V.getType(), Ptr);
2213 }
2214
2215 Value *ReplacementValue = nullptr;
2216 CallInst *CI = dyn_cast<CallInst>(&V);
2217 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2218 ReplacementValue = PrivTID;
2219 } else {
2220 InsertPointOrErrorTy AfterIP =
2221 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2222 if (!AfterIP)
2223 return AfterIP.takeError();
2224 Builder.restoreIP(*AfterIP);
2225 InnerAllocaIP = {
2226 InnerAllocaIP.getBlock(),
2227 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2228
2229 assert(ReplacementValue &&
2230 "Expected copy/create callback to set replacement value!");
2231 if (ReplacementValue == &V)
2232 return Error::success();
2233 }
2234
2235 for (Use *UPtr : Uses)
2236 UPtr->set(ReplacementValue);
2237
2238 return Error::success();
2239 };
2240
2241 // Reset the inner alloca insertion as it will be used for loading the values
2242 // wrapped into pointers before passing them into the to-be-outlined region.
2243 // Configure it to insert immediately after the fake use of zero address so
2244 // that they are available in the generated body and so that the
2245 // OpenMP-related values (thread ID and zero address pointers) remain leading
2246 // in the argument list.
2247 InnerAllocaIP = IRBuilder<>::InsertPoint(
2248 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2249
2250 // Reset the outer alloca insertion point to the entry of the relevant block
2251 // in case it was invalidated.
2252 OuterAllocIP = IRBuilder<>::InsertPoint(
2253 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2254
2255 for (Value *Input : Inputs) {
2256 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2257 if (Error Err = PrivHelper(*Input))
2258 return Err;
2259 }
2260 LLVM_DEBUG({
2261 for (Value *Output : Outputs)
2262 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2263 });
2264 assert(Outputs.empty() &&
2265 "OpenMP outlining should not produce live-out values!");
2266
2267 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2268 LLVM_DEBUG({
2269 for (auto *BB : Blocks)
2270 dbgs() << " PBR: " << BB->getName() << "\n";
2271 });
2272
2273 // Adjust the finalization stack, verify the adjustment, and call the
2274 // finalize function a last time to finalize values between the pre-fini
2275 // block and the exit block if we left the parallel "the normal way".
2276 auto FiniInfo = FinalizationStack.pop_back_val();
2277 (void)FiniInfo;
2278 assert(FiniInfo.DK == OMPD_parallel &&
2279 "Unexpected finalization stack state!");
2280
2281 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2282
2283 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2284 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2285 if (!FiniBBOrErr)
2286 return FiniBBOrErr.takeError();
2287 {
2289 Builder.restoreIP(PreFiniIP);
2290 Builder.CreateBr(*FiniBBOrErr);
2291 // There's currently a branch to omp.par.exit. Delete it. We will get there
2292 // via the fini block
2293 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2294 Term->eraseFromParent();
2295 }
2296
2297 // Register the outlined info.
2298 addOutlineInfo(std::move(OI));
2299
2300 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2301 UI->eraseFromParent();
2302
2303 return AfterIP;
2304}
2305
2307 // Build call void __kmpc_flush(ident_t *loc)
2308 uint32_t SrcLocStrSize;
2309 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2310 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2311
2313 Args);
2314}
2315
2317 if (!updateToLocation(Loc))
2318 return;
2319 emitFlush(Loc);
2320}
2321
2323 Value *Message) {
2324 if (!updateToLocation(Loc))
2325 return;
2326
2327 // Build call void __kmpc_error(ident_t *loc, int severity,
2328 // const char *message)
2329 uint32_t SrcLocStrSize;
2330 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2331 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2332 // Severity: 1 = warning, 2 = fatal.
2333 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2334 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2335 Value *Args[] = {Ident, Severity, MessageArg};
2336
2338 Args);
2339}
2340
2342 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2343 uint32_t SrcLocStrSize;
2344 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2345 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2346 Constant *I32Null = ConstantInt::getNullValue(Int32);
2347 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2348
2350 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2351}
2352
2358
2360 const DependData &Dep) {
2361 // Store the pointer to the variable
2362 Value *Addr = Builder.CreateStructGEP(
2363 DependInfo, Entry,
2364 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2365 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2366 Builder.CreateStore(DepValPtr, Addr);
2367 // Store the size of the variable
2368 Value *Size = Builder.CreateStructGEP(
2369 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2370 Builder.CreateStore(
2371 ConstantInt::get(SizeTy,
2372 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2373 Size);
2374 // Store the dependency kind
2375 Value *Flags = Builder.CreateStructGEP(
2376 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2377 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2378 static_cast<unsigned int>(Dep.DepKind)),
2379 Flags);
2380}
2381
2382// Processes the dependencies in Dependencies and does the following
2383// - Allocates space on the stack of an array of DependInfo objects
2384// - Populates each DependInfo object with relevant information of
2385// the corresponding dependence.
2386// - All code is inserted in the entry block of the current function.
2388 OpenMPIRBuilder &OMPBuilder,
2390 // Early return if we have no dependencies to process
2391 if (Dependencies.empty())
2392 return nullptr;
2393
2394 // Given a vector of DependData objects, in this function we create an
2395 // array on the stack that holds kmp_depend_info objects corresponding
2396 // to each dependency. This is then passed to the OpenMP runtime.
2397 // For example, if there are 'n' dependencies then the following psedo
2398 // code is generated. Assume the first dependence is on a variable 'a'
2399 //
2400 // \code{c}
2401 // DepArray = alloc(n x sizeof(kmp_depend_info);
2402 // idx = 0;
2403 // DepArray[idx].base_addr = ptrtoint(&a);
2404 // DepArray[idx].len = 8;
2405 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2406 // ++idx;
2407 // DepArray[idx].base_addr = ...;
2408 // \endcode
2409
2410 IRBuilderBase &Builder = OMPBuilder.Builder;
2411 Type *DependInfo = OMPBuilder.DependInfo;
2412
2413 Value *DepArray = nullptr;
2414 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2415 {
2416 // Use a InsertPointGuard to restore the location back along with the
2417 // insertion point.
2418 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2419 Builder.SetInsertPoint(
2420 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2421 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2422 }
2423
2424 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2425 Value *Base =
2426 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2427 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2428 }
2429 return DepArray;
2430}
2431
2433 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2434 // global_tid);
2435 uint32_t SrcLocStrSize;
2436 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2437 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2438 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2439
2440 // Ignore return result until untied tasks are supported.
2442 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2443}
2444
2446 DependenciesInfo Dependencies) {
2447 if (!updateToLocation(Loc))
2448 return;
2449
2450 Value *DepArray = nullptr;
2451 Type *DepArrayTy = nullptr;
2452 Value *NumDeps = nullptr;
2453 if (Dependencies.DepArray) {
2454 DepArray = Dependencies.DepArray;
2455 NumDeps = Dependencies.NumDeps;
2456 } else if (!Dependencies.Deps.empty()) {
2457 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2458 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2459 {
2461 BasicBlock &entryBB =
2462 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2463 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2464 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2465 }
2466
2467 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2468 Value *Base =
2469 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2470 this->emitTaskDependency(Builder, Base, Dep);
2471 }
2472 }
2473
2474 if (DepArray) {
2475 uint32_t SrcLocStrSize;
2476 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2477 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2478 Value *Args[] = {
2479 Ident,
2480 getOrCreateThreadID(Ident),
2481 NumDeps,
2482 DepArray,
2483 ConstantInt::get(Builder.getInt32Ty(), 0),
2485 ConstantInt::get(Builder.getInt32Ty(), false)};
2488 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2489 Args);
2490 } else {
2492 }
2493}
2494
2495/// Create the task duplication function passed to kmpc_taskloop.
2496Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2497 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2498 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2499 if (!DupCB)
2501 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2502
2503 // From OpenMP Runtime p_task_dup_t:
2504 // Routine optionally generated by the compiler for setting the lastprivate
2505 // flag and calling needed constructors for private/firstprivate objects (used
2506 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2507 // lastprivate flag.
2508 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2509
2510 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2511
2512 FunctionType *DupFuncTy = FunctionType::get(
2513 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2514 /*isVarArg=*/false);
2515
2516 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2517 "omp_taskloop_dup", M);
2518 Value *DestTaskArg = DupFunction->getArg(0);
2519 Value *SrcTaskArg = DupFunction->getArg(1);
2520 Value *LastprivateFlagArg = DupFunction->getArg(2);
2521 DestTaskArg->setName("dest_task");
2522 SrcTaskArg->setName("src_task");
2523 LastprivateFlagArg->setName("lastprivate_flag");
2524
2525 IRBuilderBase::InsertPointGuard Guard(Builder);
2526 Builder.SetInsertPoint(
2527 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2528
2529 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2530 Type *TaskWithPrivatesTy =
2531 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2532 Value *TaskPrivates = Builder.CreateGEP(
2533 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2534 Value *ContextPtr = Builder.CreateGEP(
2535 PrivatesTy, TaskPrivates,
2536 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2537 return ContextPtr;
2538 };
2539
2540 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2541 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2542
2543 DestTaskContextPtr->setName("destPtr");
2544 SrcTaskContextPtr->setName("srcPtr");
2545
2546 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2547 DupFunction->getEntryBlock().begin());
2548 InsertPointTy CodeGenIP = Builder.saveIP();
2549 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2550 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2551 if (!AfterIPOrError)
2552 return AfterIPOrError.takeError();
2553 Builder.restoreIP(*AfterIPOrError);
2554
2555 Builder.CreateRetVoid();
2556
2557 return DupFunction;
2558}
2559
2560OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2561 const LocationDescription &Loc, InsertPointTy AllocaIP,
2562 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2563 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2564 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2565 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2566 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2567 Value *TaskContextStructPtrVal, bool FreeAgent) {
2568
2569 if (!updateToLocation(Loc))
2570 return InsertPointTy();
2571
2572 uint32_t SrcLocStrSize;
2573 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2574 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2575
2576 BasicBlock *TaskloopExitBB =
2577 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2578 BasicBlock *TaskloopBodyBB =
2579 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2580 BasicBlock *TaskloopAllocaBB =
2581 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2582
2583 InsertPointTy TaskloopAllocaIP =
2584 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2585 InsertPointTy TaskloopBodyIP =
2586 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2587
2588 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2589 return Err;
2590
2591 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2592 if (!result) {
2593 return result.takeError();
2594 }
2595
2596 llvm::CanonicalLoopInfo *CLI = result.get();
2597 auto OI = std::make_unique<OutlineInfo>();
2598 OI->EntryBB = TaskloopAllocaBB;
2599 OI->OuterAllocBB = AllocaIP.getBlock();
2600 OI->ExitBB = TaskloopExitBB;
2601 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2602 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2603
2604 // Add the thread ID argument.
2605 SmallVector<Instruction *> ToBeDeleted;
2606 // dummy instruction to be used as a fake argument
2607 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2608 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2609 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2610 TaskloopAllocaIP, "lb", false, true);
2611 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2612 TaskloopAllocaIP, "ub", false, true);
2613 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2614 TaskloopAllocaIP, "step", false, true);
2615 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2616 // aggregate struct
2617 OI->Inputs.insert(FakeLB);
2618 OI->Inputs.insert(FakeUB);
2619 OI->Inputs.insert(FakeStep);
2620 if (TaskContextStructPtrVal)
2621 OI->Inputs.insert(TaskContextStructPtrVal);
2622 assert(((TaskContextStructPtrVal && DupCB) ||
2623 (!TaskContextStructPtrVal && !DupCB)) &&
2624 "Task context struct ptr and duplication callback must be both set "
2625 "or both null");
2626
2627 // It isn't safe to run the duplication bodygen callback inside the post
2628 // outlining callback so this has to be run now before we know the real task
2629 // shareds structure type.
2630 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2631 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2632 Type *FakeSharedsTy = StructType::get(
2633 Builder.getContext(),
2634 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2635 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2636 FakeSharedsTy,
2637 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2638 if (!TaskDupFnOrErr) {
2639 return TaskDupFnOrErr.takeError();
2640 }
2641 Value *TaskDupFn = *TaskDupFnOrErr;
2642
2643 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2644 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2645 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2646 FakeSharedsTy, Final, Mergeable, Priority,
2647 NumOfCollapseLoops,
2648 FreeAgent](Function &OutlinedFn) mutable {
2649 // Replace the Stale CI by appropriate RTL function call.
2650 assert(OutlinedFn.hasOneUse() &&
2651 "there must be a single user for the outlined function");
2652 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2653
2654 /* Create the casting for the Bounds Values that can be used when outlining
2655 * to replace the uses of the fakes with real values */
2656 BasicBlock *CodeReplBB = StaleCI->getParent();
2657 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2658 Value *CastedLBVal =
2659 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2660 Value *CastedUBVal =
2661 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2662 Value *CastedStepVal =
2663 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2664
2665 Builder.SetInsertPoint(StaleCI);
2666
2667 // Gather the arguments for emitting the runtime call for
2668 // @__kmpc_omp_task_alloc
2669 Function *TaskAllocFn =
2670 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2671
2672 Value *ThreadID = getOrCreateThreadID(Ident);
2673
2674 if (!NoGroup) {
2675 // Emit runtime call for @__kmpc_taskgroup
2676 Function *TaskgroupFn =
2677 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2678 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2679 }
2680
2681 // `flags` Argument Configuration
2682 // Task is tied if (Flags & 1) == 1.
2683 // Task is untied if (Flags & 1) == 0.
2684 // Task is final if (Flags & 2) == 2.
2685 // Task is not final if (Flags & 2) == 0.
2686 // Task is mergeable if (Flags & 4) == 4.
2687 // Task is not mergeable if (Flags & 4) == 0.
2688 // Task is priority if (Flags & 32) == 32.
2689 // Task is not priority if (Flags & 32) == 0.
2690 // Task is free-agent eligible if (Flags & 128) == 128.
2691 // Task is not free-agent eligible if (Flags & 128) == 0.
2692 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2693 if (Final)
2694 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2695 if (Mergeable)
2696 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2697 if (Priority)
2698 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2699 if (FreeAgent)
2700 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2701
2702 Value *TaskSize = Builder.getInt64(
2703 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2704
2705 AllocaInst *ArgStructAlloca =
2707 assert(ArgStructAlloca &&
2708 "Unable to find the alloca instruction corresponding to arguments "
2709 "for extracted function");
2710 std::optional<TypeSize> ArgAllocSize =
2711 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2712 assert(ArgAllocSize &&
2713 "Unable to determine size of arguments for extracted function");
2714 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2715
2716 // Emit the @__kmpc_omp_task_alloc runtime call
2717 // The runtime call returns a pointer to an area where the task captured
2718 // variables must be copied before the task is run (TaskData)
2719 CallInst *TaskData = Builder.CreateCall(
2720 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2721 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2722 /*task_func=*/&OutlinedFn});
2723
2724 Value *Shareds = StaleCI->getArgOperand(1);
2725 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2726 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2727 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2728 SharedsSize);
2729 // Get the pointer to loop lb, ub, step from task ptr
2730 // and set up the lowerbound,upperbound and step values
2731 llvm::Value *Lb = Builder.CreateGEP(
2732 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2733
2734 llvm::Value *Ub = Builder.CreateGEP(
2735 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2736
2737 llvm::Value *Step = Builder.CreateGEP(
2738 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2739 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2740
2741 // set up the arguments for emitting kmpc_taskloop runtime call
2742 // setting values for ifval, nogroup, sched, grainsize, task_dup
2743 Value *IfCondVal =
2744 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2745 : Builder.getInt32(1);
2746 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2747 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2748 Value *NoGroupVal = Builder.getInt32(1);
2749 Value *SchedVal = Builder.getInt32(Sched);
2750 Value *GrainSizeVal =
2751 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2752 : Builder.getInt64(0);
2753 Value *TaskDup = TaskDupFn;
2754
2755 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2756 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2757
2758 // taskloop runtime call
2759 Function *TaskloopFn =
2760 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2761 Builder.CreateCall(TaskloopFn, Args);
2762
2763 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2764 // nogroup is not defined
2765 if (!NoGroup) {
2766 Function *EndTaskgroupFn =
2767 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2768 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2769 }
2770
2771 StaleCI->eraseFromParent();
2772
2773 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2774
2775 LoadInst *SharedsOutlined =
2776 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2777 OutlinedFn.getArg(1)->replaceUsesWithIf(
2778 SharedsOutlined,
2779 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2780
2781 Value *IV = CLI->getIndVar();
2782 Type *IVTy = IV->getType();
2783 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2784
2785 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2786 // UpperBound. These GEP's can be reused for loading the tasks respective
2787 // bounds.
2788 Value *TaskLB = nullptr;
2789 Value *TaskUB = nullptr;
2790 Value *TaskStep = nullptr;
2791 Value *LoadTaskLB = nullptr;
2792 Value *LoadTaskUB = nullptr;
2793 Value *LoadTaskStep = nullptr;
2794 for (Instruction &I : *TaskloopAllocaBB) {
2795 if (I.getOpcode() == Instruction::GetElementPtr) {
2796 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2797 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2798 switch (CI->getZExtValue()) {
2799 case 0:
2800 TaskLB = &I;
2801 break;
2802 case 1:
2803 TaskUB = &I;
2804 break;
2805 case 2:
2806 TaskStep = &I;
2807 break;
2808 }
2809 }
2810 } else if (I.getOpcode() == Instruction::Load) {
2811 LoadInst &Load = cast<LoadInst>(I);
2812 if (Load.getPointerOperand() == TaskLB) {
2813 assert(TaskLB != nullptr && "Expected value for TaskLB");
2814 LoadTaskLB = &I;
2815 } else if (Load.getPointerOperand() == TaskUB) {
2816 assert(TaskUB != nullptr && "Expected value for TaskUB");
2817 LoadTaskUB = &I;
2818 } else if (Load.getPointerOperand() == TaskStep) {
2819 assert(TaskStep != nullptr && "Expected value for TaskStep");
2820 LoadTaskStep = &I;
2821 }
2822 }
2823 }
2824
2825 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2826
2827 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2828 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2829 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2830 Value *TripCountMinusOne = Builder.CreateSDiv(
2831 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2832 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2833 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2834 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2835 // set the trip count in the CLI
2836 CLI->setTripCount(CastedTripCount);
2837
2838 Builder.SetInsertPoint(CLI->getBody(),
2839 CLI->getBody()->getFirstInsertionPt());
2840
2841 if (NumOfCollapseLoops > 1) {
2842 llvm::SmallVector<User *> UsersToReplace;
2843 // When using the collapse clause, the bounds of the loop have to be
2844 // adjusted to properly represent the iterator of the outer loop.
2845 Value *IVPlusTaskLB = Builder.CreateAdd(
2846 CLI->getIndVar(),
2847 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2848 // To ensure every Use is correctly captured, we first want to record
2849 // which users to replace the value in, and then replace the value.
2850 for (auto IVUse = CLI->getIndVar()->uses().begin();
2851 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2852 User *IVUser = IVUse->getUser();
2853 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2854 if (Op->getOpcode() == Instruction::URem ||
2855 Op->getOpcode() == Instruction::UDiv) {
2856 UsersToReplace.push_back(IVUser);
2857 }
2858 }
2859 }
2860 for (User *User : UsersToReplace) {
2861 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2862 }
2863 } else {
2864 // The canonical loop is generated with a fixed lower bound. We need to
2865 // update the index calculation code to use the task's lower bound. The
2866 // generated code looks like this:
2867 // %omp_loop.iv = phi ...
2868 // ...
2869 // %tmp = mul [type] %omp_loop.iv, step
2870 // %user_index = add [type] tmp, lb
2871 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2872 // of the normalised induction variable:
2873 // 1. This one: converting the normalised IV to the user IV
2874 // 2. The increment (add)
2875 // 3. The comparison against the trip count (icmp)
2876 // (1) is the only use that is a mul followed by an add so this cannot
2877 // match other IR.
2878 assert(CLI->getIndVar()->getNumUses() == 3 &&
2879 "Canonical loop should have exactly three uses of the ind var");
2880 for (User *IVUser : CLI->getIndVar()->users()) {
2881 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2882 if (Mul->getOpcode() == Instruction::Mul) {
2883 for (User *MulUser : Mul->users()) {
2884 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2885 if (Add->getOpcode() == Instruction::Add) {
2886 Add->setOperand(1, CastedTaskLB);
2887 }
2888 }
2889 }
2890 }
2891 }
2892 }
2893 }
2894
2895 FakeLB->replaceAllUsesWith(CastedLBVal);
2896 FakeUB->replaceAllUsesWith(CastedUBVal);
2897 FakeStep->replaceAllUsesWith(CastedStepVal);
2898 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2899 I->eraseFromParent();
2900 }
2901 };
2902
2903 addOutlineInfo(std::move(OI));
2904 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2905 return Builder.saveIP();
2906}
2907
2910 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2912 llvm::Type::getInt32Ty(M.getContext()));
2913}
2914
2916 const LocationDescription &Loc, InsertPointTy AllocaIP,
2917 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2918 bool Tied, Value *Final, Value *IfCondition,
2919 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2920 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2921
2922 if (!updateToLocation(Loc))
2923 return InsertPointTy();
2924
2925 uint32_t SrcLocStrSize;
2926 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2927 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2928 // The current basic block is split into four basic blocks. After outlining,
2929 // they will be mapped as follows:
2930 // ```
2931 // def current_fn() {
2932 // current_basic_block:
2933 // br label %task.exit
2934 // task.exit:
2935 // ; instructions after task
2936 // }
2937 // def outlined_fn() {
2938 // task.alloca:
2939 // br label %task.body
2940 // task.body:
2941 // ret void
2942 // }
2943 // ```
2944 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2945 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2946 BasicBlock *TaskAllocaBB =
2947 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2948
2949 InsertPointTy TaskAllocaIP =
2950 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2951 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2952 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2953 return Err;
2954
2955 auto OI = std::make_unique<OutlineInfo>();
2956 OI->EntryBB = TaskAllocaBB;
2957 OI->OuterAllocBB = AllocaIP.getBlock();
2958 OI->ExitBB = TaskExitBB;
2959 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2960 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2961
2962 // Add the thread ID argument.
2964 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2965 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2966
2967 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2968 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2969 TaskAllocaBB,
2970 ToBeDeleted](Function &OutlinedFn) mutable {
2971 // Replace the Stale CI by appropriate RTL function call.
2972 assert(OutlinedFn.hasOneUse() &&
2973 "there must be a single user for the outlined function");
2974 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2975
2976 // HasShareds is true if any variables are captured in the outlined region,
2977 // false otherwise.
2978 bool HasShareds = StaleCI->arg_size() > 1;
2979 Builder.SetInsertPoint(StaleCI);
2980
2981 // Gather the arguments for emitting the runtime call for
2982 // @__kmpc_omp_task_alloc
2983 Function *TaskAllocFn =
2984 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2985
2986 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2987 // call.
2988 Value *ThreadID = getOrCreateThreadID(Ident);
2989
2990 // Argument - `flags`
2991 // Task is tied iff (Flags & 1) == 1.
2992 // Task is untied iff (Flags & 1) == 0.
2993 // Task is final iff (Flags & 2) == 2.
2994 // Task is not final iff (Flags & 2) == 0.
2995 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2996 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2997 // Task is detachable iff (Flags & 64) == 64.
2998 // Task is not detachable iff (Flags & 64) == 0.
2999 // Task is priority iff (Flags & 32) == 32.
3000 // Task is not priority iff (Flags & 32) == 0.
3001 // Task is free-agent eligible iff (Flags & 128) == 128.
3002 // Task is not free-agent eligible iff (Flags & 128) == 0.
3003 // TODO: Handle the other flags.
3004 Value *Flags = Builder.getInt32(Tied);
3005 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
3006 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
3007 if (Final) {
3008 Value *FinalFlag =
3009 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3010 Flags = Builder.CreateOr(FinalFlag, Flags);
3011 }
3012
3013 if (Mergeable || UseMergedIf0Path)
3014 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3015 if (EventHandle)
3016 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3017 if (Priority)
3018 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3019 if (FreeAgent)
3020 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3021
3022 // Argument - `sizeof_kmp_task_t` (TaskSize)
3023 // Tasksize refers to the size in bytes of kmp_task_t data structure
3024 // including private vars accessed in task.
3025 // TODO: add kmp_task_t_with_privates (privates)
3026 Value *TaskSize = Builder.getInt64(
3027 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3028
3029 // Argument - `sizeof_shareds` (SharedsSize)
3030 // SharedsSize refers to the shareds array size in the kmp_task_t data
3031 // structure.
3032 Value *SharedsSize = Builder.getInt64(0);
3033 if (HasShareds) {
3034 AllocaInst *ArgStructAlloca =
3036 assert(ArgStructAlloca &&
3037 "Unable to find the alloca instruction corresponding to arguments "
3038 "for extracted function");
3039 std::optional<TypeSize> ArgAllocSize =
3040 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3041 assert(ArgAllocSize &&
3042 "Unable to determine size of arguments for extracted function");
3043 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3044 }
3045 // Emit the @__kmpc_omp_task_alloc runtime call
3046 // The runtime call returns a pointer to an area where the task captured
3047 // variables must be copied before the task is run (TaskData)
3049 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3050 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3051 /*task_func=*/&OutlinedFn});
3052
3053 if (Affinities.Count && Affinities.Info) {
3055 OMPRTL___kmpc_omp_reg_task_with_affinity);
3056
3057 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3058 Affinities.Count, Affinities.Info});
3059 }
3060
3061 // Emit detach clause initialization.
3062 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3063 // task_descriptor);
3064 if (EventHandle) {
3066 OMPRTL___kmpc_task_allow_completion_event);
3067 llvm::Value *EventVal =
3068 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3069 llvm::Value *EventHandleAddr =
3070 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3071 Builder.getPtrTy(0));
3072 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3073 Builder.CreateStore(EventVal, EventHandleAddr);
3074 }
3075 // Copy the arguments for outlined function
3076 if (HasShareds) {
3077 Value *Shareds = StaleCI->getArgOperand(1);
3078 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3079 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3080 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3081 SharedsSize);
3082 }
3083
3084 if (Priority) {
3085 //
3086 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3087 // we populate the priority information into the "kmp_task_t" here
3088 //
3089 // The struct "kmp_task_t" definition is available in kmp.h
3090 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3091 // data2 is used for priority
3092 //
3093 Type *Int32Ty = Builder.getInt32Ty();
3094 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3095 // kmp_task_t* => { ptr }
3096 Type *TaskPtr = StructType::get(VoidPtr);
3097 Value *TaskGEP =
3098 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3099 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3100 Type *TaskStructType = StructType::get(
3101 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3102 Value *PriorityData = Builder.CreateInBoundsGEP(
3103 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3104 // kmp_cmplrdata_t => { ptr, ptr }
3105 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3106 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3107 PriorityData, {Zero, Zero});
3108 Builder.CreateStore(Priority, CmplrData);
3109 }
3110
3111 Value *DepArray = nullptr;
3112 Value *NumDeps = nullptr;
3113 if (Dependencies.DepArray) {
3114 DepArray = Dependencies.DepArray;
3115 NumDeps = Dependencies.NumDeps;
3116 } else if (!Dependencies.Deps.empty()) {
3117 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3118 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3119 }
3120
3121 // In the presence of the `if` clause, the following IR is generated:
3122 // ...
3123 // %data = call @__kmpc_omp_task_alloc(...)
3124 // br i1 %if_condition, label %then, label %else
3125 // then:
3126 // call @__kmpc_omp_task(...)
3127 // br label %exit
3128 // else:
3129 // ;; Wait for resolution of dependencies, if any, before
3130 // ;; beginning the task
3131 // call @__kmpc_omp_wait_deps(...)
3132 // call @__kmpc_omp_task_begin_if0(...)
3133 // call @outlined_fn(...)
3134 // call @__kmpc_omp_task_complete_if0(...)
3135 // br label %exit
3136 // exit:
3137 // ...
3138 if (IfCondition && !UseMergedIf0Path) {
3139 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3140 // terminator.
3141 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3142 Instruction *IfTerminator =
3143 Builder.GetInsertPoint()->getParent()->getTerminator();
3144 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3145 Builder.SetInsertPoint(IfTerminator);
3146 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3147 &ElseTI);
3148 Builder.SetInsertPoint(ElseTI);
3149
3150 if (DepArray) {
3151 Function *TaskWaitFn =
3152 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3154 TaskWaitFn,
3155 {Ident, ThreadID, NumDeps, DepArray,
3156 ConstantInt::get(Builder.getInt32Ty(), 0),
3158 }
3159 Function *TaskBeginFn =
3160 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3161 Function *TaskCompleteFn =
3162 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3163 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3164 CallInst *CI = nullptr;
3165 if (HasShareds)
3166 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3167 else
3168 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3169 CI->setDebugLoc(StaleCI->getDebugLoc());
3170 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3171 Builder.SetInsertPoint(ThenTI);
3172 }
3173
3174 if (DepArray) {
3175 Function *TaskFn =
3176 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3178 TaskFn,
3179 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3180 ConstantInt::get(Builder.getInt32Ty(), 0),
3182
3183 } else {
3184 // Emit the @__kmpc_omp_task runtime call to spawn the task
3185 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3186 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3187 }
3188
3189 StaleCI->eraseFromParent();
3190
3191 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3192 if (HasShareds) {
3193 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3194 OutlinedFn.getArg(1)->replaceUsesWithIf(
3195 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3196 }
3197
3198 // The insert point may refer to one of the instructions about to be
3199 // deleted. It is not needed anymore so clear it instead of leaving it
3200 // dangling.
3201 Builder.ClearInsertionPoint();
3202 for (Instruction *I : llvm::reverse(ToBeDeleted))
3203 I->eraseFromParent();
3204 };
3205
3206 addOutlineInfo(std::move(OI));
3207 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3208
3209 return Builder.saveIP();
3210}
3211
3213 const LocationDescription &Loc, InsertPointTy AllocaIP,
3214 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3215 if (!updateToLocation(Loc))
3216 return InsertPointTy();
3217
3218 uint32_t SrcLocStrSize;
3219 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3220 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3221 Value *ThreadID = getOrCreateThreadID(Ident);
3222
3223 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3224 Function *TaskgroupFn =
3225 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3226 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3227
3228 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3229 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3230 return Err;
3231
3232 Builder.SetInsertPoint(TaskgroupExitBB);
3233 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3234 Function *EndTaskgroupFn =
3235 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3236 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3237
3238 return Builder.saveIP();
3239}
3240
3242 const LocationDescription &Loc, InsertPointTy AllocaIP,
3244 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3245 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3246
3247 if (!updateToLocation(Loc))
3248 return Loc.IP;
3249
3250 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3251
3252 // Each section is emitted as a switch case
3253 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3254 // -> OMP.createSection() which generates the IR for each section
3255 // Iterate through all sections and emit a switch construct:
3256 // switch (IV) {
3257 // case 0:
3258 // <SectionStmt[0]>;
3259 // break;
3260 // ...
3261 // case <NumSection> - 1:
3262 // <SectionStmt[<NumSection> - 1]>;
3263 // break;
3264 // }
3265 // ...
3266 // section_loop.after:
3267 // <FiniCB>;
3268 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3269 Builder.restoreIP(CodeGenIP);
3271 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3272 Function *CurFn = Continue->getParent();
3273 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3274
3275 unsigned CaseNumber = 0;
3276 for (auto SectionCB : SectionCBs) {
3278 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3279 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3280 Builder.SetInsertPoint(CaseBB);
3281 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3282 if (Error Err =
3283 SectionCB(InsertPointTy(),
3284 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3285 return Err;
3286 CaseNumber++;
3287 }
3288 // remove the existing terminator from body BB since there can be no
3289 // terminators after switch/case
3290 return Error::success();
3291 };
3292 // Loop body ends here
3293 // LowerBound, UpperBound, and STride for createCanonicalLoop
3294 Type *I32Ty = Type::getInt32Ty(M.getContext());
3295 Value *LB = ConstantInt::get(I32Ty, 0);
3296 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3297 Value *ST = ConstantInt::get(I32Ty, 1);
3299 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3300 if (!LoopInfo)
3301 return LoopInfo.takeError();
3302
3303 InsertPointOrErrorTy WsloopIP =
3304 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3305 WorksharingLoopType::ForStaticLoop, !IsNowait);
3306 if (!WsloopIP)
3307 return WsloopIP.takeError();
3308 InsertPointTy AfterIP = *WsloopIP;
3309
3310 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3311 assert(LoopFini && "Bad structure of static workshare loop finalization");
3312
3313 // Apply the finalization callback in LoopAfterBB
3314 auto FiniInfo = FinalizationStack.pop_back_val();
3315 assert(FiniInfo.DK == OMPD_sections &&
3316 "Unexpected finalization stack state!");
3317 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3318 return Err;
3319
3320 return AfterIP;
3321}
3322
3325 BodyGenCallbackTy BodyGenCB,
3326 FinalizeCallbackTy FiniCB) {
3327 if (!updateToLocation(Loc))
3328 return Loc.IP;
3329
3330 auto FiniCBWrapper = [&](InsertPointTy IP) {
3331 if (IP.getBlock()->end() != IP.getPoint())
3332 return FiniCB(IP);
3333 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3334 // will fail because that function requires the Finalization Basic Block to
3335 // have a terminator, which is already removed by EmitOMPRegionBody.
3336 // IP is currently at cancelation block.
3337 // We need to backtrack to the condition block to fetch
3338 // the exit block and create a branch from cancelation
3339 // to exit block.
3341 Builder.restoreIP(IP);
3342 auto *CaseBB = Loc.IP.getBlock();
3343 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3344 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3345 Instruction *I = Builder.CreateBr(ExitBB);
3346 IP = InsertPointTy(I->getParent(), I->getIterator());
3347 return FiniCB(IP);
3348 };
3349
3350 Directive OMPD = Directive::OMPD_sections;
3351 // Since we are using Finalization Callback here, HasFinalize
3352 // and IsCancellable have to be true
3353 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3354 /*Conditional*/ false, /*hasFinalize*/ true,
3355 /*IsCancellable*/ true);
3356}
3357
3363
3364Value *OpenMPIRBuilder::getGPUThreadID() {
3367 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3368 {});
3369}
3370
3371Value *OpenMPIRBuilder::getGPUWarpSize() {
3373 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3374}
3375
3376Value *OpenMPIRBuilder::getNVPTXWarpID() {
3377 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3378 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3379}
3380
3381Value *OpenMPIRBuilder::getNVPTXLaneID() {
3382 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3383 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3384 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3385 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3386 "nvptx_lane_id");
3387}
3388
3389Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3390 Type *ToType) {
3391 Type *FromType = From->getType();
3392 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3393 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3394 assert(FromSize > 0 && "From size must be greater than zero");
3395 assert(ToSize > 0 && "To size must be greater than zero");
3396 if (FromType == ToType)
3397 return From;
3398 if (FromSize == ToSize)
3399 return Builder.CreateBitCast(From, ToType);
3400 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3401 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3402 InsertPointTy SaveIP = Builder.saveIP();
3403 Builder.restoreIP(AllocaIP);
3404 Value *CastItem = Builder.CreateAlloca(ToType);
3405 Builder.restoreIP(SaveIP);
3406
3407 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3408 CastItem, Builder.getPtrTy(0));
3409 Builder.CreateStore(From, ValCastItem);
3410 return Builder.CreateLoad(ToType, CastItem);
3411}
3412
3413Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3414 Value *Element,
3415 Type *ElementType,
3416 Value *Offset) {
3417 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3418 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3419
3420 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3421 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3422 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3423 Value *WarpSize =
3424 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3426 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3427 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3428 Value *WarpSizeCast =
3429 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3430 Value *ShuffleCall =
3431 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3432 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3433 // down to the requested element type, otherwise storing the result would
3434 // write past the end of an element narrower than the shuffle width.
3435 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3436}
3437
3438void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3439 Value *DstAddr, Type *ElemType,
3440 Value *Offset, Type *ReductionArrayTy,
3441 bool IsByRefElem) {
3442 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3443 // Create the loop over the big sized data.
3444 // ptr = (void*)Elem;
3445 // ptrEnd = (void*) Elem + 1;
3446 // Step = 8;
3447 // while (ptr + Step < ptrEnd)
3448 // shuffle((int64_t)*ptr);
3449 // Step = 4;
3450 // while (ptr + Step < ptrEnd)
3451 // shuffle((int32_t)*ptr);
3452 // ...
3453 Type *IndexTy = Builder.getIndexTy(
3454 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3455 Value *ElemPtr = DstAddr;
3456 Value *Ptr = SrcAddr;
3457 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3458 if (Size < IntSize)
3459 continue;
3460 Type *IntType = Builder.getIntNTy(IntSize * 8);
3461 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3462 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3463 Value *SrcAddrGEP =
3464 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3465 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3466 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3467
3468 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3469 if ((Size / IntSize) > 1) {
3470 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3471 SrcAddrGEP, Builder.getPtrTy());
3472 BasicBlock *PreCondBB =
3473 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3474 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3475 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3476 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3477 emitBlock(PreCondBB, CurFunc);
3478 PHINode *PhiSrc =
3479 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3480 PhiSrc->addIncoming(Ptr, CurrentBB);
3481 PHINode *PhiDest =
3482 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3483 PhiDest->addIncoming(ElemPtr, CurrentBB);
3484 Ptr = PhiSrc;
3485 ElemPtr = PhiDest;
3486 Value *PtrDiff = Builder.CreatePtrDiff(
3487 Builder.getInt8Ty(), PtrEnd,
3488 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3489 Builder.CreateCondBr(
3490 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3491 ExitBB);
3492 emitBlock(ThenBB, CurFunc);
3493 Value *Res = createRuntimeShuffleFunction(
3494 AllocaIP,
3495 Builder.CreateAlignedLoad(
3496 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3497 IntType, Offset);
3498 Builder.CreateAlignedStore(Res, ElemPtr,
3499 M.getDataLayout().getPrefTypeAlign(ElemType));
3500 Value *LocalPtr =
3501 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3502 Value *LocalElemPtr =
3503 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3504 PhiSrc->addIncoming(LocalPtr, ThenBB);
3505 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3506 emitBranch(PreCondBB);
3507 emitBlock(ExitBB, CurFunc);
3508 } else {
3509 // The shuffled value comes back as the chunk's integer type, so the
3510 // store covers exactly this chunk regardless of what ElemType is.
3511 Value *Res = createRuntimeShuffleFunction(
3512 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3513 Builder.CreateStore(Res, ElemPtr);
3514 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3515 ElemPtr =
3516 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3517 }
3518 Size = Size % IntSize;
3519 }
3520}
3521
3522Error OpenMPIRBuilder::emitReductionListCopy(
3523 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3524 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3525 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3526 Type *IndexTy = Builder.getIndexTy(
3527 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3528 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3529
3530 // Iterates, element-by-element, through the source Reduce list and
3531 // make a copy.
3532 for (auto En : enumerate(ReductionInfos)) {
3533 const ReductionInfo &RI = En.value();
3534 Value *SrcElementAddr = nullptr;
3535 AllocaInst *DestAlloca = nullptr;
3536 Value *DestElementAddr = nullptr;
3537 Value *DestElementPtrAddr = nullptr;
3538 // Should we shuffle in an element from a remote lane?
3539 bool ShuffleInElement = false;
3540 // Set to true to update the pointer in the dest Reduce list to a
3541 // newly created element.
3542 bool UpdateDestListPtr = false;
3543
3544 // Step 1.1: Get the address for the src element in the Reduce list.
3545 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3546 ReductionArrayTy, SrcBase,
3547 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3548 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3549
3550 // Step 1.2: Create a temporary to store the element in the destination
3551 // Reduce list.
3552 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3553 ReductionArrayTy, DestBase,
3554 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3555 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3556 switch (Action) {
3558 InsertPointTy CurIP = Builder.saveIP();
3559 Builder.restoreIP(AllocaIP);
3560
3561 Type *DestAllocaType =
3562 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3563 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3564 ".omp.reduction.element");
3565 DestAlloca->setAlignment(
3566 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3567 DestElementAddr = DestAlloca;
3568 DestElementAddr =
3569 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3570 DestElementAddr->getName() + ".ascast");
3571 Builder.restoreIP(CurIP);
3572 ShuffleInElement = true;
3573 UpdateDestListPtr = true;
3574 break;
3575 }
3577 DestElementAddr =
3578 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3579 break;
3580 }
3581 }
3582
3583 // Now that all active lanes have read the element in the
3584 // Reduce list, shuffle over the value from the remote lane.
3585 if (ShuffleInElement) {
3586 Type *ShuffleType = RI.ElementType;
3587 Value *ShuffleSrcAddr = SrcElementAddr;
3588 Value *ShuffleDestAddr = DestElementAddr;
3589 AllocaInst *LocalStorage = nullptr;
3590
3591 if (IsByRefElem) {
3592 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3593 assert(RI.ByRefAllocatedType &&
3594 "Expected by-ref allocated type to be set");
3595 // For by-ref reductions, we need to copy from the remote lane the
3596 // actual value of the partial reduction computed by that remote lane;
3597 // rather than, for example, a pointer to that data or, even worse, a
3598 // pointer to the descriptor of the by-ref reduction element.
3599 ShuffleType = RI.ByRefElementType;
3600
3601 if (RI.DataPtrPtrGen) {
3602 // Descriptor-based by-ref: extract data pointer from descriptor.
3603 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3604 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3605
3606 if (!GenResult)
3607 return GenResult.takeError();
3608
3609 ShuffleSrcAddr =
3610 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3611
3612 {
3613 InsertPointTy OldIP = Builder.saveIP();
3614 Builder.restoreIP(AllocaIP);
3615
3616 LocalStorage = Builder.CreateAlloca(ShuffleType);
3617 Builder.restoreIP(OldIP);
3618 ShuffleDestAddr = LocalStorage;
3619 }
3620 } else {
3621 // Non-descriptor by-ref: the pointer already references data
3622 // directly. Shuffle into the destination alloca.
3623 ShuffleDestAddr = DestElementAddr;
3624 }
3625 }
3626
3627 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3628 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3629
3630 if (IsByRefElem && RI.DataPtrPtrGen) {
3631 // Copy descriptor from source and update base_ptr to shuffled data
3632 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3633 DestAlloca, Builder.getPtrTy(), ".ascast");
3634
3635 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3636 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3637 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3638
3639 if (!GenResult)
3640 return GenResult.takeError();
3641 }
3642 } else {
3643 switch (RI.EvaluationKind) {
3644 case EvalKind::Scalar: {
3645 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3646 // Store the source element value to the dest element address.
3647 Builder.CreateStore(Elem, DestElementAddr);
3648 break;
3649 }
3650 case EvalKind::Complex: {
3651 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3652 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3653 Value *SrcReal = Builder.CreateLoad(
3654 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3655 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3656 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3657 Value *SrcImg = Builder.CreateLoad(
3658 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3659
3660 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3661 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3662 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3663 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3664 Builder.CreateStore(SrcReal, DestRealPtr);
3665 Builder.CreateStore(SrcImg, DestImgPtr);
3666 break;
3667 }
3668 case EvalKind::Aggregate: {
3669 Value *SizeVal = Builder.getInt64(
3670 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3671 Builder.CreateMemCpy(
3672 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3673 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3674 SizeVal, false);
3675 break;
3676 }
3677 };
3678 }
3679
3680 // Step 3.1: Modify reference in dest Reduce list as needed.
3681 // Modifying the reference in Reduce list to point to the newly
3682 // created element. The element is live in the current function
3683 // scope and that of functions it invokes (i.e., reduce_function).
3684 // RemoteReduceData[i] = (void*)&RemoteElem
3685 if (UpdateDestListPtr) {
3686 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3687 DestElementAddr, Builder.getPtrTy(),
3688 DestElementAddr->getName() + ".ascast");
3689 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3690 }
3691 }
3692
3693 return Error::success();
3694}
3695
3696Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3697 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3698 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3699 IRBuilder<>::InsertPointGuard IPG(Builder);
3700 LLVMContext &Ctx = M.getContext();
3701 FunctionType *FuncTy = FunctionType::get(
3702 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3703 /* IsVarArg */ false);
3704 Function *WcFunc =
3706 "_omp_reduction_inter_warp_copy_func", &M);
3707 WcFunc->setCallingConv(Config.getRuntimeCC());
3708 WcFunc->setAttributes(FuncAttrs);
3709 WcFunc->addParamAttr(0, Attribute::NoUndef);
3710 WcFunc->addParamAttr(1, Attribute::NoUndef);
3711 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3712 Builder.SetInsertPoint(EntryBB);
3713 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3714
3715 // ReduceList: thread local Reduce list.
3716 // At the stage of the computation when this function is called, partially
3717 // aggregated values reside in the first lane of every active warp.
3718 Argument *ReduceListArg = WcFunc->getArg(0);
3719 // NumWarps: number of warps active in the parallel region. This could
3720 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3721 Argument *NumWarpsArg = WcFunc->getArg(1);
3722
3723 // This array is used as a medium to transfer, one reduce element at a time,
3724 // the data from the first lane of every warp to lanes in the first warp
3725 // in order to perform the final step of a reduction in a parallel region
3726 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3727 // for reduced latency, as well as to have a distinct copy for concurrently
3728 // executing target regions. The array is declared with common linkage so
3729 // as to be shared across compilation units.
3730 StringRef TransferMediumName =
3731 "__openmp_nvptx_data_transfer_temporary_storage";
3732 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3733 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3734 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3735 if (!TransferMedium) {
3736 TransferMedium = new GlobalVariable(
3737 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3738 UndefValue::get(ArrayTy), TransferMediumName,
3739 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3740 /*AddressSpace=*/3);
3741 }
3742
3743 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3744 Value *GPUThreadID = getGPUThreadID();
3745 // nvptx_lane_id = nvptx_id % warpsize
3746 Value *LaneID = getNVPTXLaneID();
3747 // nvptx_warp_id = nvptx_id / warpsize
3748 Value *WarpID = getNVPTXWarpID();
3749
3750 InsertPointTy AllocaIP =
3751 InsertPointTy(Builder.GetInsertBlock(),
3752 Builder.GetInsertBlock()->getFirstInsertionPt());
3753 Type *Arg0Type = ReduceListArg->getType();
3754 Type *Arg1Type = NumWarpsArg->getType();
3755 Builder.restoreIP(AllocaIP);
3756 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3757 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3758 AllocaInst *NumWarpsAlloca =
3759 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3760 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3761 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3762 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3763 NumWarpsAlloca, Builder.getPtrTy(0),
3764 NumWarpsAlloca->getName() + ".ascast");
3765 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3766 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3767 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3768 InsertPointTy CodeGenIP =
3769 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3770 Builder.restoreIP(CodeGenIP);
3771
3772 Value *ReduceList =
3773 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3774
3775 for (auto En : enumerate(ReductionInfos)) {
3776 //
3777 // Warp master copies reduce element to transfer medium in __shared__
3778 // memory.
3779 //
3780 const ReductionInfo &RI = En.value();
3781 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3782 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3783 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3784 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3785 Type *CType = Builder.getIntNTy(TySize * 8);
3786
3787 unsigned NumIters = RealTySize / TySize;
3788 if (NumIters == 0)
3789 continue;
3790 Value *Cnt = nullptr;
3791 Value *CntAddr = nullptr;
3792 BasicBlock *PrecondBB = nullptr;
3793 BasicBlock *ExitBB = nullptr;
3794 if (NumIters > 1) {
3795 CodeGenIP = Builder.saveIP();
3796 Builder.restoreIP(AllocaIP);
3797 CntAddr =
3798 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3799
3800 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3801 CntAddr->getName() + ".ascast");
3802 Builder.restoreIP(CodeGenIP);
3803 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3804 CntAddr,
3805 /*Volatile=*/false);
3806 PrecondBB = BasicBlock::Create(Ctx, "precond");
3807 ExitBB = BasicBlock::Create(Ctx, "exit");
3808 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3809 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3810 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3811 /*Volatile=*/false);
3812 Value *Cmp = Builder.CreateICmpULT(
3813 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3814 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3815 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3816 }
3817
3818 // kmpc_barrier.
3819 InsertPointOrErrorTy BarrierIP1 =
3821 omp::Directive::OMPD_unknown,
3822 /* ForceSimpleCall */ false,
3823 /* CheckCancelFlag */ true);
3824 if (!BarrierIP1)
3825 return BarrierIP1.takeError();
3826 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3827 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3828 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3829
3830 // if (lane_id == 0)
3831 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3832 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3833 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3834
3835 // Reduce element = LocalReduceList[i]
3836 auto *RedListArrayTy =
3837 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3838 Type *IndexTy = Builder.getIndexTy(
3839 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3840 Value *ElemPtrPtr =
3841 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3842 {ConstantInt::get(IndexTy, 0),
3843 ConstantInt::get(IndexTy, En.index())});
3844 // elemptr = ((CopyType*)(elemptrptr)) + I
3845 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3846
3847 if (IsByRefElem && RI.DataPtrPtrGen) {
3848 InsertPointOrErrorTy GenRes =
3849 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3850
3851 if (!GenRes)
3852 return GenRes.takeError();
3853
3854 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3855 }
3856
3857 if (NumIters > 1)
3858 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3859
3860 // Get pointer to location in transfer medium.
3861 // MediumPtr = &medium[warp_id]
3862 Value *MediumPtr = Builder.CreateInBoundsGEP(
3863 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3864 // elem = *elemptr
3865 //*MediumPtr = elem
3866 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3867 // Store the source element value to the dest element address.
3868 Builder.CreateStore(Elem, MediumPtr,
3869 /*IsVolatile*/ true);
3870 Builder.CreateBr(MergeBB);
3871
3872 // else
3873 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3874 Builder.CreateBr(MergeBB);
3875
3876 // endif
3877 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3878 InsertPointOrErrorTy BarrierIP2 =
3880 omp::Directive::OMPD_unknown,
3881 /* ForceSimpleCall */ false,
3882 /* CheckCancelFlag */ true);
3883 if (!BarrierIP2)
3884 return BarrierIP2.takeError();
3885
3886 // Warp 0 copies reduce element from transfer medium
3887 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3888 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3889 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3890
3891 Value *NumWarpsVal =
3892 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3893 // Up to 32 threads in warp 0 are active.
3894 Value *IsActiveThread =
3895 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3896 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3897
3898 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3899
3900 // SecMediumPtr = &medium[tid]
3901 // SrcMediumVal = *SrcMediumPtr
3902 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3903 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3904 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3905 Value *TargetElemPtrPtr =
3906 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3907 {ConstantInt::get(IndexTy, 0),
3908 ConstantInt::get(IndexTy, En.index())});
3909 Value *TargetElemPtrVal =
3910 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3911 Value *TargetElemPtr = TargetElemPtrVal;
3912
3913 if (IsByRefElem && RI.DataPtrPtrGen) {
3914 InsertPointOrErrorTy GenRes =
3915 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3916
3917 if (!GenRes)
3918 return GenRes.takeError();
3919
3920 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3921 }
3922
3923 if (NumIters > 1)
3924 TargetElemPtr =
3925 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3926
3927 // *TargetElemPtr = SrcMediumVal;
3928 Value *SrcMediumValue =
3929 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3930 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3931 Builder.CreateBr(W0MergeBB);
3932
3933 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3934 Builder.CreateBr(W0MergeBB);
3935
3936 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3937
3938 if (NumIters > 1) {
3939 Cnt = Builder.CreateNSWAdd(
3940 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3941 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3942
3943 auto *CurFn = Builder.GetInsertBlock()->getParent();
3944 emitBranch(PrecondBB);
3945 emitBlock(ExitBB, CurFn);
3946 }
3947 RealTySize %= TySize;
3948 }
3949 }
3950
3951 Builder.CreateRetVoid();
3952
3953 return WcFunc;
3954}
3955
3956Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3957 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3958 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3959 LLVMContext &Ctx = M.getContext();
3960 IRBuilder<>::InsertPointGuard IPG(Builder);
3961 FunctionType *FuncTy =
3962 FunctionType::get(Builder.getVoidTy(),
3963 {Builder.getPtrTy(), Builder.getInt16Ty(),
3964 Builder.getInt16Ty(), Builder.getInt16Ty()},
3965 /* IsVarArg */ false);
3966 Function *SarFunc =
3968 "_omp_reduction_shuffle_and_reduce_func", &M);
3969 SarFunc->setCallingConv(Config.getRuntimeCC());
3970 SarFunc->setAttributes(FuncAttrs);
3971 SarFunc->addParamAttr(0, Attribute::NoUndef);
3972 SarFunc->addParamAttr(1, Attribute::NoUndef);
3973 SarFunc->addParamAttr(2, Attribute::NoUndef);
3974 SarFunc->addParamAttr(3, Attribute::NoUndef);
3975 SarFunc->addParamAttr(1, Attribute::SExt);
3976 SarFunc->addParamAttr(2, Attribute::SExt);
3977 SarFunc->addParamAttr(3, Attribute::SExt);
3978 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3979 Builder.SetInsertPoint(EntryBB);
3980 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3981
3982 // Thread local Reduce list used to host the values of data to be reduced.
3983 Argument *ReduceListArg = SarFunc->getArg(0);
3984 // Current lane id; could be logical.
3985 Argument *LaneIDArg = SarFunc->getArg(1);
3986 // Offset of the remote source lane relative to the current lane.
3987 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3988 // Algorithm version. This is expected to be known at compile time.
3989 Argument *AlgoVerArg = SarFunc->getArg(3);
3990
3991 Type *ReduceListArgType = ReduceListArg->getType();
3992 Type *LaneIDArgType = LaneIDArg->getType();
3993 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3994 Value *ReduceListAlloca = Builder.CreateAlloca(
3995 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3996 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3997 LaneIDArg->getName() + ".addr");
3998 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3999 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
4000 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4001 AlgoVerArg->getName() + ".addr");
4002 ArrayType *RedListArrayTy =
4003 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4004
4005 // Create a local thread-private variable to host the Reduce list
4006 // from a remote lane.
4007 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
4008 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
4009
4010 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4011 ReduceListAlloca, ReduceListArgType,
4012 ReduceListAlloca->getName() + ".ascast");
4013 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4014 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4015 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4016 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4017 RemoteLaneOffsetAlloca->getName() + ".ascast");
4018 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4019 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4020 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4021 RemoteReductionListAlloca, Builder.getPtrTy(),
4022 RemoteReductionListAlloca->getName() + ".ascast");
4023
4024 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4025 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4026 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4027 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4028
4029 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4030 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4031 Value *RemoteLaneOffset =
4032 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4033 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4034
4035 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4036
4037 // This loop iterates through the list of reduce elements and copies,
4038 // element by element, from a remote lane in the warp to RemoteReduceList,
4039 // hosted on the thread's stack.
4040 Error EmitRedLsCpRes = emitReductionListCopy(
4041 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4042 ReduceList, RemoteListAddrCast, IsByRef,
4043 {RemoteLaneOffset, nullptr, nullptr});
4044
4045 if (EmitRedLsCpRes)
4046 return EmitRedLsCpRes;
4047
4048 // The actions to be performed on the Remote Reduce list is dependent
4049 // on the algorithm version.
4050 //
4051 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4052 // LaneId % 2 == 0 && Offset > 0):
4053 // do the reduction value aggregation
4054 //
4055 // The thread local variable Reduce list is mutated in place to host the
4056 // reduced data, which is the aggregated value produced from local and
4057 // remote lanes.
4058 //
4059 // Note that AlgoVer is expected to be a constant integer known at compile
4060 // time.
4061 // When AlgoVer==0, the first conjunction evaluates to true, making
4062 // the entire predicate true during compile time.
4063 // When AlgoVer==1, the second conjunction has only the second part to be
4064 // evaluated during runtime. Other conjunctions evaluates to false
4065 // during compile time.
4066 // When AlgoVer==2, the third conjunction has only the second part to be
4067 // evaluated during runtime. Other conjunctions evaluates to false
4068 // during compile time.
4069 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4070 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4071 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4072 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4073 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4074 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4075 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4076 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4077 Value *RemoteOffsetComp =
4078 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4079 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4080 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4081 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4082
4083 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4084 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4085 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4086
4087 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4088 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4089 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4090 ReduceList, Builder.getPtrTy());
4091 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4092 RemoteListAddrCast, Builder.getPtrTy());
4093 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4094 ->addFnAttr(Attribute::NoUnwind);
4095 Builder.CreateBr(MergeBB);
4096
4097 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4098 Builder.CreateBr(MergeBB);
4099
4100 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4101
4102 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4103 // Reduce list.
4104 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4105 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4106 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4107
4108 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4109 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4110 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4111 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4112
4113 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4114
4115 EmitRedLsCpRes = emitReductionListCopy(
4116 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4117 RemoteListAddrCast, ReduceList, IsByRef);
4118
4119 if (EmitRedLsCpRes)
4120 return EmitRedLsCpRes;
4121
4122 Builder.CreateBr(CpyMergeBB);
4123
4124 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4125 Builder.CreateBr(CpyMergeBB);
4126
4127 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4128
4129 Builder.CreateRetVoid();
4130
4131 return SarFunc;
4132}
4133
4135OpenMPIRBuilder::generateReductionDescriptor(
4136 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4137 Type *DescriptorType,
4138 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4139 DataPtrPtrGen) {
4140
4141 // Copy the source descriptor to preserve all metadata (rank, extents,
4142 // strides, etc.)
4143 Value *DescriptorSize =
4144 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4145 Builder.CreateMemCpy(
4146 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4147 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4148 DescriptorSize);
4149
4150 // Update the base pointer field to point to the local shuffled data
4151 Value *DataPtrField;
4152 InsertPointOrErrorTy GenResult =
4153 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4154
4155 if (!GenResult)
4156 return GenResult.takeError();
4157
4158 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4159 DataPtr, Builder.getPtrTy(), ".ascast"),
4160 DataPtrField);
4161
4162 return Builder.saveIP();
4163}
4164
4165Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4166 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4167 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4168 InsertPointTy OldIP = Builder.saveIP();
4169 Builder.restoreIP(AllocaIP);
4170
4171 AllocaInst *DescriptorAlloca =
4172 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4173 DescriptorAlloca->setAlignment(
4174 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4175 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4176 DescriptorAlloca, DescriptorPtrTy,
4177 DescriptorAlloca->getName() + ".ascast");
4178
4179 Builder.restoreIP(OldIP);
4180
4181 InsertPointOrErrorTy GenResult =
4182 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4183 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4184 if (!GenResult)
4185 return GenResult.takeError();
4186
4187 return DescriptorAddr;
4188}
4189
4190Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4191 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4192 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4193 IRBuilder<>::InsertPointGuard IPG(Builder);
4194 LLVMContext &Ctx = M.getContext();
4195 FunctionType *FuncTy = FunctionType::get(
4196 Builder.getVoidTy(),
4197 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4198 /* IsVarArg */ false);
4199 Function *LtGCFunc =
4201 "_omp_reduction_list_to_global_copy_func", &M);
4202 LtGCFunc->setAttributes(FuncAttrs);
4203 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4204 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4205 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4206
4207 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4208 Builder.SetInsertPoint(EntryBlock);
4209 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4210
4211 // Buffer: global reduction buffer.
4212 Argument *BufferArg = LtGCFunc->getArg(0);
4213 // Idx: index of the buffer.
4214 Argument *IdxArg = LtGCFunc->getArg(1);
4215 // ReduceList: thread local Reduce list.
4216 Argument *ReduceListArg = LtGCFunc->getArg(2);
4217
4218 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4219 BufferArg->getName() + ".addr");
4220 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4221 IdxArg->getName() + ".addr");
4222 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4223 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4224 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4225 BufferArgAlloca, Builder.getPtrTy(),
4226 BufferArgAlloca->getName() + ".ascast");
4227 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4228 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4229 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4230 ReduceListArgAlloca, Builder.getPtrTy(),
4231 ReduceListArgAlloca->getName() + ".ascast");
4232
4233 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4234 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4235 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4236
4237 Value *LocalReduceList =
4238 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4239 Value *BufferArgVal =
4240 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4241 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4242 Type *IndexTy = Builder.getIndexTy(
4243 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4244 for (auto En : enumerate(ReductionInfos)) {
4245 const ReductionInfo &RI = En.value();
4246 auto *RedListArrayTy =
4247 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4248 // Reduce element = LocalReduceList[i]
4249 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4250 RedListArrayTy, LocalReduceList,
4251 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4252 // elemptr = ((CopyType*)(elemptrptr)) + I
4253 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4254
4255 // Global = Buffer.VD[Idx];
4256 Value *BufferVD =
4257 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4258 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4259 ReductionsBufferTy, BufferVD, 0, En.index());
4260
4261 switch (RI.EvaluationKind) {
4262 case EvalKind::Scalar: {
4263 Value *TargetElement;
4264
4265 if (IsByRef.empty() || !IsByRef[En.index()]) {
4266 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4267 } else {
4268 if (RI.DataPtrPtrGen) {
4269 InsertPointOrErrorTy GenResult =
4270 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4271
4272 if (!GenResult)
4273 return GenResult.takeError();
4274
4275 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4276 }
4277 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4278 }
4279
4280 Builder.CreateStore(TargetElement, GlobVal);
4281 break;
4282 }
4283 case EvalKind::Complex: {
4284 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4285 RI.ElementType, ElemPtr, 0, 0, ".realp");
4286 Value *SrcReal = Builder.CreateLoad(
4287 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4288 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4289 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4290 Value *SrcImg = Builder.CreateLoad(
4291 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4292
4293 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4294 RI.ElementType, GlobVal, 0, 0, ".realp");
4295 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4296 RI.ElementType, GlobVal, 0, 1, ".imagp");
4297 Builder.CreateStore(SrcReal, DestRealPtr);
4298 Builder.CreateStore(SrcImg, DestImgPtr);
4299 break;
4300 }
4301 case EvalKind::Aggregate: {
4302 Value *SizeVal =
4303 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4304 Builder.CreateMemCpy(
4305 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4306 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4307 break;
4308 }
4309 }
4310 }
4311
4312 Builder.CreateRetVoid();
4313 return LtGCFunc;
4314}
4315
4316Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4317 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4318 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4319 IRBuilder<>::InsertPointGuard IPG(Builder);
4320 LLVMContext &Ctx = M.getContext();
4321 FunctionType *FuncTy = FunctionType::get(
4322 Builder.getVoidTy(),
4323 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4324 /* IsVarArg */ false);
4325 Function *LtGRFunc =
4327 "_omp_reduction_list_to_global_reduce_func", &M);
4328 LtGRFunc->setAttributes(FuncAttrs);
4329 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4330 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4331 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4332
4333 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4334 Builder.SetInsertPoint(EntryBlock);
4335 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4336
4337 // Buffer: global reduction buffer.
4338 Argument *BufferArg = LtGRFunc->getArg(0);
4339 // Idx: index of the buffer.
4340 Argument *IdxArg = LtGRFunc->getArg(1);
4341 // ReduceList: thread local Reduce list.
4342 Argument *ReduceListArg = LtGRFunc->getArg(2);
4343
4344 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4345 BufferArg->getName() + ".addr");
4346 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4347 IdxArg->getName() + ".addr");
4348 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4349 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4350 auto *RedListArrayTy =
4351 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4352
4353 // 1. Build a list of reduction variables.
4354 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4355 Value *LocalReduceList =
4356 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4357
4358 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4359
4360 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4361 BufferArgAlloca, Builder.getPtrTy(),
4362 BufferArgAlloca->getName() + ".ascast");
4363 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4364 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4365 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4366 ReduceListArgAlloca, Builder.getPtrTy(),
4367 ReduceListArgAlloca->getName() + ".ascast");
4368 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4369 LocalReduceList, Builder.getPtrTy(),
4370 LocalReduceList->getName() + ".ascast");
4371
4372 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4373 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4374 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4375
4376 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4377 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4378 Type *IndexTy = Builder.getIndexTy(
4379 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4380 for (auto En : enumerate(ReductionInfos)) {
4381 const ReductionInfo &RI = En.value();
4382
4383 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4384 RedListArrayTy, LocalReduceListAddrCast,
4385 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4386 Value *BufferVD =
4387 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4388 // Global = Buffer.VD[Idx];
4389 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4390 ReductionsBufferTy, BufferVD, 0, En.index());
4391
4392 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4393 // Get source descriptor from the reduce list argument
4394 Value *ReduceList =
4395 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4396 Value *SrcElementPtrPtr =
4397 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4398 {ConstantInt::get(IndexTy, 0),
4399 ConstantInt::get(IndexTy, En.index())});
4400 Value *SrcDescriptorAddr =
4401 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4402
4403 // Copy descriptor from source and update base_ptr to global buffer data
4404 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4405 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4406 if (!ByRefAlloc)
4407 return ByRefAlloc.takeError();
4408
4409 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4410 } else {
4411 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4412 }
4413 }
4414
4415 // Call reduce_function(GlobalReduceList, ReduceList)
4416 Value *ReduceList =
4417 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4418 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4419 ->addFnAttr(Attribute::NoUnwind);
4420 Builder.CreateRetVoid();
4421 return LtGRFunc;
4422}
4423
4424Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4425 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4426 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4427 IRBuilder<>::InsertPointGuard IPG(Builder);
4428 LLVMContext &Ctx = M.getContext();
4429 FunctionType *FuncTy = FunctionType::get(
4430 Builder.getVoidTy(),
4431 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4432 /* IsVarArg */ false);
4433 Function *GtLCFunc =
4435 "_omp_reduction_global_to_list_copy_func", &M);
4436 GtLCFunc->setAttributes(FuncAttrs);
4437 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4438 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4439 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4440
4441 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4442 Builder.SetInsertPoint(EntryBlock);
4443 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4444
4445 // Buffer: global reduction buffer.
4446 Argument *BufferArg = GtLCFunc->getArg(0);
4447 // Idx: index of the buffer.
4448 Argument *IdxArg = GtLCFunc->getArg(1);
4449 // ReduceList: thread local Reduce list.
4450 Argument *ReduceListArg = GtLCFunc->getArg(2);
4451
4452 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4453 BufferArg->getName() + ".addr");
4454 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4455 IdxArg->getName() + ".addr");
4456 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4457 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4458 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4459 BufferArgAlloca, Builder.getPtrTy(),
4460 BufferArgAlloca->getName() + ".ascast");
4461 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4462 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4463 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4464 ReduceListArgAlloca, Builder.getPtrTy(),
4465 ReduceListArgAlloca->getName() + ".ascast");
4466 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4467 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4468 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4469
4470 Value *LocalReduceList =
4471 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4472 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4473 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4474 Type *IndexTy = Builder.getIndexTy(
4475 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4476 for (auto En : enumerate(ReductionInfos)) {
4477 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4478 auto *RedListArrayTy =
4479 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4480 // Reduce element = LocalReduceList[i]
4481 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4482 RedListArrayTy, LocalReduceList,
4483 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4484 // elemptr = ((CopyType*)(elemptrptr)) + I
4485 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4486 // Global = Buffer.VD[Idx];
4487 Value *BufferVD =
4488 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4489 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4490 ReductionsBufferTy, BufferVD, 0, En.index());
4491
4492 switch (RI.EvaluationKind) {
4493 case EvalKind::Scalar: {
4494 Type *ElemType = RI.ElementType;
4495
4496 if (!IsByRef.empty() && IsByRef[En.index()]) {
4497 ElemType = RI.ByRefElementType;
4498 if (RI.DataPtrPtrGen) {
4499 InsertPointOrErrorTy GenResult =
4500 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4501
4502 if (!GenResult)
4503 return GenResult.takeError();
4504
4505 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4506 }
4507 }
4508
4509 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4510 Builder.CreateStore(TargetElement, ElemPtr);
4511 break;
4512 }
4513 case EvalKind::Complex: {
4514 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4515 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4516 Value *SrcReal = Builder.CreateLoad(
4517 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4518 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4519 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4520 Value *SrcImg = Builder.CreateLoad(
4521 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4522
4523 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4524 RI.ElementType, ElemPtr, 0, 0, ".realp");
4525 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4526 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4527 Builder.CreateStore(SrcReal, DestRealPtr);
4528 Builder.CreateStore(SrcImg, DestImgPtr);
4529 break;
4530 }
4531 case EvalKind::Aggregate: {
4532 Value *SizeVal =
4533 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4534 Builder.CreateMemCpy(
4535 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4536 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4537 SizeVal, false);
4538 break;
4539 }
4540 }
4541 }
4542
4543 Builder.CreateRetVoid();
4544 return GtLCFunc;
4545}
4546
4547Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4548 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4549 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4550 IRBuilder<>::InsertPointGuard IPG(Builder);
4551 LLVMContext &Ctx = M.getContext();
4552 auto *FuncTy = FunctionType::get(
4553 Builder.getVoidTy(),
4554 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4555 /* IsVarArg */ false);
4556 Function *GtLRFunc =
4558 "_omp_reduction_global_to_list_reduce_func", &M);
4559 GtLRFunc->setAttributes(FuncAttrs);
4560 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4561 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4562 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4563
4564 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4565 Builder.SetInsertPoint(EntryBlock);
4566 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4567
4568 // Buffer: global reduction buffer.
4569 Argument *BufferArg = GtLRFunc->getArg(0);
4570 // Idx: index of the buffer.
4571 Argument *IdxArg = GtLRFunc->getArg(1);
4572 // ReduceList: thread local Reduce list.
4573 Argument *ReduceListArg = GtLRFunc->getArg(2);
4574
4575 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4576 BufferArg->getName() + ".addr");
4577 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4578 IdxArg->getName() + ".addr");
4579 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4580 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4581 ArrayType *RedListArrayTy =
4582 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4583
4584 // 1. Build a list of reduction variables.
4585 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4586 Value *LocalReduceList =
4587 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4588
4589 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4590
4591 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4592 BufferArgAlloca, Builder.getPtrTy(),
4593 BufferArgAlloca->getName() + ".ascast");
4594 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4595 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4596 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4597 ReduceListArgAlloca, Builder.getPtrTy(),
4598 ReduceListArgAlloca->getName() + ".ascast");
4599 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4600 LocalReduceList, Builder.getPtrTy(),
4601 LocalReduceList->getName() + ".ascast");
4602
4603 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4604 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4605 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4606
4607 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4608 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4609 Type *IndexTy = Builder.getIndexTy(
4610 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4611 for (auto En : enumerate(ReductionInfos)) {
4612 const ReductionInfo &RI = En.value();
4613
4614 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4615 RedListArrayTy, ReductionList,
4616 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4617 // Global = Buffer.VD[Idx];
4618 Value *BufferVD =
4619 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4620 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4621 ReductionsBufferTy, BufferVD, 0, En.index());
4622
4623 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4624 // Get source descriptor from the reduce list
4625 Value *ReduceListVal =
4626 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4627 Value *SrcElementPtrPtr =
4628 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4629 {ConstantInt::get(IndexTy, 0),
4630 ConstantInt::get(IndexTy, En.index())});
4631 Value *SrcDescriptorAddr =
4632 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4633
4634 // Copy descriptor from source and update base_ptr to global buffer data
4635 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4636 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4637 if (!ByRefAlloc)
4638 return ByRefAlloc.takeError();
4639
4640 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4641 } else {
4642 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4643 }
4644 }
4645
4646 // Call reduce_function(ReduceList, GlobalReduceList)
4647 Value *ReduceList =
4648 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4649 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4650 ->addFnAttr(Attribute::NoUnwind);
4651 Builder.CreateRetVoid();
4652 return GtLRFunc;
4653}
4654
4655std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4656 std::string Suffix =
4657 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4658 return (Name + Suffix).str();
4659}
4660
4661Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4662 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4664 AttributeList FuncAttrs) {
4665 IRBuilder<>::InsertPointGuard IPG(Builder);
4666 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4667 {Builder.getPtrTy(), Builder.getPtrTy()},
4668 /* IsVarArg */ false);
4669 std::string Name = getReductionFuncName(ReducerName);
4670 Function *ReductionFunc =
4672 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4673 ReductionFunc->setAttributes(FuncAttrs);
4674 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4675 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4676 BasicBlock *EntryBB =
4677 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4678 Builder.SetInsertPoint(EntryBB);
4679 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4680
4681 // Need to alloca memory here and deal with the pointers before getting
4682 // LHS/RHS pointers out
4683 Value *LHSArrayPtr = nullptr;
4684 Value *RHSArrayPtr = nullptr;
4685 Argument *Arg0 = ReductionFunc->getArg(0);
4686 Argument *Arg1 = ReductionFunc->getArg(1);
4687 Type *Arg0Type = Arg0->getType();
4688 Type *Arg1Type = Arg1->getType();
4689
4690 Value *LHSAlloca =
4691 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4692 Value *RHSAlloca =
4693 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4694 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4695 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4696 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4697 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4698 Builder.CreateStore(Arg0, LHSAddrCast);
4699 Builder.CreateStore(Arg1, RHSAddrCast);
4700 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4701 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4702
4703 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4704 Type *IndexTy = Builder.getIndexTy(
4705 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4706 SmallVector<Value *> LHSPtrs, RHSPtrs;
4707 for (auto En : enumerate(ReductionInfos)) {
4708 const ReductionInfo &RI = En.value();
4709 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4710 RedArrayTy, RHSArrayPtr,
4711 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4712 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4713 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4714 RHSI8Ptr, RI.PrivateVariable->getType(),
4715 RHSI8Ptr->getName() + ".ascast");
4716
4717 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4718 RedArrayTy, LHSArrayPtr,
4719 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4720 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4721 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4722 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4723
4725 LHSPtrs.emplace_back(LHSPtr);
4726 RHSPtrs.emplace_back(RHSPtr);
4727 } else {
4728 Value *LHS = LHSPtr;
4729 Value *RHS = RHSPtr;
4730
4731 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4732 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4733 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4734 }
4735
4736 Value *Reduced;
4737 InsertPointOrErrorTy AfterIP =
4738 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4739 if (!AfterIP)
4740 return AfterIP.takeError();
4741 if (!Builder.GetInsertBlock())
4742 return ReductionFunc;
4743
4744 Builder.restoreIP(*AfterIP);
4745
4746 if (!IsByRef.empty() && !IsByRef[En.index()])
4747 Builder.CreateStore(Reduced, LHSPtr);
4748 }
4749 }
4750
4752 for (auto En : enumerate(ReductionInfos)) {
4753 unsigned Index = En.index();
4754 const ReductionInfo &RI = En.value();
4755 Value *LHSFixupPtr, *RHSFixupPtr;
4756 Builder.restoreIP(RI.ReductionGenClang(
4757 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4758
4759 // Fix the CallBack code genereated to use the correct Values for the LHS
4760 // and RHS
4761 LHSFixupPtr->replaceUsesWithIf(
4762 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4763 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4764 ReductionFunc;
4765 });
4766 RHSFixupPtr->replaceUsesWithIf(
4767 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4768 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4769 ReductionFunc;
4770 });
4771 }
4772
4773 Builder.CreateRetVoid();
4774 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4775 // to the entry block (this is dones for higher opt levels by later passes in
4776 // the pipeline). This has caused issues because non-entry `alloca`s force the
4777 // function to use dynamic stack allocations and we might run out of scratch
4778 // memory.
4779 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4780
4781 return ReductionFunc;
4782}
4783
4784static void
4786 bool IsGPU) {
4787 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4788 (void)RI;
4789 assert(RI.Variable && "expected non-null variable");
4790 assert(RI.PrivateVariable && "expected non-null private variable");
4791 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4792 "expected non-null reduction generator callback");
4793 if (!IsGPU) {
4794 assert(
4795 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4796 "expected variables and their private equivalents to have the same "
4797 "type");
4798 }
4799 assert(RI.Variable->getType()->isPointerTy() &&
4800 "expected variables to be pointers");
4801 }
4802}
4803
4804// The atomic cross-team reduction fast path applies when every reduction in the
4805// set can be represented by an atomicrmw. Clang only populates it for scalar
4806// reductions with a supported atomic operator.
4809 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4810 return static_cast<bool>(RI.AtomicReductionGen);
4811 });
4812}
4813
4815 const LocationDescription &Loc, InsertPointTy AllocaIP,
4816 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4817 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4818 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4819 Value *SrcLocInfo) {
4820 if (!updateToLocation(Loc))
4821 return InsertPointTy();
4822 Builder.restoreIP(CodeGenIP);
4823 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4824 LLVMContext &Ctx = M.getContext();
4825
4826 // Source location for the ident struct
4827 if (!SrcLocInfo) {
4828 uint32_t SrcLocStrSize;
4829 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4830 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4831 }
4832
4833 if (ReductionInfos.size() == 0)
4834 return Builder.saveIP();
4835
4836 BasicBlock *ContinuationBlock = nullptr;
4838 // Copied code from createReductions
4839 BasicBlock *InsertBlock = Loc.IP.getBlock();
4840 ContinuationBlock =
4841 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4842 InsertBlock->getTerminator()->eraseFromParent();
4843 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4844 }
4845
4846 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4847 AttributeList FuncAttrs;
4848 AttrBuilder AttrBldr(Ctx);
4849 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4850 AttrBldr.addAttribute(Attr);
4851 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4852 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4853
4854 CodeGenIP = Builder.saveIP();
4855 Expected<Function *> ReductionResult = createReductionFunction(
4856 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4857 ReductionGenCBKind, FuncAttrs);
4858 if (!ReductionResult)
4859 return ReductionResult.takeError();
4860 Function *ReductionFunc = *ReductionResult;
4861 Builder.restoreIP(CodeGenIP);
4862
4863 // Set the grid value in the config needed for lowering later on
4864 if (GridValue.has_value())
4865 Config.setGridValue(GridValue.value());
4866 else
4867 Config.setGridValue(getGridValue(T, ReductionFunc));
4868
4869 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4870 // RedList, shuffle_reduce_func, interwarp_copy_func);
4871 // or
4872 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4873 Value *Res;
4874
4875 // 1. Build a list of reduction variables.
4876 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4877 auto Size = ReductionInfos.size();
4878 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4879 Type *FuncPtrTy =
4880 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4881 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4882 CodeGenIP = Builder.saveIP();
4883 Builder.restoreIP(AllocaIP);
4884 Value *ReductionListAlloca =
4885 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4886 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4887 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4888 Builder.restoreIP(CodeGenIP);
4889 Type *IndexTy = Builder.getIndexTy(
4890 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4891 for (auto En : enumerate(ReductionInfos)) {
4892 const ReductionInfo &RI = En.value();
4893 Value *ElemPtr = Builder.CreateInBoundsGEP(
4894 RedArrayTy, ReductionList,
4895 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4896
4897 Value *PrivateVar = RI.PrivateVariable;
4898 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4899 if (IsByRefElem)
4900 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4901
4902 Value *CastElem =
4903 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4904 Builder.CreateStore(CastElem, ElemPtr);
4905 }
4906 CodeGenIP = Builder.saveIP();
4907 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4908 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4909
4910 if (!SarFunc)
4911 return SarFunc.takeError();
4912
4913 Expected<Function *> CopyResult =
4914 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4915 if (!CopyResult)
4916 return CopyResult.takeError();
4917 Function *WcFunc = *CopyResult;
4918 Builder.restoreIP(CodeGenIP);
4919
4920 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4921
4922 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4923 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4924 // not currently use it. It is computed here conservatively as max(element
4925 // sizes) * N rather than the exact sum, which over-calculates the size for
4926 // mixed reduction types but is harmless given the argument is unused.
4927 // TODO: Consider dropping this computation if the runtime API is ever revised
4928 // to remove the unused parameter.
4929 unsigned MaxDataSize = 0;
4930 SmallVector<Type *> ReductionTypeArgs;
4931 for (auto En : enumerate(ReductionInfos)) {
4932 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4933 // the actual data size stored in the global reduction buffer, consistent
4934 // with the ReductionsBufferTy struct used for GEP offsets below.
4935 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4936 ? En.value().ByRefElementType
4937 : En.value().ElementType;
4938 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4939 if (Size > MaxDataSize)
4940 MaxDataSize = Size;
4941 ReductionTypeArgs.emplace_back(RedTypeArg);
4942 }
4943 Value *ReductionDataSize =
4944 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4945
4946 // Helper function to copy thread-local data back to the original reduction
4947 // list.
4948 Function *CopyScratchToListFunc = nullptr;
4949 // Thread-local storage for the reduction variables.
4950 Value *ScratchForCopyBack = nullptr;
4951 // RL pointer to which the final value from the per-thread scratch should be
4952 // copied back. (Basically RL, appropriately casted if necessary.)
4953 Value *RLForCopyBack = RL;
4954
4955 bool IsAtomicReduction =
4956 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4957
4958 if (!IsTeamsReduction) {
4959 Value *SarFuncCast =
4960 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4961 Value *WcFuncCast =
4962 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4963 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4964 WcFuncCast};
4966 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4967 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4968 } else if (IsAtomicReduction) {
4969 // Atomic cross-team reduction fast path: determine the team's main thread
4970 // that is later to fold its value atomically into the mapped variable.
4971 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4972 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4973 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4974 } else {
4975 CodeGenIP = Builder.saveIP();
4976 StructType *ReductionsBufferTy = StructType::create(
4977 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4978
4979 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4980 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4981 if (!LtGCFunc)
4982 return LtGCFunc.takeError();
4983
4984 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4985 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4986 if (!GtLCFunc)
4987 return GtLCFunc.takeError();
4988
4989 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4990 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4991 if (!GtLRFunc)
4992 return GtLRFunc.takeError();
4993
4994 Builder.restoreIP(CodeGenIP);
4995
4996 // The runtime's cross-team final aggregate uses the storage pointed at by
4997 // its reduce-list argument as per-thread scratch. When the surrounding
4998 // kernel is already in SPMD execution mode, clang emitted each reduction
4999 // private as a per-thread `alloca addrspace(5)`, so the original red_list
5000 // (RL) is already per-thread and nothing else is needed.
5001 //
5002 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
5003 // Generic-mode globalization put the reduction private into team-shared
5004 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
5005 // point all threads of the last team would race on the shared LDS slot.
5006 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
5007 // value in, and hand the per-thread RL to the runtime instead. The writer
5008 // thread copies the final value from that per-thread scratch back to RL
5009 // before running the existing combine path below.
5010
5011 // Thread-local RL (might need localization below before being passed to the
5012 // runtime).
5013 Value *RuntimeRL = RL;
5014
5015 if (!IsSPMD) {
5016 CodeGenIP = Builder.saveIP();
5017 Builder.restoreIP(AllocaIP);
5018 // Allocate thread-local buffer for the reduction variables.
5019 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
5020 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
5021 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5022 PerThreadScratchAlloca, PtrTy,
5023 PerThreadScratchAlloca->getName() + ".ascast");
5024 // Allocate thread-local buffer for the pointers to the reduction
5025 // variables.
5026 Value *PerThreadRedListAlloca =
5027 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5028 ".omp.reduction.per_thread_red_list");
5029 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5030 PerThreadRedListAlloca, PtrTy,
5031 PerThreadRedListAlloca->getName() + ".ascast");
5032 Builder.restoreIP(CodeGenIP);
5033
5034 // Iterate over the reduction variables and copy the team-local value to
5035 // the thread-local buffer.
5036 for (auto En : enumerate(ReductionInfos)) {
5037 const ReductionInfo &RI = En.value();
5038 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5039
5040 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5041 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5042 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5043 0, En.index());
5044
5045 Value *RuntimeListEntry = FieldPtr;
5046 if (IsByRefElem && RI.DataPtrPtrGen) {
5047 Value *SrcDescriptor =
5048 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5049 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5050 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5051 if (!Descriptor)
5052 return Descriptor.takeError();
5053 RuntimeListEntry = *Descriptor;
5054 }
5055 Builder.CreateStore(RuntimeListEntry, Slot);
5056 }
5057 // The copy helpers were emitted with default-AS (AS 0) pointer params
5058 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5059 // but PerThreadScratch and RL live in the target's default AS, which
5060 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5061 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5062 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5063 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5064 PerThreadScratch, CopyArg0Ty);
5065 RLForCopyBack =
5066 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5067 // Use index 0 because there is no array of target values to index into,
5068 // there is only one thread-local memory slot.
5069 // restoreIP above left a stale/empty debug location; this inlinable call
5070 // to a debug-info-bearing helper needs one or the verifier rejects the
5071 // module ("!dbg attachment points at wrong subprogram") after inlining.
5072 Builder.SetCurrentDebugLocation(Loc.DL);
5073 Builder.CreateCall(
5074 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5075 CopyScratchToListFunc = *GtLCFunc;
5076 }
5077
5078 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5079 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5080
5081 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5082 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5083 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5084 }
5085
5086 // 5. Build if (res == 1)
5087 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5088 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5089 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5090 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5091
5092 // 6. Build then branch: where we have reduced values in the master
5093 // thread in each team.
5094 // __kmpc_end_reduce{_nowait}(<gtid>);
5095 // break;
5096 emitBlock(ThenBB, CurFunc);
5097
5098 // Copy the writer thread's per-thread scratch result back into the original
5099 // red-list storage before the existing combine path reads RI.PrivateVariable.
5100 // Set a debug location: this inlinable call to a debug-info-bearing helper
5101 // needs one or the verifier rejects the module after inlining.
5102 if (ScratchForCopyBack) {
5103 Builder.SetCurrentDebugLocation(Loc.DL);
5104 Builder.CreateCall(
5105 CopyScratchToListFunc,
5106 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5107 }
5108
5109 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5110 for (auto En : enumerate(ReductionInfos)) {
5111 const ReductionInfo &RI = En.value();
5112
5113 // Atomic cross-team fast path: each team's main thread folds its
5114 // team-reduced value directly into the mapped reduction variable with a
5115 // single atomicrmw.
5116 if (IsAtomicReduction) {
5118 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5119 if (!AfterIP)
5120 return AfterIP.takeError();
5121 Builder.restoreIP(*AfterIP);
5122 continue;
5123 }
5124
5126 Value *RedValue = RI.Variable;
5127
5128 Value *RHS =
5129 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5130
5132 Value *LHSPtr, *RHSPtr;
5133 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5134 &LHSPtr, &RHSPtr, CurFunc));
5135
5136 // Fix the CallBack code genereated to use the correct Values for the LHS
5137 // and RHS. Cast to match types before replacing (necessary to handle
5138 // different address spaces).
5139 if (LHSPtr->getType() != RedValue->getType())
5140 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5141 RedValue, LHSPtr->getType());
5142 if (RHSPtr->getType() != RHS->getType())
5143 RHS =
5144 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5145
5146 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5147 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5148 ReductionFunc;
5149 });
5150 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5151 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5152 ReductionFunc;
5153 });
5154 } else {
5155 if (IsByRef.empty() || !IsByRef[En.index()]) {
5156 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5157 "red.value." + Twine(En.index()));
5158 }
5159 Value *PrivateRedValue = Builder.CreateLoad(
5160 ValueType, RHS, "red.private.value" + Twine(En.index()));
5161 Value *Reduced;
5162 InsertPointOrErrorTy AfterIP =
5163 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5164 if (!AfterIP)
5165 return AfterIP.takeError();
5166 Builder.restoreIP(*AfterIP);
5167
5168 if (!IsByRef.empty() && !IsByRef[En.index()])
5169 Builder.CreateStore(Reduced, RI.Variable);
5170 }
5171 }
5172 emitBlock(ExitBB, CurFunc);
5173 if (ContinuationBlock) {
5174 Builder.CreateBr(ContinuationBlock);
5175 Builder.SetInsertPoint(ContinuationBlock);
5176 }
5177 Config.setEmitLLVMUsed();
5178
5179 return Builder.saveIP();
5180}
5181
5183 Type *VoidTy = Type::getVoidTy(M.getContext());
5184 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5185 auto *FuncTy =
5186 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5188 ".omp.reduction.func", &M);
5189}
5190
5192 Function *ReductionFunc,
5194 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5195 IRBuilder<>::InsertPointGuard IPG(Builder);
5196 Module *Module = ReductionFunc->getParent();
5197 BasicBlock *ReductionFuncBlock =
5198 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5199 Builder.SetInsertPoint(ReductionFuncBlock);
5200 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5201 Value *LHSArrayPtr = nullptr;
5202 Value *RHSArrayPtr = nullptr;
5203 if (IsGPU) {
5204 // Need to alloca memory here and deal with the pointers before getting
5205 // LHS/RHS pointers out
5206 //
5207 Argument *Arg0 = ReductionFunc->getArg(0);
5208 Argument *Arg1 = ReductionFunc->getArg(1);
5209 Type *Arg0Type = Arg0->getType();
5210 Type *Arg1Type = Arg1->getType();
5211
5212 Value *LHSAlloca =
5213 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5214 Value *RHSAlloca =
5215 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5216 Value *LHSAddrCast =
5217 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5218 Value *RHSAddrCast =
5219 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5220 Builder.CreateStore(Arg0, LHSAddrCast);
5221 Builder.CreateStore(Arg1, RHSAddrCast);
5222 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5223 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5224 } else {
5225 LHSArrayPtr = ReductionFunc->getArg(0);
5226 RHSArrayPtr = ReductionFunc->getArg(1);
5227 }
5228
5229 unsigned NumReductions = ReductionInfos.size();
5230 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5231
5232 for (auto En : enumerate(ReductionInfos)) {
5233 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5234 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5235 RedArrayTy, LHSArrayPtr, 0, En.index());
5236 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5237 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5238 LHSI8Ptr, RI.Variable->getType());
5239 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5240 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5241 RedArrayTy, RHSArrayPtr, 0, En.index());
5242 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5243 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5244 RHSI8Ptr, RI.PrivateVariable->getType());
5245 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5246 Value *Reduced;
5248 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5249 if (!AfterIP)
5250 return AfterIP.takeError();
5251
5252 Builder.restoreIP(*AfterIP);
5253 // TODO: Consider flagging an error.
5254 if (!Builder.GetInsertBlock())
5255 return Error::success();
5256
5257 // store is inside of the reduction region when using by-ref
5258 if (!IsByRef[En.index()])
5259 Builder.CreateStore(Reduced, LHSPtr);
5260 }
5261 Builder.CreateRetVoid();
5262 return Error::success();
5263}
5264
5266 const LocationDescription &Loc, InsertPointTy AllocaIP,
5267 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5268 bool IsNoWait, bool IsTeamsReduction) {
5269 assert(ReductionInfos.size() == IsByRef.size());
5270 if (Config.isGPU())
5271 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5272 IsByRef, IsNoWait, IsTeamsReduction);
5273
5274 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5275
5276 if (!updateToLocation(Loc))
5277 return InsertPointTy();
5278
5279 if (ReductionInfos.size() == 0)
5280 return Builder.saveIP();
5281
5282 BasicBlock *InsertBlock = Loc.IP.getBlock();
5283 BasicBlock *ContinuationBlock =
5284 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5285 InsertBlock->getTerminator()->eraseFromParent();
5286
5287 // Create and populate array of type-erased pointers to private reduction
5288 // values.
5289 unsigned NumReductions = ReductionInfos.size();
5290 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5291 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5292 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5293
5294 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5295 // Emitting the alloca moved the insertion point into the alloca block and
5296 // can clear the debug loc. Restore back to Loc.DL.
5297 Builder.SetCurrentDebugLocation(Loc.DL);
5298
5299 for (auto En : enumerate(ReductionInfos)) {
5300 unsigned Index = En.index();
5301 const ReductionInfo &RI = En.value();
5302 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5303 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5304 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5305 }
5306
5307 // Emit a call to the runtime function that orchestrates the reduction.
5308 // Declare the reduction function in the process.
5309 Type *IndexTy = Builder.getIndexTy(
5310 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5311 Function *Func = Builder.GetInsertBlock()->getParent();
5312 Module *Module = Func->getParent();
5313 uint32_t SrcLocStrSize;
5314 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5315 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5316 return RI.AtomicReductionGen;
5317 });
5318 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5319 CanGenerateAtomic
5320 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5321 : IdentFlag(0));
5322 Value *ThreadId = getOrCreateThreadID(Ident);
5323 Constant *NumVariables = Builder.getInt32(NumReductions);
5324 const DataLayout &DL = Module->getDataLayout();
5325 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5326 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5327 Function *ReductionFunc = getFreshReductionFunc(*Module);
5328 Value *Lock = getOMPCriticalRegionLock(".reduction");
5330 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5331 : RuntimeFunction::OMPRTL___kmpc_reduce);
5332 CallInst *ReduceCall =
5333 createRuntimeFunctionCall(ReduceFunc,
5334 {Ident, ThreadId, NumVariables, RedArraySize,
5335 RedArray, ReductionFunc, Lock},
5336 "reduce");
5337
5338 // Create final reduction entry blocks for the atomic and non-atomic case.
5339 // Emit IR that dispatches control flow to one of the blocks based on the
5340 // reduction supporting the atomic mode.
5341 BasicBlock *NonAtomicRedBlock =
5342 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5343 BasicBlock *AtomicRedBlock =
5344 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5345 SwitchInst *Switch =
5346 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5347 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5348 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5349
5350 // Populate the non-atomic reduction using the elementwise reduction function.
5351 // This loads the elements from the global and private variables and reduces
5352 // them before storing back the result to the global variable.
5353 Builder.SetInsertPoint(NonAtomicRedBlock);
5354 for (auto En : enumerate(ReductionInfos)) {
5355 const ReductionInfo &RI = En.value();
5357 // We have one less load for by-ref case because that load is now inside of
5358 // the reduction region
5359 Value *RedValue = RI.Variable;
5360 if (!IsByRef[En.index()]) {
5361 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5362 "red.value." + Twine(En.index()));
5363 }
5364 Value *PrivateRedValue =
5365 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5366 "red.private.value." + Twine(En.index()));
5367 Value *Reduced;
5368 InsertPointOrErrorTy AfterIP =
5369 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5370 if (!AfterIP)
5371 return AfterIP.takeError();
5372 Builder.restoreIP(*AfterIP);
5373
5374 if (!Builder.GetInsertBlock())
5375 return InsertPointTy();
5376 // for by-ref case, the load is inside of the reduction region
5377 if (!IsByRef[En.index()])
5378 Builder.CreateStore(Reduced, RI.Variable);
5379 }
5380 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5381 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5382 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5383 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5384 Builder.CreateBr(ContinuationBlock);
5385
5386 // Populate the atomic reduction using the atomic elementwise reduction
5387 // function. There are no loads/stores here because they will be happening
5388 // inside the atomic elementwise reduction.
5389 Builder.SetInsertPoint(AtomicRedBlock);
5390 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5391 for (const ReductionInfo &RI : ReductionInfos) {
5393 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5394 if (!AfterIP)
5395 return AfterIP.takeError();
5396 Builder.restoreIP(*AfterIP);
5397 if (!Builder.GetInsertBlock())
5398 return InsertPointTy();
5399 }
5400 Builder.CreateBr(ContinuationBlock);
5401 } else {
5402 Builder.CreateUnreachable();
5403 }
5404
5405 // Populate the outlined reduction function using the elementwise reduction
5406 // function. Partial values are extracted from the type-erased array of
5407 // pointers to private variables.
5408 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5409 IsByRef, /*isGPU=*/false);
5410 if (Err)
5411 return Err;
5412
5413 if (!Builder.GetInsertBlock())
5414 return InsertPointTy();
5415
5416 Builder.SetInsertPoint(ContinuationBlock);
5417 return Builder.saveIP();
5418}
5419
5422 BodyGenCallbackTy BodyGenCB,
5423 FinalizeCallbackTy FiniCB) {
5424 if (!updateToLocation(Loc))
5425 return Loc.IP;
5426
5427 Directive OMPD = Directive::OMPD_master;
5428 uint32_t SrcLocStrSize;
5429 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5430 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5431 Value *ThreadId = getOrCreateThreadID(Ident);
5432 Value *Args[] = {Ident, ThreadId};
5433
5434 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5435 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5436
5437 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5438 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5439
5440 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5441 /*Conditional*/ true, /*hasFinalize*/ true);
5442}
5443
5446 BodyGenCallbackTy BodyGenCB,
5447 FinalizeCallbackTy FiniCB, Value *Filter) {
5449 if (!updateToLocation(Loc))
5450 return Loc.IP;
5451
5452 Directive OMPD = Directive::OMPD_masked;
5453 uint32_t SrcLocStrSize;
5454 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5455 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5456 Value *ThreadId = getOrCreateThreadID(Ident);
5457 Value *Args[] = {Ident, ThreadId, Filter};
5458 Value *ArgsEnd[] = {Ident, ThreadId};
5459
5460 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5461 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5462
5463 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5464 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5465
5466 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5467 /*Conditional*/ true, /*hasFinalize*/ true);
5468}
5469
5471 llvm::FunctionCallee Callee,
5473 const llvm::Twine &Name) {
5474 llvm::CallInst *Call = Builder.CreateCall(
5475 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5476 Call->setDoesNotThrow();
5477 return Call;
5478}
5479
5480// Expects input basic block is dominated by BeforeScanBB.
5481// Once Scan directive is encountered, the code after scan directive should be
5482// dominated by AfterScanBB. Scan directive splits the code sequence to
5483// scan and input phase. Based on whether inclusive or exclusive
5484// clause is used in the scan directive and whether input loop or scan loop
5485// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5486// input loop and second is the scan loop. The code generated handles only
5487// inclusive scans now.
5489 const LocationDescription &Loc, InsertPointTy AllocaIP,
5490 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5491 bool IsInclusive, ScanInfo *ScanRedInfo) {
5492 if (ScanRedInfo->OMPFirstScanLoop) {
5493 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5494 ScanVarsType, ScanRedInfo);
5495 if (Err)
5496 return Err;
5497 }
5498 if (!updateToLocation(Loc))
5499 return Loc.IP;
5500
5501 llvm::Value *IV = ScanRedInfo->IV;
5502
5503 if (ScanRedInfo->OMPFirstScanLoop) {
5504 // Emit buffer[i] = red; at the end of the input phase.
5505 for (size_t i = 0; i < ScanVars.size(); i++) {
5506 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5507 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5508 Type *DestTy = ScanVarsType[i];
5509 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5510 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5511
5512 Builder.CreateStore(Src, Val);
5513 }
5514 }
5515 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5516 emitBlock(ScanRedInfo->OMPScanDispatch,
5517 Builder.GetInsertBlock()->getParent());
5518
5519 if (!ScanRedInfo->OMPFirstScanLoop) {
5520 IV = ScanRedInfo->IV;
5521 // Emit red = buffer[i]; at the entrance to the scan phase.
5522 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5523 for (size_t i = 0; i < ScanVars.size(); i++) {
5524 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5525 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5526 Type *DestTy = ScanVarsType[i];
5527 Value *SrcPtr =
5528 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5529 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5530 Builder.CreateStore(Src, ScanVars[i]);
5531 }
5532 }
5533
5534 // TODO: Update it to CreateBr and remove dead blocks
5535 llvm::Value *CmpI = Builder.getInt1(true);
5536 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5537 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5538 ScanRedInfo->OMPAfterScanBlock);
5539 } else {
5540 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5541 ScanRedInfo->OMPBeforeScanBlock);
5542 }
5543 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5544 Builder.GetInsertBlock()->getParent());
5545 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5546 return Builder.saveIP();
5547}
5548
5549Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5550 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5551 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5552
5553 Builder.restoreIP(AllocaIP);
5554 // Create the shared pointer at alloca IP.
5555 for (size_t i = 0; i < ScanVars.size(); i++) {
5556 llvm::Value *BuffPtr =
5557 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5558 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5559 }
5560
5561 // Allocate temporary buffer by master thread
5562 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5563 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5564 Builder.restoreIP(CodeGenIP);
5565 Value *AllocSpan =
5566 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5567 for (size_t i = 0; i < ScanVars.size(); i++) {
5568 Type *IntPtrTy = Builder.getInt32Ty();
5569 Value *Allocsize = Builder.CreateTypeSize(
5570 IntPtrTy, M.getDataLayout().getTypeAllocSize(ScanVarsType[i]));
5571 Value *Buff =
5572 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5573 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5574 }
5575 return Error::success();
5576 };
5577 // TODO: Perform finalization actions for variables. This has to be
5578 // called for variables which have destructors/finalizers.
5579 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5580
5581 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5582 llvm::Value *FilterVal = Builder.getInt32(0);
5584 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5585
5586 if (!AfterIP)
5587 return AfterIP.takeError();
5588 Builder.restoreIP(*AfterIP);
5589 BasicBlock *InputBB = Builder.GetInsertBlock();
5590 if (InputBB->hasTerminator())
5591 Builder.SetInsertPoint(InputBB->getTerminator());
5592 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5593 if (!AfterIP)
5594 return AfterIP.takeError();
5595 Builder.restoreIP(*AfterIP);
5596
5597 return Error::success();
5598}
5599
5600Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5601 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5602 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5603 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5604 Builder.restoreIP(CodeGenIP);
5605 for (ReductionInfo RedInfo : ReductionInfos) {
5606 Value *PrivateVar = RedInfo.PrivateVariable;
5607 Value *OrigVar = RedInfo.Variable;
5608 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5609 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5610
5611 Type *SrcTy = RedInfo.ElementType;
5612 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5613 "arrayOffset");
5614 Value *Src = Builder.CreateLoad(SrcTy, Val);
5615
5616 Builder.CreateStore(Src, OrigVar);
5617 Builder.CreateFree(Buff);
5618 }
5619 return Error::success();
5620 };
5621 // TODO: Perform finalization actions for variables. This has to be
5622 // called for variables which have destructors/finalizers.
5623 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5624
5625 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5626 Builder.SetInsertPoint(TI);
5627 else
5628 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5629
5630 llvm::Value *FilterVal = Builder.getInt32(0);
5632 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5633
5634 if (!AfterIP)
5635 return AfterIP.takeError();
5636 Builder.restoreIP(*AfterIP);
5637 BasicBlock *InputBB = Builder.GetInsertBlock();
5638 if (InputBB->hasTerminator())
5639 Builder.SetInsertPoint(InputBB->getTerminator());
5640 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5641 if (!AfterIP)
5642 return AfterIP.takeError();
5643 Builder.restoreIP(*AfterIP);
5644 return Error::success();
5645}
5646
5648 const LocationDescription &Loc,
5650 ScanInfo *ScanRedInfo) {
5651
5652 if (!updateToLocation(Loc))
5653 return Loc.IP;
5654 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5655 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5656 Builder.restoreIP(CodeGenIP);
5657 Function *CurFn = Builder.GetInsertBlock()->getParent();
5658 // for (int k = 0; k <= ceil(log2(n)); ++k)
5659 llvm::BasicBlock *LoopBB =
5660 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5661 llvm::BasicBlock *ExitBB =
5662 splitBB(Builder, false, "omp.outer.log.scan.exit");
5664 Builder.GetInsertBlock()->getModule(),
5665 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5666 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5667 llvm::Value *Arg =
5668 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5669 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5671 Builder.GetInsertBlock()->getModule(),
5672 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5673 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5674 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5675 llvm::Value *NMin1 = Builder.CreateNUWSub(
5676 ScanRedInfo->Span,
5677 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5678 Builder.SetInsertPoint(InputBB);
5679 Builder.CreateBr(LoopBB);
5680 emitBlock(LoopBB, CurFn);
5681 Builder.SetInsertPoint(LoopBB);
5682
5683 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5684 // size pow2k = 1;
5685 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5686 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5687 InputBB);
5688 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5689 InputBB);
5690 // for (size i = n - 1; i >= 2 ^ k; --i)
5691 // tmp[i] op= tmp[i-pow2k];
5692 llvm::BasicBlock *InnerLoopBB =
5693 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5694 llvm::BasicBlock *InnerExitBB =
5695 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5696 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5697 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5698 emitBlock(InnerLoopBB, CurFn);
5699 Builder.SetInsertPoint(InnerLoopBB);
5700 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5701 IVal->addIncoming(NMin1, LoopBB);
5702 for (ReductionInfo RedInfo : ReductionInfos) {
5703 Value *ReductionVal = RedInfo.PrivateVariable;
5704 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5705 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5706 Type *DestTy = RedInfo.ElementType;
5707 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5708 Value *LHSPtr =
5709 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5710 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5711 Value *RHSPtr =
5712 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5713 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5714 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5715 llvm::Value *Result;
5716 InsertPointOrErrorTy AfterIP =
5717 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5718 if (!AfterIP)
5719 return AfterIP.takeError();
5720 Builder.CreateStore(Result, LHSPtr);
5721 }
5722 llvm::Value *NextIVal = Builder.CreateNUWSub(
5723 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5724 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5725 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5726 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5727 emitBlock(InnerExitBB, CurFn);
5728 llvm::Value *Next = Builder.CreateNUWAdd(
5729 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5730 Counter->addIncoming(Next, Builder.GetInsertBlock());
5731 // pow2k <<= 1;
5732 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5733 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5734 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5735 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5736 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5737 return Error::success();
5738 };
5739
5740 // TODO: Perform finalization actions for variables. This has to be
5741 // called for variables which have destructors/finalizers.
5742 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5743
5744 llvm::Value *FilterVal = Builder.getInt32(0);
5746 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5747
5748 if (!AfterIP)
5749 return AfterIP.takeError();
5750 Builder.restoreIP(*AfterIP);
5751 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5752
5753 if (!AfterIP)
5754 return AfterIP.takeError();
5755 Builder.restoreIP(*AfterIP);
5756 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5757 if (Err)
5758 return Err;
5759
5760 return AfterIP;
5761}
5762
5763Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5764 llvm::function_ref<Error()> InputLoopGen,
5765 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5766 ScanInfo *ScanRedInfo) {
5767
5768 {
5769 // Emit loop with input phase:
5770 // for (i: 0..<num_iters>) {
5771 // <input phase>;
5772 // buffer[i] = red;
5773 // }
5774 ScanRedInfo->OMPFirstScanLoop = true;
5775 Error Err = InputLoopGen();
5776 if (Err)
5777 return Err;
5778 }
5779 {
5780 // Emit loop with scan phase:
5781 // for (i: 0..<num_iters>) {
5782 // red = buffer[i];
5783 // <scan phase>;
5784 // }
5785 ScanRedInfo->OMPFirstScanLoop = false;
5786 Error Err = ScanLoopGen(Builder);
5787 if (Err)
5788 return Err;
5789 }
5790 return Error::success();
5791}
5792
5793void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5794 Function *Fun = Builder.GetInsertBlock()->getParent();
5795 ScanRedInfo->OMPScanDispatch =
5796 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5797 ScanRedInfo->OMPAfterScanBlock =
5798 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5799 ScanRedInfo->OMPBeforeScanBlock =
5800 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5801 ScanRedInfo->OMPScanLoopExit =
5802 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5803}
5805 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5806 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5807 Module *M = F->getParent();
5808 LLVMContext &Ctx = M->getContext();
5809 Type *IndVarTy = TripCount->getType();
5810
5811 // Create the basic block structure.
5812 BasicBlock *Preheader =
5813 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5814 BasicBlock *Header =
5815 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5816 BasicBlock *Cond =
5817 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5818 BasicBlock *Body =
5819 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5820 BasicBlock *Latch =
5821 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5822 BasicBlock *Exit =
5823 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5824 BasicBlock *After =
5825 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5826
5827 // Use specified DebugLoc for new instructions.
5828 Builder.SetCurrentDebugLocation(DL);
5829
5830 Builder.SetInsertPoint(Preheader);
5831 Builder.CreateBr(Header);
5832
5833 Builder.SetInsertPoint(Header);
5834 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5835 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5836 Builder.CreateBr(Cond);
5837
5838 Builder.SetInsertPoint(Cond);
5839 Value *Cmp =
5840 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5841 Builder.CreateCondBr(Cmp, Body, Exit);
5842
5843 Builder.SetInsertPoint(Body);
5844 Builder.CreateBr(Latch);
5845
5846 Builder.SetInsertPoint(Latch);
5847 // Decide whether the induction variable increment can carry nsw.
5848 //
5849 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5850 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5851 // for valid programs 0 <= count <= INT_MAX always holds.
5852 //
5853 // Collapsed loops: the trip count is a product that can overflow i32 even for
5854 // a conforming program, so nsw is kept only when the product is a constant
5855 // that provably fits, dropped otherwise.
5856 bool HasNSW = Config.hasNoSignedWrap();
5857 if (HasNSW) {
5858 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5859 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5861 if (CI->getValue().ugt(SignedMax))
5862 HasNSW = false;
5863 } else if (IsCollapsed) {
5864 HasNSW = false;
5865 }
5866 }
5867 Value *Next =
5868 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5869 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5870 Builder.CreateBr(Header);
5871 IndVarPHI->addIncoming(Next, Latch);
5872
5873 Builder.SetInsertPoint(Exit);
5874 Builder.CreateBr(After);
5875
5876 // Remember and return the canonical control flow.
5877 LoopInfos.emplace_front();
5878 CanonicalLoopInfo *CL = &LoopInfos.front();
5879
5880 CL->Header = Header;
5881 CL->Cond = Cond;
5882 CL->Latch = Latch;
5883 CL->Exit = Exit;
5884
5885#ifndef NDEBUG
5886 CL->assertOK();
5887#endif
5888 return CL;
5889}
5890
5893 LoopBodyGenCallbackTy BodyGenCB,
5894 Value *TripCount, const Twine &Name) {
5895 BasicBlock *BB = Loc.IP.getBlock();
5896 BasicBlock *NextBB = BB->getNextNode();
5897
5898 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5899 NextBB, NextBB, Name);
5900 BasicBlock *After = CL->getAfter();
5901
5902 // If location is not set, don't connect the loop.
5903 if (updateToLocation(Loc)) {
5904 // Split the loop at the insertion point: Branch to the preheader and move
5905 // every following instruction to after the loop (the After BB). Also, the
5906 // new successor is the loop's after block.
5907 spliceBB(Builder, After, /*CreateBranch=*/false);
5908 Builder.CreateBr(CL->getPreheader());
5909 }
5910
5911 // Emit the body content. We do it after connecting the loop to the CFG to
5912 // avoid that the callback encounters degenerate BBs.
5913 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5914 return Err;
5915
5916#ifndef NDEBUG
5917 CL->assertOK();
5918#endif
5919 return CL;
5920}
5921
5923 ScanInfos.emplace_front();
5924 ScanInfo *Result = &ScanInfos.front();
5925 return Result;
5926}
5927
5931 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5932 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5933 LocationDescription ComputeLoc =
5934 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5935 updateToLocation(ComputeLoc);
5936
5938
5940 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5941 ScanRedInfo->Span = TripCount;
5942 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5943 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5944
5945 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5946 Builder.restoreIP(CodeGenIP);
5947 ScanRedInfo->IV = IV;
5948 createScanBBs(ScanRedInfo);
5949 BasicBlock *InputBlock = Builder.GetInsertBlock();
5950 Instruction *Terminator = InputBlock->getTerminator();
5951 assert(Terminator->getNumSuccessors() == 1);
5952 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5953 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5954 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5955 Builder.GetInsertBlock()->getParent());
5956 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5957 emitBlock(ScanRedInfo->OMPScanLoopExit,
5958 Builder.GetInsertBlock()->getParent());
5959 Builder.CreateBr(ContinueBlock);
5960 Builder.SetInsertPoint(
5961 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5962 return BodyGenCB(Builder.saveIP(), IV);
5963 };
5964
5965 const auto &&InputLoopGen = [&]() -> Error {
5967 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5968 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5969 if (!LoopInfo)
5970 return LoopInfo.takeError();
5971 Result.push_back(*LoopInfo);
5972 Builder.restoreIP((*LoopInfo)->getAfterIP());
5973 return Error::success();
5974 };
5975 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5977 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5978 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5979 if (!LoopInfo)
5980 return LoopInfo.takeError();
5981 Result.push_back(*LoopInfo);
5982 Builder.restoreIP((*LoopInfo)->getAfterIP());
5983 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5984 return Error::success();
5985 };
5986 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5987 if (Err)
5988 return Err;
5989 return Result;
5990}
5991
5993 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5994 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5995
5996 // Consider the following difficulties (assuming 8-bit signed integers):
5997 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5998 // DO I = 1, 100, 50
5999 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
6000 // DO I = 100, 0, -128
6001
6002 // Start, Stop and Step must be of the same integer type.
6003 auto *IndVarTy = cast<IntegerType>(Start->getType());
6004 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
6005 assert(IndVarTy == Step->getType() && "Step type mismatch");
6006
6008
6009 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
6010 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
6011
6012 // Like Step, but always positive.
6013 Value *Incr = Step;
6014
6015 // Distance between Start and Stop; always positive.
6016 Value *Span;
6017
6018 // Condition whether there are no iterations are executed at all, e.g. because
6019 // UB < LB.
6020 Value *ZeroCmp;
6021
6022 if (IsSigned) {
6023 // Ensure that increment is positive. If not, negate and invert LB and UB.
6024 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6025 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6026 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6027 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6028 Span = Builder.CreateSub(UB, LB, "", false, true);
6029 ZeroCmp = Builder.CreateICmp(
6030 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6031 } else {
6032 Span = Builder.CreateSub(Stop, Start, "", true);
6033 ZeroCmp = Builder.CreateICmp(
6034 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6035 }
6036
6037 Value *CountIfLooping;
6038 if (InclusiveStop) {
6039 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6040 } else {
6041 // Avoid incrementing past stop since it could overflow.
6042 Value *CountIfTwo = Builder.CreateAdd(
6043 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6044 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6045 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6046 }
6047
6048 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6049 "omp_" + Name + ".tripcount");
6050}
6051
6054 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6055 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6056 ScanInfo *ScanRedInfo) {
6057 LocationDescription ComputeLoc =
6058 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6059
6061 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6062
6063 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6064 Builder.restoreIP(CodeGenIP);
6065 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6066 /*HasNSW=*/Config.hasNoSignedWrap());
6067 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6068 /*HasNSW=*/Config.hasNoSignedWrap());
6069 if (InScan)
6070 ScanRedInfo->IV = IndVar;
6071 return BodyGenCB(Builder.saveIP(), IndVar);
6072 };
6073 LocationDescription LoopLoc =
6074 ComputeIP.isSet()
6075 ? Loc
6076 : LocationDescription(Builder.saveIP(),
6077 Builder.getCurrentDebugLocation());
6078 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6079}
6080
6081// Returns an LLVM function to call for initializing loop bounds using OpenMP
6082// static scheduling for composite `distribute parallel for` depending on
6083// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6084// integers as unsigned similarly to CanonicalLoopInfo.
6085static FunctionCallee
6087 OpenMPIRBuilder &OMPBuilder) {
6088 unsigned Bitwidth = Ty->getIntegerBitWidth();
6089 if (Bitwidth == 32)
6090 return OMPBuilder.getOrCreateRuntimeFunction(
6091 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6092 if (Bitwidth == 64)
6093 return OMPBuilder.getOrCreateRuntimeFunction(
6094 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6095 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6096}
6097
6098// Returns an LLVM function to call for initializing loop bounds using OpenMP
6099// static scheduling depending on `type`. Only i32 and i64 are supported by the
6100// runtime. Always interpret integers as unsigned similarly to
6101// CanonicalLoopInfo.
6103 OpenMPIRBuilder &OMPBuilder) {
6104 unsigned Bitwidth = Ty->getIntegerBitWidth();
6105 if (Bitwidth == 32)
6106 return OMPBuilder.getOrCreateRuntimeFunction(
6107 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6108 if (Bitwidth == 64)
6109 return OMPBuilder.getOrCreateRuntimeFunction(
6110 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6111 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6112}
6113
6114OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6115 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6116 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6117 OMPScheduleType DistScheduleSchedType) {
6118 assert(CLI->isValid() && "Requires a valid canonical loop");
6119 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6120 "Require dedicated allocate IP");
6121
6122 // Set up the source location value for OpenMP runtime.
6123 Builder.restoreIP(CLI->getPreheaderIP());
6124 Builder.SetCurrentDebugLocation(DL);
6125
6126 uint32_t SrcLocStrSize;
6127 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6129 switch (LoopType) {
6130 case WorksharingLoopType::ForStaticLoop:
6131 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6132 break;
6133 case WorksharingLoopType::DistributeStaticLoop:
6134 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6135 break;
6136 case WorksharingLoopType::DistributeForStaticLoop:
6137 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6138 break;
6139 }
6140 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6141
6142 // Declare useful OpenMP runtime functions.
6143 Value *IV = CLI->getIndVar();
6144 Type *IVTy = IV->getType();
6145 FunctionCallee StaticInit =
6146 LoopType == WorksharingLoopType::DistributeForStaticLoop
6147 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6148 : getKmpcForStaticInitForType(IVTy, M, *this);
6149 FunctionCallee StaticFini =
6150 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6151
6152 // Allocate space for computed loop bounds as expected by the "init" function.
6153 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6154
6155 Type *I32Type = Type::getInt32Ty(M.getContext());
6156 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6157 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6158 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6159 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6160 CLI->setLastIter(PLastIter);
6161
6162 // At the end of the preheader, prepare for calling the "init" function by
6163 // storing the current loop bounds into the allocated space. A canonical loop
6164 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6165 // and produces an inclusive upper bound.
6166 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6167 Constant *Zero = ConstantInt::get(IVTy, 0);
6168 Constant *One = ConstantInt::get(IVTy, 1);
6169 Builder.CreateStore(Zero, PLowerBound);
6170 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6171 Builder.CreateStore(UpperBound, PUpperBound);
6172 Builder.CreateStore(One, PStride);
6173
6174 Value *ThreadNum =
6175 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6176
6177 OMPScheduleType SchedType =
6178 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6179 ? OMPScheduleType::OrderedDistribute
6181 Constant *SchedulingType =
6182 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6183
6184 // Call the "init" function and update the trip count of the loop with the
6185 // value it produced.
6186 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6187 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6188 this](Value *SchedulingType, auto &Builder) {
6189 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6190 PLowerBound, PUpperBound});
6191 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6192 Value *PDistUpperBound =
6193 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6194 Args.push_back(PDistUpperBound);
6195 }
6196 Args.append({PStride, One, Zero});
6197 createRuntimeFunctionCall(StaticInit, Args);
6198 };
6199 BuildInitCall(SchedulingType, Builder);
6200 if (HasDistSchedule &&
6201 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6202 Constant *DistScheduleSchedType = ConstantInt::get(
6203 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6204 // We want to emit a second init function call for the dist_schedule clause
6205 // to the Distribute construct. This should only be done however if a
6206 // Workshare Loop is nested within a Distribute Construct
6207 BuildInitCall(DistScheduleSchedType, Builder);
6208 }
6209 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6210 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6211 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6212 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6213 CLI->setTripCount(TripCount);
6214
6215 // Update all uses of the induction variable except the one in the condition
6216 // block that compares it with the actual upper bound, and the increment in
6217 // the latch block.
6218
6219 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6220 Builder.SetInsertPoint(CLI->getBody(),
6221 CLI->getBody()->getFirstInsertionPt());
6222 Builder.SetCurrentDebugLocation(DL);
6223 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6224 /*HasNSW=*/Config.hasNoSignedWrap());
6225 });
6226
6227 // In the "exit" block, call the "fini" function.
6228 Builder.SetInsertPoint(CLI->getExit(),
6229 CLI->getExit()->getTerminator()->getIterator());
6230 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6231
6232 // Add the barrier if requested.
6233 if (NeedsBarrier) {
6234 InsertPointOrErrorTy BarrierIP =
6236 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6237 /* CheckCancelFlag */ false);
6238 if (!BarrierIP)
6239 return BarrierIP.takeError();
6240 }
6241
6242 InsertPointTy AfterIP = CLI->getAfterIP();
6243 CLI->invalidate();
6244
6245 return AfterIP;
6246}
6247
6248static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6249 LoopInfo &LI);
6250static void addLoopMetadata(CanonicalLoopInfo *Loop,
6252
6254 LLVMContext &Ctx, Loop *Loop,
6256 SmallVector<Metadata *> &LoopMDList) {
6257 SmallSet<BasicBlock *, 8> Reachable;
6258
6259 // Get the basic blocks from the loop in which memref instructions
6260 // can be found.
6261 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6262 // preferably without running any passes.
6263 for (BasicBlock *Block : Loop->getBlocks()) {
6264 if (Block == CLI->getCond() || Block == CLI->getHeader())
6265 continue;
6266 Reachable.insert(Block);
6267 }
6268
6269 // Add access group metadata to memory-access instructions.
6271 for (BasicBlock *BB : Reachable)
6273 // TODO: If the loop has existing parallel access metadata, have
6274 // to combine two lists.
6275 LoopMDList.push_back(MDNode::get(
6276 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6277}
6278
6280OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6281 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6282 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6283 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6284 assert(CLI->isValid() && "Requires a valid canonical loop");
6285 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6286
6287 LLVMContext &Ctx = CLI->getFunction()->getContext();
6288 Value *IV = CLI->getIndVar();
6289 Value *OrigTripCount = CLI->getTripCount();
6290 Type *IVTy = IV->getType();
6291 assert(IVTy->getIntegerBitWidth() <= 64 &&
6292 "Max supported tripcount bitwidth is 64 bits");
6293 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6294 : Type::getInt64Ty(Ctx);
6295 Type *I32Type = Type::getInt32Ty(M.getContext());
6296 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6297 Constant *One = ConstantInt::get(InternalIVTy, 1);
6298
6299 Function *F = CLI->getFunction();
6300 // Blocks must have terminators.
6301 // FIXME: Don't run analyses on incomplete/invalid IR.
6302 SmallVector<Instruction *> UIs;
6303 for (BasicBlock &BB : *F)
6304 if (!BB.hasTerminator())
6305 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6307 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6308 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6309 LoopAnalysis LIA;
6310 LoopInfo &&LI = LIA.run(*F, FAM);
6311 for (Instruction *I : UIs)
6312 I->eraseFromParent();
6313 Loop *L = LI.getLoopFor(CLI->getHeader());
6314 SmallVector<Metadata *> LoopMDList;
6315 if (ChunkSize || DistScheduleChunkSize)
6316 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6317 addLoopMetadata(CLI, LoopMDList);
6318
6319 // Declare useful OpenMP runtime functions.
6320 FunctionCallee StaticInit =
6321 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6322 FunctionCallee StaticFini =
6323 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6324
6325 // Allocate space for computed loop bounds as expected by the "init" function.
6326 Builder.restoreIP(AllocaIP);
6327 Builder.SetCurrentDebugLocation(DL);
6328 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6329 Value *PLowerBound =
6330 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6331 Value *PUpperBound =
6332 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6333 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6334 CLI->setLastIter(PLastIter);
6335
6336 // Set up the source location value for the OpenMP runtime.
6337 Builder.restoreIP(CLI->getPreheaderIP());
6338 Builder.SetCurrentDebugLocation(DL);
6339
6340 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6341 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6342 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6343 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6344 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6345 "distschedulechunksize");
6346 Value *CastedTripCount =
6347 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6348
6349 Constant *SchedulingType =
6350 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6351 Constant *DistSchedulingType =
6352 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6353 Builder.CreateStore(Zero, PLowerBound);
6354 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6355 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6356 Value *UpperBound =
6357 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6358 Builder.CreateStore(UpperBound, PUpperBound);
6359 Builder.CreateStore(One, PStride);
6360
6361 // Call the "init" function and update the trip count of the loop with the
6362 // value it produced.
6363 uint32_t SrcLocStrSize;
6364 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6365 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6366 if (DistScheduleSchedType != OMPScheduleType::None) {
6367 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6368 }
6369 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6370 Value *ThreadNum =
6371 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6372 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6373 PUpperBound, PStride, One,
6374 this](Value *SchedulingType, Value *ChunkSize,
6375 auto &Builder) {
6377 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6378 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6379 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6380 /*pstride=*/PStride, /*incr=*/One,
6381 /*chunk=*/ChunkSize});
6382 };
6383 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6384 if (DistScheduleSchedType != OMPScheduleType::None &&
6385 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6386 SchedType != OMPScheduleType::OrderedDistribute) {
6387 // We want to emit a second init function call for the dist_schedule clause
6388 // to the Distribute construct. This should only be done however if a
6389 // Workshare Loop is nested within a Distribute Construct
6390 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6391 }
6392
6393 // Load values written by the "init" function.
6394 Value *FirstChunkStart =
6395 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6396 Value *FirstChunkStop =
6397 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6398 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6399 Value *ChunkRange =
6400 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6401 Value *NextChunkStride =
6402 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6403
6404 // Create outer "dispatch" loop for enumerating the chunks.
6405 BasicBlock *DispatchEnter = splitBB(Builder, true);
6406 Value *DispatchCounter;
6407
6408 // It is safe to assume this didn't return an error because the callback
6409 // passed into createCanonicalLoop is the only possible error source, and it
6410 // always returns success.
6411 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6412 {Builder.saveIP(), DL},
6413 [&](InsertPointTy BodyIP, Value *Counter) {
6414 DispatchCounter = Counter;
6415 return Error::success();
6416 },
6417 FirstChunkStart, CastedTripCount, NextChunkStride,
6418 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6419 "dispatch"));
6420
6421 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6422 // not have to preserve the canonical invariant.
6423 BasicBlock *DispatchBody = DispatchCLI->getBody();
6424 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6425 BasicBlock *DispatchExit = DispatchCLI->getExit();
6426 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6427 DispatchCLI->invalidate();
6428
6429 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6430 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6431 redirectTo(CLI->getExit(), DispatchLatch, DL);
6432 redirectTo(DispatchBody, DispatchEnter, DL);
6433
6434 // Prepare the prolog of the chunk loop.
6435 Builder.restoreIP(CLI->getPreheaderIP());
6436 Builder.SetCurrentDebugLocation(DL);
6437
6438 // Compute the number of iterations of the chunk loop.
6439 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6440 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6441 Value *IsLastChunk =
6442 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6443 Value *CountUntilOrigTripCount =
6444 Builder.CreateSub(CastedTripCount, DispatchCounter);
6445 Value *ChunkTripCount = Builder.CreateSelect(
6446 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6447 Value *BackcastedChunkTC =
6448 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6449 CLI->setTripCount(BackcastedChunkTC);
6450
6451 // Update all uses of the induction variable except the one in the condition
6452 // block that compares it with the actual upper bound, and the increment in
6453 // the latch block.
6454 Value *BackcastedDispatchCounter =
6455 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6456 CLI->mapIndVar([&](Instruction *) -> Value * {
6457 Builder.restoreIP(CLI->getBodyIP());
6458 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6459 });
6460
6461 // In the "exit" block, call the "fini" function.
6462 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6463 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6464
6465 // Add the barrier if requested.
6466 if (NeedsBarrier) {
6467 InsertPointOrErrorTy AfterIP =
6468 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6469 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6470 if (!AfterIP)
6471 return AfterIP.takeError();
6472 }
6473
6474#ifndef NDEBUG
6475 // Even though we currently do not support applying additional methods to it,
6476 // the chunk loop should remain a canonical loop.
6477 CLI->assertOK();
6478#endif
6479
6480 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6481}
6482
6483// Returns an LLVM function to call for executing an OpenMP static worksharing
6484// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6485// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6486static FunctionCallee
6488 WorksharingLoopType LoopType) {
6489 unsigned Bitwidth = Ty->getIntegerBitWidth();
6490 Module &M = OMPBuilder->M;
6491 switch (LoopType) {
6492 case WorksharingLoopType::ForStaticLoop:
6493 if (Bitwidth == 32)
6494 return OMPBuilder->getOrCreateRuntimeFunction(
6495 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6496 if (Bitwidth == 64)
6497 return OMPBuilder->getOrCreateRuntimeFunction(
6498 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6499 break;
6500 case WorksharingLoopType::DistributeStaticLoop:
6501 if (Bitwidth == 32)
6502 return OMPBuilder->getOrCreateRuntimeFunction(
6503 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6504 if (Bitwidth == 64)
6505 return OMPBuilder->getOrCreateRuntimeFunction(
6506 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6507 break;
6508 case WorksharingLoopType::DistributeForStaticLoop:
6509 if (Bitwidth == 32)
6510 return OMPBuilder->getOrCreateRuntimeFunction(
6511 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6512 if (Bitwidth == 64)
6513 return OMPBuilder->getOrCreateRuntimeFunction(
6514 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6515 break;
6516 }
6517 if (Bitwidth != 32 && Bitwidth != 64) {
6518 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6519 }
6520 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6521}
6522
6523// Inserts a call to proper OpenMP Device RTL function which handles
6524// loop worksharing.
6526 WorksharingLoopType LoopType,
6527 BasicBlock *InsertBlock, Value *Ident,
6528 Value *LoopBodyArg, Value *TripCount,
6529 Function &LoopBodyFn, bool NoLoop) {
6530 Type *TripCountTy = TripCount->getType();
6531 Module &M = OMPBuilder->M;
6532 IRBuilder<> &Builder = OMPBuilder->Builder;
6533 FunctionCallee RTLFn =
6534 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6535 SmallVector<Value *, 8> RealArgs;
6536 RealArgs.push_back(Ident);
6537 RealArgs.push_back(&LoopBodyFn);
6538 RealArgs.push_back(LoopBodyArg);
6539 RealArgs.push_back(TripCount);
6540 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6541 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6542 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6543 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6544 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6545 return;
6546 }
6547 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6548 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6549 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6550 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6551
6552 RealArgs.push_back(
6553 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6554 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6555 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6556 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6557 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6558 } else {
6559 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6560 }
6561
6562 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6563}
6564
6566 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6567 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6568 WorksharingLoopType LoopType, bool NoLoop) {
6569 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6570 BasicBlock *Preheader = CLI->getPreheader();
6571 Value *TripCount = CLI->getTripCount();
6572
6573 // After loop body outling, the loop body contains only set up
6574 // of loop body argument structure and the call to the outlined
6575 // loop body function. Firstly, we need to move setup of loop body args
6576 // into loop preheader.
6577 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6578 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6579
6580 // The next step is to remove the whole loop. We do not it need anymore.
6581 // That's why make an unconditional branch from loop preheader to loop
6582 // exit block
6583 Builder.restoreIP({Preheader, Preheader->end()});
6584 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6585 Preheader->getTerminator()->eraseFromParent();
6586 Builder.CreateBr(CLI->getExit());
6587
6588 // Delete dead loop blocks
6589 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6590 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6591 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6592 CleanUpInfo.EntryBB = CLI->getHeader();
6593 CleanUpInfo.ExitBB = CLI->getExit();
6594 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6595 DeleteDeadBlocks(BlocksToBeRemoved);
6596
6597 // Find the instruction which corresponds to loop body argument structure
6598 // and remove the call to loop body function instruction.
6599 Value *LoopBodyArg;
6600 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6601 assert(OutlinedFnUser &&
6602 "Expected unique undroppable user of outlined function");
6603 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6604 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6605 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6606 "Expected outlined function call to be located in loop preheader");
6607 // Check in case no argument structure has been passed.
6608 if (OutlinedFnCallInstruction->arg_size() > 1)
6609 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6610 else
6611 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6612 OutlinedFnCallInstruction->eraseFromParent();
6613
6614 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6615 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6616
6617 for (auto &ToBeDeletedItem : ToBeDeleted)
6618 ToBeDeletedItem->eraseFromParent();
6619 CLI->invalidate();
6620}
6621
6622OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6623 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6624 WorksharingLoopType LoopType, bool NoLoop) {
6625 uint32_t SrcLocStrSize;
6626 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6628 switch (LoopType) {
6629 case WorksharingLoopType::ForStaticLoop:
6630 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6631 break;
6632 case WorksharingLoopType::DistributeStaticLoop:
6633 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6634 break;
6635 case WorksharingLoopType::DistributeForStaticLoop:
6636 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6637 break;
6638 }
6639 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6640
6641 auto OI = std::make_unique<OutlineInfo>();
6642 OI->OuterAllocBB = CLI->getPreheader();
6643 Function *OuterFn = CLI->getPreheader()->getParent();
6644
6645 // Instructions which need to be deleted at the end of code generation
6646 SmallVector<Instruction *, 4> ToBeDeleted;
6647
6648 OI->OuterAllocBB = AllocaIP.getBlock();
6649
6650 // Mark the body loop as region which needs to be extracted
6651 OI->EntryBB = CLI->getBody();
6652 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6653 "omp.prelatch");
6654
6655 // Prepare loop body for extraction
6656 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6657
6658 // Insert new loop counter variable which will be used only in loop
6659 // body.
6660 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6661 Instruction *NewLoopCntLoad =
6662 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6663 // New loop counter instructions are redundant in the loop preheader when
6664 // code generation for workshare loop is finshed. That's why mark them as
6665 // ready for deletion.
6666 ToBeDeleted.push_back(NewLoopCntLoad);
6667 ToBeDeleted.push_back(NewLoopCnt);
6668
6669 // Analyse loop body region. Find all input variables which are used inside
6670 // loop body region.
6671 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6673 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6674
6675 CodeExtractorAnalysisCache CEAC(*OuterFn);
6676 CodeExtractor Extractor(Blocks,
6677 /* DominatorTree */ nullptr,
6678 /* AggregateArgs */ true,
6679 /* BlockFrequencyInfo */ nullptr,
6680 /* BranchProbabilityInfo */ nullptr,
6681 /* AssumptionCache */ nullptr,
6682 /* AllowVarArgs */ true,
6683 /* AllowAlloca */ true,
6684 /* AllocationBlock */ CLI->getPreheader(),
6685 /* DeallocationBlocks */ {},
6686 /* Suffix */ ".omp_wsloop",
6687 /* AggrArgsIn0AddrSpace */ true);
6688
6689 BasicBlock *CommonExit = nullptr;
6690 SetVector<Value *> SinkingCands, HoistingCands;
6691
6692 // Find allocas outside the loop body region which are used inside loop
6693 // body
6694 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6695
6696 // We need to model loop body region as the function f(cnt, loop_arg).
6697 // That's why we replace loop induction variable by the new counter
6698 // which will be one of loop body function argument
6700 CLI->getIndVar()->user_end());
6701 for (auto Use : Users) {
6702 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6703 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6704 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6705 }
6706 }
6707 }
6708 // Make sure that loop counter variable is not merged into loop body
6709 // function argument structure and it is passed as separate variable
6710 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6711
6712 // PostOutline CB is invoked when loop body function is outlined and
6713 // loop body is replaced by call to outlined function. We need to add
6714 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6715 // function will handle loop control logic.
6716 //
6717 OI->PostOutlineCB = [=, ToBeDeletedVec =
6718 std::move(ToBeDeleted)](Function &OutlinedFn) {
6719 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6720 LoopType, NoLoop);
6721 };
6722 addOutlineInfo(std::move(OI));
6723 return CLI->getAfterIP();
6724}
6725
6728 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6729 bool HasSimdModifier, bool HasMonotonicModifier,
6730 bool HasNonmonotonicModifier, bool HasOrderedClause,
6731 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6732 Value *DistScheduleChunkSize) {
6733 if (Config.isTargetDevice())
6734 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6735 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6736 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6737 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6738
6739 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6740 OMPScheduleType::ModifierOrdered;
6741 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6742 if (HasDistSchedule) {
6743 DistScheduleSchedType = DistScheduleChunkSize
6744 ? OMPScheduleType::OrderedDistributeChunked
6745 : OMPScheduleType::OrderedDistribute;
6746 }
6747 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6748 case OMPScheduleType::BaseStatic:
6749 case OMPScheduleType::BaseDistribute:
6750 assert((!ChunkSize || !DistScheduleChunkSize) &&
6751 "No chunk size with static-chunked schedule");
6752 if (IsOrdered && !HasDistSchedule)
6753 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6754 NeedsBarrier, ChunkSize);
6755 // FIXME: Monotonicity ignored?
6756 if (DistScheduleChunkSize)
6757 return applyStaticChunkedWorkshareLoop(
6758 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6759 DistScheduleChunkSize, DistScheduleSchedType);
6760 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6761 HasDistSchedule);
6762
6763 case OMPScheduleType::BaseStaticChunked:
6764 case OMPScheduleType::BaseDistributeChunked:
6765 if (IsOrdered && !HasDistSchedule)
6766 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6767 NeedsBarrier, ChunkSize);
6768 // FIXME: Monotonicity ignored?
6769 return applyStaticChunkedWorkshareLoop(
6770 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6771 DistScheduleChunkSize, DistScheduleSchedType);
6772
6773 case OMPScheduleType::BaseRuntime:
6774 case OMPScheduleType::BaseAuto:
6775 case OMPScheduleType::BaseGreedy:
6776 case OMPScheduleType::BaseBalanced:
6777 case OMPScheduleType::BaseSteal:
6778 case OMPScheduleType::BaseRuntimeSimd:
6779 assert(!ChunkSize &&
6780 "schedule type does not support user-defined chunk sizes");
6781 [[fallthrough]];
6782 case OMPScheduleType::BaseGuidedSimd:
6783 case OMPScheduleType::BaseDynamicChunked:
6784 case OMPScheduleType::BaseGuidedChunked:
6785 case OMPScheduleType::BaseGuidedIterativeChunked:
6786 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6787 case OMPScheduleType::BaseStaticBalancedChunked:
6788 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6789 NeedsBarrier, ChunkSize);
6790
6791 default:
6792 llvm_unreachable("Unknown/unimplemented schedule kind");
6793 }
6794}
6795
6796/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6797/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6798/// the runtime. Always interpret integers as unsigned similarly to
6799/// CanonicalLoopInfo.
6800static FunctionCallee
6802 unsigned Bitwidth = Ty->getIntegerBitWidth();
6803 if (Bitwidth == 32)
6804 return OMPBuilder.getOrCreateRuntimeFunction(
6805 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6806 if (Bitwidth == 64)
6807 return OMPBuilder.getOrCreateRuntimeFunction(
6808 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6809 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6810}
6811
6812/// Returns an LLVM function to call for updating the next loop using OpenMP
6813/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6814/// the runtime. Always interpret integers as unsigned similarly to
6815/// CanonicalLoopInfo.
6816static FunctionCallee
6818 unsigned Bitwidth = Ty->getIntegerBitWidth();
6819 if (Bitwidth == 32)
6820 return OMPBuilder.getOrCreateRuntimeFunction(
6821 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6822 if (Bitwidth == 64)
6823 return OMPBuilder.getOrCreateRuntimeFunction(
6824 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6825 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6826}
6827
6828/// Returns an LLVM function to call for finalizing the dynamic loop using
6829/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6830/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6831static FunctionCallee
6833 unsigned Bitwidth = Ty->getIntegerBitWidth();
6834 if (Bitwidth == 32)
6835 return OMPBuilder.getOrCreateRuntimeFunction(
6836 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6837 if (Bitwidth == 64)
6838 return OMPBuilder.getOrCreateRuntimeFunction(
6839 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6840 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6841}
6842
6844OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6845 InsertPointTy AllocaIP,
6846 OMPScheduleType SchedType,
6847 bool NeedsBarrier, Value *Chunk) {
6848 assert(CLI->isValid() && "Requires a valid canonical loop");
6849 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6850 "Require dedicated allocate IP");
6852 "Require valid schedule type");
6853
6854 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6855 OMPScheduleType::ModifierOrdered;
6856
6857 // Set up the source location value for OpenMP runtime.
6858 Builder.SetCurrentDebugLocation(DL);
6859
6860 uint32_t SrcLocStrSize;
6861 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6862 Value *SrcLoc =
6863 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6864
6865 // Declare useful OpenMP runtime functions.
6866 Value *IV = CLI->getIndVar();
6867 Type *IVTy = IV->getType();
6868 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6869 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6870
6871 // Allocate space for computed loop bounds as expected by the "init" function.
6872 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6873 Type *I32Type = Type::getInt32Ty(M.getContext());
6874 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6875 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6876 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6877 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6878 CLI->setLastIter(PLastIter);
6879
6880 // At the end of the preheader, prepare for calling the "init" function by
6881 // storing the current loop bounds into the allocated space. A canonical loop
6882 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6883 // and produces an inclusive upper bound.
6884 BasicBlock *PreHeader = CLI->getPreheader();
6885 Builder.SetInsertPoint(PreHeader->getTerminator());
6886 Constant *One = ConstantInt::get(IVTy, 1);
6887 Builder.CreateStore(One, PLowerBound);
6888 Value *UpperBound = CLI->getTripCount();
6889 Builder.CreateStore(UpperBound, PUpperBound);
6890 Builder.CreateStore(One, PStride);
6891
6892 BasicBlock *Header = CLI->getHeader();
6893 BasicBlock *Exit = CLI->getExit();
6894 BasicBlock *Cond = CLI->getCond();
6895 BasicBlock *Latch = CLI->getLatch();
6896 InsertPointTy AfterIP = CLI->getAfterIP();
6897
6898 // The CLI will be "broken" in the code below, as the loop is no longer
6899 // a valid canonical loop.
6900
6901 if (!Chunk)
6902 Chunk = One;
6903
6904 Value *ThreadNum =
6905 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6906
6907 Constant *SchedulingType =
6908 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6909
6910 // Call the "init" function.
6911 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6912 /* LowerBound */ One, UpperBound,
6913 /* step */ One, Chunk});
6914
6915 // An outer loop around the existing one.
6916 BasicBlock *OuterCond = BasicBlock::Create(
6917 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6918 PreHeader->getParent());
6919 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6920 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6922 DynamicNext,
6923 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6924 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6925 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6926 Value *LowerBound =
6927 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6928 Builder.CreateCondBr(MoreWork, Header, Exit);
6929
6930 // Change PHI-node in loop header to use outer cond rather than preheader,
6931 // and set IV to the LowerBound.
6932 Instruction *Phi = &Header->front();
6933 auto *PI = cast<PHINode>(Phi);
6934 PI->setIncomingBlock(0, OuterCond);
6935 PI->setIncomingValue(0, LowerBound);
6936
6937 // Then set the pre-header to jump to the OuterCond
6938 Instruction *Term = PreHeader->getTerminator();
6939 auto *Br = cast<UncondBrInst>(Term);
6940 Br->setSuccessor(OuterCond);
6941
6942 // Modify the inner condition:
6943 // * Use the UpperBound returned from the DynamicNext call.
6944 // * jump to the loop outer loop when done with one of the inner loops.
6945 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6946 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6947 Instruction *Comp = &*Builder.GetInsertPoint();
6948 auto *CI = cast<CmpInst>(Comp);
6949 CI->setOperand(1, UpperBound);
6950 // Redirect the inner exit to branch to outer condition.
6951 Instruction *Branch = &Cond->back();
6952 auto *BI = cast<CondBrInst>(Branch);
6953 assert(BI->getSuccessor(1) == Exit);
6954 BI->setSuccessor(1, OuterCond);
6955
6956 // Call the "fini" function if "ordered" is present in wsloop directive.
6957 if (Ordered) {
6958 Builder.SetInsertPoint(&Latch->back());
6959 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6960 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6961 }
6962
6963 // Add the barrier if requested.
6964 if (NeedsBarrier) {
6965 Builder.SetInsertPoint(&Exit->back());
6966 InsertPointOrErrorTy BarrierIP =
6968 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6969 /* CheckCancelFlag */ false);
6970 if (!BarrierIP)
6971 return BarrierIP.takeError();
6972 }
6973
6974 CLI->invalidate();
6975 return AfterIP;
6976}
6977
6978/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6979/// after this \p OldTarget will be orphaned.
6981 BasicBlock *NewTarget, DebugLoc DL) {
6982 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6983 redirectTo(Pred, NewTarget, DL);
6984}
6985
6987 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6988 // We add a block to BBsToKeep iff we have proven it has an external use.
6990
6991 while (true) {
6992 bool Changed = false;
6993
6994 for (BasicBlock *BB : BBs) {
6995 if (BBsToKeep.contains(BB))
6996 continue;
6997
6998 for (Use &U : BB->uses()) {
6999 auto *UseInst = dyn_cast<Instruction>(U.getUser());
7000 if (!UseInst)
7001 continue;
7002 BasicBlock *UseBB = UseInst->getParent();
7003 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
7004 BBsToKeep.insert(BB);
7005 Changed = true;
7006 break;
7007 }
7008 }
7009 }
7010
7011 if (!Changed)
7012 break;
7013 }
7014
7016 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7017 DeleteDeadBlocks(BBsToDelete);
7018}
7019
7020CanonicalLoopInfo *
7022 InsertPointTy ComputeIP) {
7023 assert(Loops.size() >= 1 && "At least one loop required");
7024 size_t NumLoops = Loops.size();
7025
7026 // Nothing to do if there is already just one loop.
7027 if (NumLoops == 1)
7028 return Loops.front();
7029
7030 CanonicalLoopInfo *Outermost = Loops.front();
7031 CanonicalLoopInfo *Innermost = Loops.back();
7032 BasicBlock *OrigPreheader = Outermost->getPreheader();
7033 BasicBlock *OrigAfter = Outermost->getAfter();
7034 Function *F = OrigPreheader->getParent();
7035
7036 // Loop control blocks that may become orphaned later.
7037 SmallVector<BasicBlock *, 12> OldControlBBs;
7038 OldControlBBs.reserve(6 * Loops.size());
7040 Loop->collectControlBlocks(OldControlBBs);
7041
7042 // Setup the IRBuilder for inserting the trip count computation.
7043 Builder.SetCurrentDebugLocation(DL);
7044 if (ComputeIP.isSet())
7045 Builder.restoreIP(ComputeIP);
7046 else
7047 Builder.restoreIP(Outermost->getPreheaderIP());
7048
7049 // Derive the collapsed' loop trip count.
7050 // TODO: Find common/largest indvar type.
7051 Value *CollapsedTripCount = nullptr;
7052 for (CanonicalLoopInfo *L : Loops) {
7053 assert(L->isValid() &&
7054 "All loops to collapse must be valid canonical loops");
7055 Value *OrigTripCount = L->getTripCount();
7056 if (!CollapsedTripCount) {
7057 CollapsedTripCount = OrigTripCount;
7058 continue;
7059 }
7060
7061 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7062 CollapsedTripCount =
7063 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7064 }
7065
7066 // Create the collapsed loop control flow.
7067 CanonicalLoopInfo *Result =
7068 createLoopSkeleton(DL, CollapsedTripCount, F,
7069 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7070 /*IsCollapsed=*/true);
7071
7072 // Build the collapsed loop body code.
7073 // Start with deriving the input loop induction variables from the collapsed
7074 // one, using a divmod scheme. To preserve the original loops' order, the
7075 // innermost loop use the least significant bits.
7076 Builder.restoreIP(Result->getBodyIP());
7077
7078 Value *Leftover = Result->getIndVar();
7079 SmallVector<Value *> NewIndVars;
7080 NewIndVars.resize(NumLoops);
7081 for (int i = NumLoops - 1; i >= 1; --i) {
7082 Value *OrigTripCount = Loops[i]->getTripCount();
7083
7084 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7085 NewIndVars[i] = NewIndVar;
7086
7087 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7088 }
7089 // Outermost loop gets all the remaining bits.
7090 NewIndVars[0] = Leftover;
7091
7092 // Construct the loop body control flow.
7093 // We progressively construct the branch structure following in direction of
7094 // the control flow, from the leading in-between code, the loop nest body, the
7095 // trailing in-between code, and rejoining the collapsed loop's latch.
7096 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7097 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7098 // its predecessors as sources.
7099 BasicBlock *ContinueBlock = Result->getBody();
7100 BasicBlock *ContinuePred = nullptr;
7101 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7102 BasicBlock *NextSrc) {
7103 if (ContinueBlock)
7104 redirectTo(ContinueBlock, Dest, DL);
7105 else
7106 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7107
7108 ContinueBlock = nullptr;
7109 ContinuePred = NextSrc;
7110 };
7111
7112 // The code before the nested loop of each level.
7113 // Because we are sinking it into the nest, it will be executed more often
7114 // that the original loop. More sophisticated schemes could keep track of what
7115 // the in-between code is and instantiate it only once per thread.
7116 for (size_t i = 0; i < NumLoops - 1; ++i)
7117 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7118
7119 // Connect the loop nest body.
7120 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7121
7122 // The code after the nested loop at each level.
7123 for (size_t i = NumLoops - 1; i > 0; --i)
7124 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7125
7126 // Connect the finished loop to the collapsed loop latch.
7127 ContinueWith(Result->getLatch(), nullptr);
7128
7129 // Replace the input loops with the new collapsed loop.
7130 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7131 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7132
7133 // Replace the input loop indvars with the derived ones.
7134 for (size_t i = 0; i < NumLoops; ++i)
7135 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7136
7137 // Remove unused parts of the input loops.
7138 removeUnusedBlocksFromParent(OldControlBBs);
7139
7140 for (CanonicalLoopInfo *L : Loops)
7141 L->invalidate();
7142
7143#ifndef NDEBUG
7144 Result->assertOK();
7145#endif
7146 return Result;
7147}
7148
7149std::vector<CanonicalLoopInfo *>
7151 ArrayRef<Value *> TileSizes) {
7152 assert(TileSizes.size() == Loops.size() &&
7153 "Must pass as many tile sizes as there are loops");
7154 int NumLoops = Loops.size();
7155 assert(NumLoops >= 1 && "At least one loop to tile required");
7156
7157 CanonicalLoopInfo *OutermostLoop = Loops.front();
7158 CanonicalLoopInfo *InnermostLoop = Loops.back();
7159 Function *F = OutermostLoop->getBody()->getParent();
7160 BasicBlock *InnerEnter = InnermostLoop->getBody();
7161 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7162
7163 // Loop control blocks that may become orphaned later.
7164 SmallVector<BasicBlock *, 12> OldControlBBs;
7165 OldControlBBs.reserve(6 * Loops.size());
7167 Loop->collectControlBlocks(OldControlBBs);
7168
7169 // Collect original trip counts and induction variable to be accessible by
7170 // index. Also, the structure of the original loops is not preserved during
7171 // the construction of the tiled loops, so do it before we scavenge the BBs of
7172 // any original CanonicalLoopInfo.
7173 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7174 for (CanonicalLoopInfo *L : Loops) {
7175 assert(L->isValid() && "All input loops must be valid canonical loops");
7176 OrigTripCounts.push_back(L->getTripCount());
7177 OrigIndVars.push_back(L->getIndVar());
7178 }
7179
7180 // Collect the code between loop headers. These may contain SSA definitions
7181 // that are used in the loop nest body. To be usable with in the innermost
7182 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7183 // these instructions may be executed more often than before the tiling.
7184 // TODO: It would be sufficient to only sink them into body of the
7185 // corresponding tile loop.
7187 for (int i = 0; i < NumLoops - 1; ++i) {
7188 CanonicalLoopInfo *Surrounding = Loops[i];
7189 CanonicalLoopInfo *Nested = Loops[i + 1];
7190
7191 BasicBlock *EnterBB = Surrounding->getBody();
7192 BasicBlock *ExitBB = Nested->getHeader();
7193 InbetweenCode.emplace_back(EnterBB, ExitBB);
7194 }
7195
7196 // Compute the trip counts of the floor loops.
7197 Builder.SetCurrentDebugLocation(DL);
7198 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7199 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7200 for (int i = 0; i < NumLoops; ++i) {
7201 Value *TileSize = TileSizes[i];
7202 Value *OrigTripCount = OrigTripCounts[i];
7203 Type *IVType = OrigTripCount->getType();
7204
7205 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7206 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7207
7208 // 0 if tripcount divides the tilesize, 1 otherwise.
7209 // 1 means we need an additional iteration for a partial tile.
7210 //
7211 // Unfortunately we cannot just use the roundup-formula
7212 // (tripcount + tilesize - 1)/tilesize
7213 // because the summation might overflow. We do not want introduce undefined
7214 // behavior when the untiled loop nest did not.
7215 Value *FloorTripOverflow =
7216 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7217
7218 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7219 Value *FloorTripCount =
7220 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7221 "omp_floor" + Twine(i) + ".tripcount", true);
7222
7223 // Remember some values for later use.
7224 FloorCompleteCount.push_back(FloorCompleteTripCount);
7225 FloorCount.push_back(FloorTripCount);
7226 FloorRems.push_back(FloorTripRem);
7227 }
7228
7229 // Generate the new loop nest, from the outermost to the innermost.
7230 std::vector<CanonicalLoopInfo *> Result;
7231 Result.reserve(NumLoops * 2);
7232
7233 // The basic block of the surrounding loop that enters the nest generated
7234 // loop.
7235 BasicBlock *Enter = OutermostLoop->getPreheader();
7236
7237 // The basic block of the surrounding loop where the inner code should
7238 // continue.
7239 BasicBlock *Continue = OutermostLoop->getAfter();
7240
7241 // Where the next loop basic block should be inserted.
7242 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7243
7244 auto EmbeddNewLoop =
7245 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7246 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7247 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7248 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7249 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7250 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7251
7252 // Setup the position where the next embedded loop connects to this loop.
7253 Enter = EmbeddedLoop->getBody();
7254 Continue = EmbeddedLoop->getLatch();
7255 OutroInsertBefore = EmbeddedLoop->getLatch();
7256 return EmbeddedLoop;
7257 };
7258
7259 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7260 const Twine &NameBase) {
7261 for (auto P : enumerate(TripCounts)) {
7262 CanonicalLoopInfo *EmbeddedLoop =
7263 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7264 Result.push_back(EmbeddedLoop);
7265 }
7266 };
7267
7268 EmbeddNewLoops(FloorCount, "floor");
7269
7270 // Within the innermost floor loop, emit the code that computes the tile
7271 // sizes.
7272 Builder.SetInsertPoint(Enter->getTerminator());
7273 SmallVector<Value *, 4> TileCounts;
7274 for (int i = 0; i < NumLoops; ++i) {
7275 CanonicalLoopInfo *FloorLoop = Result[i];
7276 Value *TileSize = TileSizes[i];
7277
7278 Value *FloorIsEpilogue =
7279 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7280 Value *TileTripCount =
7281 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7282
7283 TileCounts.push_back(TileTripCount);
7284 }
7285
7286 // Create the tile loops.
7287 EmbeddNewLoops(TileCounts, "tile");
7288
7289 // Insert the inbetween code into the body.
7290 BasicBlock *BodyEnter = Enter;
7291 BasicBlock *BodyEntered = nullptr;
7292 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7293 BasicBlock *EnterBB = P.first;
7294 BasicBlock *ExitBB = P.second;
7295
7296 if (BodyEnter)
7297 redirectTo(BodyEnter, EnterBB, DL);
7298 else
7299 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7300
7301 BodyEnter = nullptr;
7302 BodyEntered = ExitBB;
7303 }
7304
7305 // Append the original loop nest body into the generated loop nest body.
7306 if (BodyEnter)
7307 redirectTo(BodyEnter, InnerEnter, DL);
7308 else
7309 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7311
7312 // Replace the original induction variable with an induction variable computed
7313 // from the tile and floor induction variables.
7314 Builder.restoreIP(Result.back()->getBodyIP());
7315 for (int i = 0; i < NumLoops; ++i) {
7316 CanonicalLoopInfo *FloorLoop = Result[i];
7317 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7318 Value *OrigIndVar = OrigIndVars[i];
7319 Value *Size = TileSizes[i];
7320
7321 Value *Scale =
7322 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7323 Value *Shift =
7324 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7325 OrigIndVar->replaceAllUsesWith(Shift);
7326 }
7327
7328 // Remove unused parts of the original loops.
7329 removeUnusedBlocksFromParent(OldControlBBs);
7330
7331 for (CanonicalLoopInfo *L : Loops)
7332 L->invalidate();
7333
7334#ifndef NDEBUG
7335 for (CanonicalLoopInfo *GenL : Result)
7336 GenL->assertOK();
7337#endif
7338 return Result;
7339}
7340
7341/// Attach metadata \p Properties to the basic block described by \p BB. If the
7342/// basic block already has metadata, the basic block properties are appended.
7345 // Nothing to do if no property to attach.
7346 if (Properties.empty())
7347 return;
7348
7349 LLVMContext &Ctx = BB->getContext();
7350 SmallVector<Metadata *> NewProperties;
7351 NewProperties.push_back(nullptr);
7352
7353 // If the basic block already has metadata, prepend it to the new metadata.
7354 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7355 if (Existing)
7356 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7357
7358 append_range(NewProperties, Properties);
7359 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7360 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7361
7362 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7363}
7364
7365/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7366/// loop already has metadata, the loop properties are appended.
7369 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7370
7371 // Attach metadata to the loop's latch
7372 BasicBlock *Latch = Loop->getLatch();
7373 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7375}
7376
7377/// Attach llvm.access.group metadata to the memref instructions of \p Block
7379 LoopInfo &LI) {
7380 for (Instruction &I : *Block) {
7381 if (I.mayReadOrWriteMemory()) {
7382 // TODO: This instruction may already have access group from
7383 // other pragmas e.g. #pragma clang loop vectorize. Append
7384 // so that the existing metadata is not overwritten.
7385 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7386 }
7387 }
7388}
7389
7390CanonicalLoopInfo *
7392 CanonicalLoopInfo *firstLoop = Loops.front();
7393 CanonicalLoopInfo *lastLoop = Loops.back();
7394 Function *F = firstLoop->getPreheader()->getParent();
7395
7396 // Loop control blocks that will become orphaned later
7397 SmallVector<BasicBlock *> oldControlBBs;
7399 Loop->collectControlBlocks(oldControlBBs);
7400
7401 // Collect original trip counts
7402 SmallVector<Value *> origTripCounts;
7403 for (CanonicalLoopInfo *L : Loops) {
7404 assert(L->isValid() && "All input loops must be valid canonical loops");
7405 origTripCounts.push_back(L->getTripCount());
7406 }
7407
7408 Builder.SetCurrentDebugLocation(DL);
7409
7410 // Compute max trip count.
7411 // The fused loop will be from 0 to max(origTripCounts)
7412 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7413 F, firstLoop->getHeader());
7414 Builder.SetInsertPoint(TCBlock);
7415 Value *fusedTripCount = nullptr;
7416 for (CanonicalLoopInfo *L : Loops) {
7417 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7418 Value *origTripCount = L->getTripCount();
7419 if (!fusedTripCount) {
7420 fusedTripCount = origTripCount;
7421 continue;
7422 }
7423 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7424 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7425 ".omp.fuse.tc");
7426 }
7427
7428 // Generate new loop
7429 CanonicalLoopInfo *fused =
7430 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7431 lastLoop->getLatch(), "fused");
7432
7433 // Replace original loops with the fused loop
7434 // Preheader and After are not considered inside the CLI.
7435 // These are used to compute the individual TCs of the loops
7436 // so they have to be put before the resulting fused loop.
7437 // Moving them up for readability.
7438 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7439 Loops[i]->getPreheader()->moveBefore(TCBlock);
7440 Loops[i]->getAfter()->moveBefore(TCBlock);
7441 }
7442 lastLoop->getPreheader()->moveBefore(TCBlock);
7443
7444 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7445 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7446 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7447 }
7448 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7449 redirectTo(TCBlock, fused->getPreheader(), DL);
7450 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7451
7452 // Build the fused body
7453 // Create new Blocks with conditions that jump to the original loop bodies
7455 SmallVector<Value *> condValues;
7456 for (size_t i = 0; i < Loops.size(); ++i) {
7457 BasicBlock *condBlock = BasicBlock::Create(
7458 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7459 Builder.SetInsertPoint(condBlock);
7460 Value *condValue =
7461 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7462 condBBs.push_back(condBlock);
7463 condValues.push_back(condValue);
7464 }
7465 // Join the condition blocks with the bodies of the original loops
7466 redirectTo(fused->getBody(), condBBs[0], DL);
7467 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7468 Builder.SetInsertPoint(condBBs[i]);
7469 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7470 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7471 // Replace the IV with the fused IV
7472 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7473 }
7474 // Last body jumps to the created end body block
7475 Builder.SetInsertPoint(condBBs.back());
7476 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7477 fused->getLatch());
7478 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7479 // Replace the IV with the fused IV
7480 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7481
7482 // The loop latch must have only one predecessor. Currently it is branched to
7483 // from both the last condition block and the last loop body
7484 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7485 "omp.fused.pre_latch");
7486
7487 // Remove unused parts
7488 removeUnusedBlocksFromParent(oldControlBBs);
7489
7490 // Invalidate old CLIs
7491 for (CanonicalLoopInfo *L : Loops)
7492 L->invalidate();
7493
7494#ifndef NDEBUG
7495 fused->assertOK();
7496#endif
7497 return fused;
7498}
7499
7501 LLVMContext &Ctx = Builder.getContext();
7503 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7504 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7505}
7506
7508 LLVMContext &Ctx = Builder.getContext();
7510 Loop, {
7511 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7512 });
7513}
7514
7515void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7516 Value *IfCond, ValueToValueMapTy &VMap,
7517 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7518 const Twine &NamePrefix) {
7519 Function *F = CanonicalLoop->getFunction();
7520
7521 // We can't do
7522 // if (cond) {
7523 // simd_loop;
7524 // } else {
7525 // non_simd_loop;
7526 // }
7527 // because then the CanonicalLoopInfo would only point to one of the loops:
7528 // leading to other constructs operating on the same loop to malfunction.
7529 // Instead generate
7530 // while (...) {
7531 // if (cond) {
7532 // simd_body;
7533 // } else {
7534 // not_simd_body;
7535 // }
7536 // }
7537 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7538 // body at -O3
7539
7540 // Define where if branch should be inserted
7541 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7542
7543 // Create additional blocks for the if statement
7544 BasicBlock *Cond = SplitBeforeIt->getParent();
7545 llvm::LLVMContext &C = Cond->getContext();
7547 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7549 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7550
7551 // Create if condition branch.
7552 Builder.SetInsertPoint(SplitBeforeIt);
7553 Instruction *BrInstr =
7554 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7555 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7556 // Then block contains branch to omp loop body which needs to be vectorized
7557 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7558 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7559
7560 Builder.SetInsertPoint(ElseBlock);
7561
7562 // Clone loop for the else branch
7564
7565 SmallVector<BasicBlock *, 8> ExistingBlocks;
7566 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7567 ExistingBlocks.push_back(ThenBlock);
7568 ExistingBlocks.append(L->block_begin(), L->block_end());
7569 // Cond is the block that has the if clause condition
7570 // LoopCond is omp_loop.cond
7571 // LoopHeader is omp_loop.header
7572 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7573 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7574 assert(LoopCond && LoopHeader && "Invalid loop structure");
7575 for (BasicBlock *Block : ExistingBlocks) {
7576 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7577 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7578 continue;
7579 }
7580 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7581
7582 // fix name not to be omp.if.then
7583 if (Block == ThenBlock)
7584 NewBB->setName(NamePrefix + ".if.else");
7585
7586 NewBB->moveBefore(CanonicalLoop->getExit());
7587 VMap[Block] = NewBB;
7588 NewBlocks.push_back(NewBB);
7589 }
7590 remapInstructionsInBlocks(NewBlocks, VMap);
7591 Builder.CreateBr(NewBlocks.front());
7592
7593 // The loop latch must have only one predecessor. Currently it is branched to
7594 // from both the 'then' and 'else' branches.
7595 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7596 NamePrefix + ".pre_latch");
7597
7598 // Ensure that the then block is added to the loop so we add the attributes in
7599 // the next step
7600 L->addBasicBlockToLoop(ThenBlock, LI);
7601}
7602
7603unsigned
7605 const StringMap<bool> &Features) {
7606 if (TargetTriple.isX86()) {
7607 if (Features.lookup("avx512f"))
7608 return 512;
7609 else if (Features.lookup("avx"))
7610 return 256;
7611 return 128;
7612 }
7613 if (TargetTriple.isPPC())
7614 return 128;
7615 if (TargetTriple.isWasm())
7616 return 128;
7617 if (TargetTriple.isSystemZ())
7618 return 64;
7619 return 0;
7620}
7621
7623 MapVector<Value *, Value *> AlignedVars,
7624 Value *IfCond, OrderKind Order,
7625 ConstantInt *Simdlen, ConstantInt *Safelen) {
7626 LLVMContext &Ctx = Builder.getContext();
7627
7628 Function *F = CanonicalLoop->getFunction();
7629
7630 // Blocks must have terminators.
7631 // FIXME: Don't run analyses on incomplete/invalid IR.
7633 for (BasicBlock &BB : *F)
7634 if (!BB.hasTerminator())
7635 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7636
7637 // TODO: We should not rely on pass manager. Currently we use pass manager
7638 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7639 // object. We should have a method which returns all blocks between
7640 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7642 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7643 FAM.registerPass([]() { return LoopAnalysis(); });
7644 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7645
7646 LoopAnalysis LIA;
7647 LoopInfo &&LI = LIA.run(*F, FAM);
7648
7649 for (Instruction *I : UIs)
7650 I->eraseFromParent();
7651
7652 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7653 if (AlignedVars.size()) {
7654 InsertPointTy IP = Builder.saveIP();
7655 for (auto &AlignedItem : AlignedVars) {
7656 Value *AlignedPtr = AlignedItem.first;
7657 Value *Alignment = AlignedItem.second;
7658 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7659 Builder.SetInsertPoint(loadInst->getNextNode());
7660 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7661 Alignment);
7662 }
7663 Builder.restoreIP(IP);
7664 }
7665
7666 if (IfCond) {
7667 ValueToValueMapTy VMap;
7668 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7669 }
7670
7672
7673 // Get the basic blocks from the loop in which memref instructions
7674 // can be found.
7675 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7676 // preferably without running any passes.
7677 for (BasicBlock *Block : L->getBlocks()) {
7678 if (Block == CanonicalLoop->getCond() ||
7679 Block == CanonicalLoop->getHeader())
7680 continue;
7681 Reachable.insert(Block);
7682 }
7683
7684 SmallVector<Metadata *> LoopMDList;
7685
7686 // In presence of finite 'safelen', it may be unsafe to mark all
7687 // the memory instructions parallel, because loop-carried
7688 // dependences of 'safelen' iterations are possible.
7689 // If clause order(concurrent) is specified then the memory instructions
7690 // are marked parallel even if 'safelen' is finite.
7691 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7692 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7693
7694 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7695 // versions so we can't add the loop attributes in that case.
7696 if (IfCond) {
7697 // we can still add llvm.loop.parallel_access
7698 addLoopMetadata(CanonicalLoop, LoopMDList);
7699 return;
7700 }
7701
7702 // Use the above access group metadata to create loop level
7703 // metadata, which should be distinct for each loop.
7704 LoopMDList.push_back(
7705 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7706
7707 if (Simdlen || Safelen) {
7708 // If both simdlen and safelen clauses are specified, the value of the
7709 // simdlen parameter must be less than or equal to the value of the safelen
7710 // parameter. Therefore, use safelen only in the absence of simdlen.
7711 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7712 LoopMDList.push_back(
7713 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7714 ConstantAsMetadata::get(VectorizeWidth)}));
7715 }
7716
7717 addLoopMetadata(CanonicalLoop, LoopMDList);
7718}
7719
7720/// Create the TargetMachine object to query the backend for optimization
7721/// preferences.
7722///
7723/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7724/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7725/// needed for the LLVM pass pipline. We use some default options to avoid
7726/// having to pass too many settings from the frontend that probably do not
7727/// matter.
7728///
7729/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7730/// method. If we are going to use TargetMachine for more purposes, especially
7731/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7732/// might become be worth requiring front-ends to pass on their TargetMachine,
7733/// or at least cache it between methods. Note that while fontends such as Clang
7734/// have just a single main TargetMachine per translation unit, "target-cpu" and
7735/// "target-features" that determine the TargetMachine are per-function and can
7736/// be overrided using __attribute__((target("OPTIONS"))).
7737static std::unique_ptr<TargetMachine>
7739 Module *M = F->getParent();
7740
7741 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7742 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7743 const llvm::Triple &Triple = M->getTargetTriple();
7744
7745 std::string Error;
7747 if (!TheTarget)
7748 return {};
7749
7751 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7752 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7753 /*CodeModel=*/std::nullopt, OptLevel));
7754}
7755
7756/// Heuristically determine the best-performant unroll factor for \p CLI. This
7757/// depends on the target processor. We are re-using the same heuristics as the
7758/// LoopUnrollPass.
7760 Function *F = CLI->getFunction();
7761
7762 // Assume the user requests the most aggressive unrolling, even if the rest of
7763 // the code is optimized using a lower setting.
7765 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7766
7767 // Blocks must have terminators.
7768 // FIXME: Don't run analyses on incomplete/invalid IR.
7770 for (BasicBlock &BB : *F)
7771 if (!BB.hasTerminator())
7772 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7773
7775 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7776 FAM.registerPass([]() { return AssumptionAnalysis(); });
7777 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7778 FAM.registerPass([]() { return LoopAnalysis(); });
7779 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7780 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7781 TargetIRAnalysis TIRA;
7782 if (TM)
7783 TIRA = TargetIRAnalysis(
7784 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7785 FAM.registerPass([&]() { return TIRA; });
7786
7787 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7789 ScalarEvolution &&SE = SEA.run(*F, FAM);
7791 DominatorTree &&DT = DTA.run(*F, FAM);
7792 LoopAnalysis LIA;
7793 LoopInfo &&LI = LIA.run(*F, FAM);
7795 AssumptionCache &&AC = ACT.run(*F, FAM);
7797
7798 for (Instruction *I : UIs)
7799 I->eraseFromParent();
7800
7801 Loop *L = LI.getLoopFor(CLI->getHeader());
7802 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7803
7805 L, SE, TTI,
7806 /*BlockFrequencyInfo=*/nullptr,
7807 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7808 /*UserThreshold=*/std::nullopt,
7809 /*UserAllowPartial=*/true,
7810 /*UserAllowRuntime=*/true,
7811 /*UserUpperBound=*/std::nullopt,
7812 /*UserFullUnrollMaxCount=*/std::nullopt);
7813
7814 UP.Force = true;
7815
7816 // Account for additional optimizations taking place before the LoopUnrollPass
7817 // would unroll the loop.
7820
7821 // Use normal unroll factors even if the rest of the code is optimized for
7822 // size.
7825
7826 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7827 << " Threshold=" << UP.Threshold << "\n"
7828 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7829 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7830 << " PartialOptSizeThreshold="
7831 << UP.PartialOptSizeThreshold << "\n");
7832
7833 // Disable peeling.
7836 /*UserAllowPeeling=*/false,
7837 /*UserAllowProfileBasedPeeling=*/false,
7838 /*UnrollingSpecficValues=*/false);
7839
7841 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7842
7843 // Assume that reads and writes to stack variables can be eliminated by
7844 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7845 // size.
7846 for (BasicBlock *BB : L->blocks()) {
7847 for (Instruction &I : *BB) {
7848 Value *Ptr;
7849 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7850 Ptr = Load->getPointerOperand();
7851 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7852 Ptr = Store->getPointerOperand();
7853 } else
7854 continue;
7855
7856 Ptr = Ptr->stripPointerCasts();
7857
7858 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7859 if (Alloca->getParent() == &F->getEntryBlock())
7860 EphValues.insert(&I);
7861 }
7862 }
7863 }
7864
7865 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7866
7867 // Loop is not unrollable if the loop contains certain instructions.
7868 if (!UCE.canUnroll()) {
7869 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7870 return 1;
7871 }
7872
7873 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7874 << "\n");
7875
7876 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7877 // be able to use it.
7878 int TripCount = 0;
7879 int MaxTripCount = 0;
7880 bool MaxOrZero = false;
7881 unsigned TripMultiple = 0;
7882
7883 unsigned Factor =
7884 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7885 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7886 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7887
7888 // This function returns 1 to signal to not unroll a loop.
7889 if (Factor == 0)
7890 return 1;
7891 return Factor;
7892}
7893
7895 int32_t Factor,
7896 CanonicalLoopInfo **UnrolledCLI) {
7897 assert(Factor >= 0 && "Unroll factor must not be negative");
7898
7899 Function *F = Loop->getFunction();
7900 LLVMContext &Ctx = F->getContext();
7901
7902 // If the unrolled loop is not used for another loop-associated directive, it
7903 // is sufficient to add metadata for the LoopUnrollPass.
7904 if (!UnrolledCLI) {
7905 SmallVector<Metadata *, 2> LoopMetadata;
7906 LoopMetadata.push_back(
7907 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7908
7909 if (Factor >= 1) {
7911 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7912 LoopMetadata.push_back(MDNode::get(
7913 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7914 }
7915
7916 addLoopMetadata(Loop, LoopMetadata);
7917 return;
7918 }
7919
7920 // Heuristically determine the unroll factor.
7921 if (Factor == 0)
7923
7924 // No change required with unroll factor 1.
7925 if (Factor == 1) {
7926 *UnrolledCLI = Loop;
7927 return;
7928 }
7929
7930 assert(Factor >= 2 &&
7931 "unrolling only makes sense with a factor of 2 or larger");
7932
7933 Type *IndVarTy = Loop->getIndVarType();
7934
7935 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7936 // unroll the inner loop.
7937 Value *FactorVal =
7938 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7939 /*isSigned=*/false));
7940 std::vector<CanonicalLoopInfo *> LoopNest =
7941 tileLoops(DL, {Loop}, {FactorVal});
7942 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7943 *UnrolledCLI = LoopNest[0];
7944 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7945
7946 // LoopUnrollPass can only fully unroll loops with constant trip count.
7947 // Unroll by the unroll factor with a fallback epilog for the remainder
7948 // iterations if necessary.
7950 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7952 InnerLoop,
7953 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7955 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7956
7957#ifndef NDEBUG
7958 (*UnrolledCLI)->assertOK();
7959#endif
7960}
7961
7964 llvm::Value *BufSize, llvm::Value *CpyBuf,
7965 llvm::Value *CpyFn, llvm::Value *DidIt) {
7966 if (!updateToLocation(Loc))
7967 return Loc.IP;
7968
7969 uint32_t SrcLocStrSize;
7970 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7971 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7972 Value *ThreadId = getOrCreateThreadID(Ident);
7973
7974 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7975
7976 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7977
7978 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7979 createRuntimeFunctionCall(Fn, Args);
7980
7981 return Builder.saveIP();
7982}
7983
7985 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7986 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7988
7989 if (!updateToLocation(Loc))
7990 return Loc.IP;
7991
7992 // If needed allocate and initialize `DidIt` with 0.
7993 // DidIt: flag variable: 1=single thread; 0=not single thread.
7994 llvm::Value *DidIt = nullptr;
7995 if (!CPVars.empty()) {
7996 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7997 Builder.CreateStore(Builder.getInt32(0), DidIt);
7998 }
7999
8000 Directive OMPD = Directive::OMPD_single;
8001 uint32_t SrcLocStrSize;
8002 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8003 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8004 Value *ThreadId = getOrCreateThreadID(Ident);
8005 Value *Args[] = {Ident, ThreadId};
8006
8007 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8008 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8009
8010 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8011 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8012
8013 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8014 if (Error Err = FiniCB(IP))
8015 return Err;
8016
8017 // The thread that executes the single region must set `DidIt` to 1.
8018 // This is used by __kmpc_copyprivate, to know if the caller is the
8019 // single thread or not.
8020 if (DidIt)
8021 Builder.CreateStore(Builder.getInt32(1), DidIt);
8022
8023 return Error::success();
8024 };
8025
8026 // generates the following:
8027 // if (__kmpc_single()) {
8028 // .... single region ...
8029 // __kmpc_end_single
8030 // }
8031 // __kmpc_copyprivate
8032 // __kmpc_barrier
8033
8034 InsertPointOrErrorTy AfterIP =
8035 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8036 /*Conditional*/ true,
8037 /*hasFinalize*/ true);
8038 if (!AfterIP)
8039 return AfterIP.takeError();
8040
8041 if (DidIt) {
8042 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8043 // NOTE BufSize is currently unused, so just pass 0.
8045 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8046 CPFuncs[I], DidIt);
8047 // NOTE __kmpc_copyprivate already inserts a barrier
8048 } else if (!IsNowait) {
8049 InsertPointOrErrorTy AfterIP =
8051 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8052 /* CheckCancelFlag */ false);
8053 if (!AfterIP)
8054 return AfterIP.takeError();
8055 }
8056 return Builder.saveIP();
8057}
8058
8061 BodyGenCallbackTy BodyGenCB,
8062 FinalizeCallbackTy FiniCB, bool IsNowait) {
8063
8064 if (!updateToLocation(Loc))
8065 return Loc.IP;
8066
8067 // All threads execute the scope body — no conditional entry.
8068 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8069 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8070 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8071 /*IsCancellable=*/false);
8072 if (!AfterIP)
8073 return AfterIP.takeError();
8074
8075 Builder.restoreIP(*AfterIP);
8076 if (!IsNowait) {
8077 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8078 omp::Directive::OMPD_unknown,
8079 /*ForceSimpleCall=*/false,
8080 /*CheckCancelFlag=*/false);
8081 if (!AfterIP)
8082 return AfterIP.takeError();
8083 }
8084 return Builder.saveIP();
8085}
8086
8088 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8089 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8090
8091 if (!updateToLocation(Loc))
8092 return Loc.IP;
8093
8094 Directive OMPD = Directive::OMPD_critical;
8095 uint32_t SrcLocStrSize;
8096 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8097 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8098 Value *ThreadId = getOrCreateThreadID(Ident);
8099 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8100 Value *Args[] = {Ident, ThreadId, LockVar};
8101
8102 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8103 Function *RTFn = nullptr;
8104 if (HintInst) {
8105 // Add Hint to entry Args and create call
8106 EnterArgs.push_back(HintInst);
8107 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8108 } else {
8109 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8110 }
8111 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8112
8113 Function *ExitRTLFn =
8114 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8115 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8116
8117 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8118 /*Conditional*/ false, /*hasFinalize*/ true);
8119}
8120
8123 InsertPointTy AllocaIP, unsigned NumLoops,
8124 ArrayRef<llvm::Value *> StoreValues,
8125 const Twine &Name, bool IsDependSource) {
8126 assert(
8127 llvm::all_of(StoreValues,
8128 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8129 "OpenMP runtime requires depend vec with i64 type");
8130
8131 if (!updateToLocation(Loc))
8132 return Loc.IP;
8133
8134 // Allocate space for vector and generate alloc instruction.
8135 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8136 Builder.restoreIP(AllocaIP);
8137 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8138 ArgsBase->setAlignment(Align(8));
8140
8141 // Store the index value with offset in depend vector.
8142 for (unsigned I = 0; I < NumLoops; ++I) {
8143 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8144 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8145 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8146 STInst->setAlignment(Align(8));
8147 }
8148
8149 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8150 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8151
8152 uint32_t SrcLocStrSize;
8153 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8154 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8155 Value *ThreadId = getOrCreateThreadID(Ident);
8156 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8157
8158 Function *RTLFn = nullptr;
8159 if (IsDependSource)
8160 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8161 else
8162 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8163 createRuntimeFunctionCall(RTLFn, Args);
8164
8165 return Builder.saveIP();
8166}
8167
8169 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8170 FinalizeCallbackTy FiniCB, bool IsThreads) {
8171 if (!updateToLocation(Loc))
8172 return Loc.IP;
8173
8174 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8175 Instruction *EntryCall = nullptr;
8176 Instruction *ExitCall = nullptr;
8177
8178 if (IsThreads) {
8179 uint32_t SrcLocStrSize;
8180 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8181 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8182 Value *ThreadId = getOrCreateThreadID(Ident);
8183 Value *Args[] = {Ident, ThreadId};
8184
8185 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8186 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8187
8188 Function *ExitRTLFn =
8189 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8190 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8191 }
8192
8193 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8194 /*Conditional*/ false, /*hasFinalize*/ true);
8195}
8196
8197OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8198 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8199 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8200 bool HasFinalize, bool IsCancellable) {
8201
8202 if (HasFinalize)
8203 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8204
8205 // Create inlined region's entry and body blocks, in preparation
8206 // for conditional creation
8207 BasicBlock *EntryBB = Builder.GetInsertBlock();
8208 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8210 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8211 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8212 BasicBlock *FiniBB =
8213 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8214
8215 Builder.SetInsertPoint(EntryBB->getTerminator());
8216 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8217
8218 // generate body
8219 if (Error Err =
8220 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8221 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8222 return Err;
8223
8224 // emit exit call and do any needed finalization.
8225 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8226 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8227 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8228 "Unexpected control flow graph state!!");
8229 InsertPointOrErrorTy AfterIP =
8230 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8231 if (!AfterIP)
8232 return AfterIP.takeError();
8233
8234 // If we are skipping the region of a non conditional, remove the exit
8235 // block, and clear the builder's insertion point.
8236 assert(SplitPos->getParent() == ExitBB &&
8237 "Unexpected Insertion point location!");
8238 auto merged = MergeBlockIntoPredecessor(ExitBB);
8239 BasicBlock *ExitPredBB = SplitPos->getParent();
8240 auto InsertBB = merged ? ExitPredBB : ExitBB;
8242 SplitPos->eraseFromParent();
8243 Builder.SetInsertPoint(InsertBB);
8244
8245 return Builder.saveIP();
8246}
8247
8248OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8249 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8250 // if nothing to do, Return current insertion point.
8251 if (!Conditional || !EntryCall)
8252 return Builder.saveIP();
8253
8254 BasicBlock *EntryBB = Builder.GetInsertBlock();
8255 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8256 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8257 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8258
8259 // Emit thenBB and set the Builder's insertion point there for
8260 // body generation next. Place the block after the current block.
8261 Function *CurFn = EntryBB->getParent();
8262 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8263
8264 // Move Entry branch to end of ThenBB, and replace with conditional
8265 // branch (If-stmt)
8266 Instruction *EntryBBTI = EntryBB->getTerminator();
8267 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8268 EntryBBTI->removeFromParent();
8269 Builder.SetInsertPoint(UI);
8270 Builder.Insert(EntryBBTI);
8271 UI->eraseFromParent();
8272 Builder.SetInsertPoint(ThenBB->getTerminator());
8273
8274 // return an insertion point to ExitBB.
8275 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8276}
8277
8278OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8279 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8280 bool HasFinalize) {
8281
8282 Builder.restoreIP(FinIP);
8283
8284 // If there is finalization to do, emit it before the exit call
8285 if (HasFinalize) {
8286 assert(!FinalizationStack.empty() &&
8287 "Unexpected finalization stack state!");
8288
8289 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8290 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8291
8292 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8293 return std::move(Err);
8294
8295 // Exit condition: insertion point is before the terminator of the new Fini
8296 // block
8297 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8298 }
8299
8300 if (!ExitCall)
8301 return Builder.saveIP();
8302
8303 // place the Exitcall as last instruction before Finalization block terminator
8304 ExitCall->removeFromParent();
8305 Builder.Insert(ExitCall);
8306
8307 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8308 ExitCall->getIterator());
8309}
8310
8312 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8313 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8314 if (!IP.isSet())
8315 return IP;
8316
8318
8319 // creates the following CFG structure
8320 // OMP_Entry : (MasterAddr != PrivateAddr)?
8321 // F T
8322 // | \
8323 // | copin.not.master
8324 // | /
8325 // v /
8326 // copyin.not.master.end
8327 // |
8328 // v
8329 // OMP.Entry.Next
8330
8331 BasicBlock *OMP_Entry = IP.getBlock();
8332 Function *CurFn = OMP_Entry->getParent();
8333 BasicBlock *CopyBegin =
8334 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8335 BasicBlock *CopyEnd = nullptr;
8336
8337 // If entry block is terminated, split to preserve the branch to following
8338 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8340 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8341 "copyin.not.master.end");
8342 OMP_Entry->getTerminator()->eraseFromParent();
8343 } else {
8344 CopyEnd =
8345 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8346 }
8347
8348 Builder.SetInsertPoint(OMP_Entry);
8349 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8350 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8351 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8352 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8353
8354 Builder.SetInsertPoint(CopyBegin);
8355 if (BranchtoEnd)
8356 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8357
8358 return Builder.saveIP();
8359}
8360
8362 Value *Size, Value *Allocator,
8363 std::string Name) {
8365 if (!updateToLocation(Loc))
8366 return nullptr;
8367
8368 uint32_t SrcLocStrSize;
8369 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8370 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8371 Value *ThreadId = getOrCreateThreadID(Ident);
8372 Value *Args[] = {ThreadId, Size, Allocator};
8373
8374 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8375
8376 return createRuntimeFunctionCall(Fn, Args, Name);
8377}
8378
8380 Value *Align, Value *Size,
8381 Value *Allocator,
8382 std::string Name) {
8384 if (!updateToLocation(Loc))
8385 return nullptr;
8386
8387 uint32_t SrcLocStrSize;
8388 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8389 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8390 Value *ThreadId = getOrCreateThreadID(Ident);
8391 Value *Args[] = {ThreadId, Align, Size, Allocator};
8392
8393 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8394
8395 return Builder.CreateCall(Fn, Args, Name);
8396}
8397
8399 Value *Addr, Value *Allocator,
8400 std::string Name) {
8402 if (!updateToLocation(Loc))
8403 return nullptr;
8404
8405 uint32_t SrcLocStrSize;
8406 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8407 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8408 Value *ThreadId = getOrCreateThreadID(Ident);
8409 Value *Args[] = {ThreadId, Addr, Allocator};
8410 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8411 return createRuntimeFunctionCall(Fn, Args, Name);
8412}
8413
8415 Value *Size,
8416 const Twine &Name) {
8419
8420 Value *Args[] = {Size};
8421 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8422 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8424 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8425 return Call;
8426}
8427
8429 Type *VarType,
8430 const Twine &Name) {
8431 return createOMPAllocShared(
8432 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8433}
8434
8436 Value *Addr, Value *Size,
8437 const Twine &Name) {
8440
8441 Value *Args[] = {Addr, Size};
8442 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8443 return Builder.CreateCall(Fn, Args, Name);
8444}
8445
8447 Value *Addr, Type *VarType,
8448 const Twine &Name) {
8449 return createOMPFreeShared(
8450 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8451 Name);
8452}
8453
8455 const LocationDescription &Loc, Value *InteropVar,
8457 Value *DependenceAddress, bool HaveNowaitClause) {
8460
8461 uint32_t SrcLocStrSize;
8462 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8463 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8464 Value *ThreadId = getOrCreateThreadID(Ident);
8465 if (Device == nullptr)
8467 else if (Device->getType() != Int32)
8468 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8469 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8470 if (NumDependences == nullptr) {
8471 NumDependences = ConstantInt::get(Int32, 0);
8472 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8473 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8474 }
8475 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8476 Value *Args[] = {
8477 Ident, ThreadId, InteropVar, InteropTypeVal,
8478 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8479
8480 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8481
8482 return createRuntimeFunctionCall(Fn, Args);
8483}
8484
8486 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8487 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8490
8491 uint32_t SrcLocStrSize;
8492 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8493 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8494 Value *ThreadId = getOrCreateThreadID(Ident);
8495 if (Device == nullptr)
8497 else if (Device->getType() != Int32)
8498 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8499 if (NumDependences == nullptr) {
8500 NumDependences = ConstantInt::get(Int32, 0);
8501 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8502 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8503 }
8504 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8505 Value *Args[] = {
8506 Ident, ThreadId, InteropVar, Device,
8507 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8508
8509 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8510
8511 return createRuntimeFunctionCall(Fn, Args);
8512}
8513
8515 Value *InteropVar, Value *Device,
8516 Value *NumDependences,
8517 Value *DependenceAddress,
8518 bool HaveNowaitClause) {
8521 uint32_t SrcLocStrSize;
8522 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8523 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8524 Value *ThreadId = getOrCreateThreadID(Ident);
8525 if (Device == nullptr)
8527 else if (Device->getType() != Int32)
8528 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8529 if (NumDependences == nullptr) {
8530 NumDependences = ConstantInt::get(Int32, 0);
8531 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8532 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8533 }
8534 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8535 Value *Args[] = {
8536 Ident, ThreadId, InteropVar, Device,
8537 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8538
8539 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8540
8541 return createRuntimeFunctionCall(Fn, Args);
8542}
8543
8546 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8549
8550 uint32_t SrcLocStrSize;
8551 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8552 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8553 Value *ThreadId = getOrCreateThreadID(Ident);
8554 Constant *ThreadPrivateCache =
8555 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8556 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8557
8558 Function *Fn =
8559 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8560
8561 return createRuntimeFunctionCall(Fn, Args);
8562}
8563
8565 const LocationDescription &Loc,
8567 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8568 "expected num_threads and num_teams to be specified");
8569
8570 if (!updateToLocation(Loc))
8571 return nullptr;
8572
8573 uint32_t SrcLocStrSize;
8574 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8575 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8576 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8577 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8578 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8579 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8580 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8581 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8582
8583 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8584 Function *Kernel = DebugKernelWrapper;
8585
8586 // We need to strip the debug prefix to get the correct kernel name.
8587 StringRef KernelName = Kernel->getName();
8588 const std::string DebugPrefix = "_debug__";
8589 if (KernelName.ends_with(DebugPrefix)) {
8590 KernelName = KernelName.drop_back(DebugPrefix.length());
8591 Kernel = M.getFunction(KernelName);
8592 assert(Kernel && "Expected the real kernel to exist");
8593 }
8594
8595 // Manifest the launch configuration in the metadata matching the kernel
8596 // environment.
8597 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8598 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8599 Attrs.MaxTeams.front());
8600
8601 // Don't derive or write thread bounds for Bare kernels.
8602 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8603 if (Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_BARE) {
8604 // If MaxThreads is not set and needs adjustment, select the maximum
8605 // between the default workgroup size and the MinThreads value. This is
8606 // only meaningful for targets with a known grid value (i.e. GPUs); for
8607 // other targets (e.g. host kernels) leave it unset so the runtime falls
8608 // back to its own device-specific default.
8609 if (MaxThreadsVal < 0 && UseDefaultMaxThreads && hasGridValue(T))
8610 MaxThreadsVal =
8611 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8612 Attrs.MinThreads.front());
8613
8614 // Generic mode runs the main thread on a warp of its own, past
8615 // thread_limit. Reserve the widest warp any target has. Not on SPIR-V,
8616 // causes problems with Level Zero.
8617 if (MaxThreadsVal > 0 &&
8618 Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC && hasGridValue(T) &&
8619 !T.isSPIRV())
8620 MaxThreadsVal = int32_t(
8621 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8622 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8623
8624 if (MaxThreadsVal > 0)
8625 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8626 MaxThreadsVal);
8627 }
8628
8629 Constant *MinThreads =
8630 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8631 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8632 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8633 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8634 Constant *ReductionDataSize =
8635 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8636
8637 const DataLayout &DL = M.getDataLayout();
8638
8639 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8640 Constant *DynamicEnvironmentInitializer =
8641 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8642 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8643 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8644 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8645 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8646 DL.getDefaultGlobalsAddressSpace());
8647 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8648
8649 Constant *DynamicEnvironment =
8650 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8651 ? DynamicEnvironmentGV
8652 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8653 DynamicEnvironmentPtr);
8654
8655 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8656 ConfigurationEnvironment, {
8657 UseGenericStateMachineVal,
8658 MayUseNestedParallelismVal,
8659 IsSPMDVal,
8660 MinThreads,
8661 MaxThreads,
8662 MinTeams,
8663 MaxTeams,
8664 ReductionDataSize,
8665 });
8666 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8667 KernelEnvironment, {
8668 ConfigurationEnvironmentInitializer,
8669 Ident,
8670 DynamicEnvironment,
8671 });
8672 std::string KernelEnvironmentName =
8673 (KernelName + "_kernel_environment").str();
8674 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8675 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8676 KernelEnvironmentInitializer, KernelEnvironmentName,
8677 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8678 DL.getDefaultGlobalsAddressSpace());
8679 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8680
8681 return KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8682 ? KernelEnvironmentGV
8683 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8684 KernelEnvironmentPtr);
8685}
8686
8688 const LocationDescription &Loc,
8690 Constant *KernelEnvironment = emitKernelEnvironment(Loc, Attrs);
8691 if (!KernelEnvironment)
8692 return Loc.IP;
8693
8694 if (!updateToLocation(Loc))
8695 return Loc.IP;
8696
8697 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8699 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8700
8701 Value *KernelLaunchEnvironment =
8702 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8703 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8704 KernelLaunchEnvironment =
8705 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8706 ? KernelLaunchEnvironment
8707 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8708 KernelLaunchEnvParamTy);
8709 CallInst *ThreadKind = createRuntimeFunctionCall(
8710 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8711
8712 Value *ExecUserCode = Builder.CreateICmpEQ(
8713 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8714 "exec_user_code");
8715
8716 // ThreadKind = __kmpc_target_init(...)
8717 // if (ThreadKind == -1)
8718 // user_code
8719 // else
8720 // return;
8721
8722 auto *UI = Builder.CreateUnreachable();
8723 BasicBlock *CheckBB = UI->getParent();
8724 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8725
8726 BasicBlock *WorkerExitBB = BasicBlock::Create(
8727 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8728 Builder.SetInsertPoint(WorkerExitBB);
8729 Builder.CreateRetVoid();
8730
8731 auto *CheckBBTI = CheckBB->getTerminator();
8732 Builder.SetInsertPoint(CheckBBTI);
8733 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8734
8735 CheckBBTI->eraseFromParent();
8736 UI->eraseFromParent();
8737
8738 // Continue in the "user_code" block, see diagram above and in
8739 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8740 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8741}
8742
8744 int32_t TeamsReductionDataSize) {
8745 if (!updateToLocation(Loc))
8746 return;
8747
8749 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8750
8752
8753 if (!TeamsReductionDataSize)
8754 return;
8755
8756 Function *Kernel = Builder.GetInsertBlock()->getParent();
8757 // We need to strip the debug prefix to get the correct kernel name.
8758 StringRef KernelName = Kernel->getName();
8759 const std::string DebugPrefix = "_debug__";
8760 if (KernelName.ends_with(DebugPrefix))
8761 KernelName = KernelName.drop_back(DebugPrefix.length());
8762 auto *KernelEnvironmentGV =
8763 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8764 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8765 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8766 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8767 KernelEnvironmentInitializer,
8768 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8769 KernelEnvironmentGV->setInitializer(NewInitializer);
8770}
8771
8772static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8773 bool Min) {
8774 if (Kernel.hasFnAttribute(Name)) {
8775 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8776 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8777 }
8778 Kernel.addFnAttr(Name, llvm::utostr(Value));
8779}
8780
8781std::pair<int32_t, int32_t>
8783 int32_t ThreadLimit =
8784 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8785
8786 if (T.isAMDGPU()) {
8787 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8788 if (!Attr.isValid() || !Attr.isStringAttribute())
8789 return {0, ThreadLimit};
8790 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8791 int32_t LB, UB;
8792 if (!llvm::to_integer(UBStr, UB, 10))
8793 return {0, ThreadLimit};
8794 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8795 if (!llvm::to_integer(LBStr, LB, 10))
8796 return {0, UB};
8797 return {LB, UB};
8798 }
8799
8800 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8801 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8802 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8803 }
8804 return {0, ThreadLimit};
8805}
8806
8808 Function &Kernel, int32_t LB,
8809 int32_t UB) {
8810 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8811
8812 if (T.isAMDGPU()) {
8813 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8814 llvm::utostr(LB) + "," + llvm::utostr(UB));
8815 return;
8816 }
8817
8819}
8820
8821std::pair<int32_t, int32_t>
8823 // TODO: Read from backend annotations if available.
8824 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8825}
8826
8828 int32_t LB, int32_t UB) {
8829 if (UB > 0) {
8830 if (T.isNVPTX())
8832 if (T.isAMDGPU())
8833 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8834 }
8835
8836 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8837}
8838
8839void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8840 Function *OutlinedFn) {
8841 if (Config.isTargetDevice()) {
8843 // TODO: Determine if DSO local can be set to true.
8844 OutlinedFn->setDSOLocal(false);
8846 if (T.isAMDGCN())
8848 else if (T.isNVPTX())
8850 else if (T.isSPIRV())
8852 }
8853}
8854
8855Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8856 StringRef EntryFnIDName) {
8857 if (Config.isTargetDevice()) {
8858 assert(OutlinedFn && "The outlined function must exist if embedded");
8859 return OutlinedFn;
8860 }
8861
8862 return new GlobalVariable(
8863 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8864 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8865}
8866
8867Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8868 StringRef EntryFnName) {
8869 if (OutlinedFn)
8870 return OutlinedFn;
8871
8872 assert(!M.getGlobalVariable(EntryFnName, true) &&
8873 "Named kernel already exists?");
8874 return new GlobalVariable(
8875 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8876 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8877}
8878
8880 TargetRegionEntryInfo &EntryInfo,
8881 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8882 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8883
8884 SmallString<64> EntryFnName;
8885 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8886
8887 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8888 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8889 if (!CBResult)
8890 return CBResult.takeError();
8891 OutlinedFn = *CBResult;
8892 } else {
8893 OutlinedFn = nullptr;
8894 }
8895
8896 // If this target outline function is not an offload entry, we don't need to
8897 // register it. This may be in the case of a false if clause, or if there are
8898 // no OpenMP targets.
8899 if (!IsOffloadEntry)
8900 return Error::success();
8901
8902 std::string EntryFnIDName =
8903 Config.isTargetDevice()
8904 ? std::string(EntryFnName)
8905 : createPlatformSpecificName({EntryFnName, "region_id"});
8906
8907 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8908 EntryFnName, EntryFnIDName);
8909 return Error::success();
8910}
8911
8913 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8914 StringRef EntryFnName, StringRef EntryFnIDName) {
8915 if (OutlinedFn)
8916 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8917 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8918 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8919 OffloadInfoManager.registerTargetRegionEntryInfo(
8920 EntryInfo, EntryAddr, OutlinedFnID,
8922 return OutlinedFnID;
8923}
8924
8926 const LocationDescription &Loc, InsertPointTy AllocaIP,
8927 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8928 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8929 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8930 omp::RuntimeFunction *MapperFunc,
8932 BodyGenTy BodyGenType)>
8933 BodyGenCB,
8934 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8935 if (!updateToLocation(Loc))
8936 return InsertPointTy();
8937
8938 Builder.restoreIP(CodeGenIP);
8939
8940 bool IsStandAlone = !BodyGenCB;
8941 MapInfosTy *MapInfo;
8942 // Generate the code for the opening of the data environment. Capture all the
8943 // arguments of the runtime call by reference because they are used in the
8944 // closing of the region.
8945 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8946 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8947 MapInfo = &GenMapInfoCB(Builder.saveIP());
8948 if (Error Err = emitOffloadingArrays(
8949 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8950 /*IsNonContiguous=*/true, DeviceAddrCB))
8951 return Err;
8952
8953 TargetDataRTArgs RTArgs;
8955
8956 // Emit the number of elements in the offloading arrays.
8957 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8958
8959 // Source location for the ident struct
8960 if (!SrcLocInfo) {
8961 uint32_t SrcLocStrSize;
8962 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8963 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8964 }
8965
8966 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8967 SrcLocInfo, DeviceID,
8968 PointerNum, RTArgs.BasePointersArray,
8969 RTArgs.PointersArray, RTArgs.SizesArray,
8970 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8971 RTArgs.MappersArray};
8972
8973 if (IsStandAlone) {
8974 assert(MapperFunc && "MapperFunc missing for standalone target data");
8975
8976 auto TaskBodyCB = [&](Value *, Value *,
8978 if (Info.HasNoWait) {
8979 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8983 }
8984
8986 OffloadingArgs);
8987
8988 if (Info.HasNoWait) {
8989 BasicBlock *OffloadContBlock =
8990 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8991 Function *CurFn = Builder.GetInsertBlock()->getParent();
8992 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8993 Builder.restoreIP(Builder.saveIP());
8994 }
8995 return Error::success();
8996 };
8997
8998 bool RequiresOuterTargetTask = Info.HasNoWait;
8999 if (!RequiresOuterTargetTask)
9000 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
9001 /*TargetTaskAllocaIP=*/{}));
9002 else
9003 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
9004 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
9005 } else {
9006 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
9007 omp::OMPRTL___tgt_target_data_begin_mapper);
9008
9009 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
9010
9011 for (auto DeviceMap : Info.DevicePtrInfoMap) {
9012 if (isa<AllocaInst>(DeviceMap.second.second)) {
9013 auto *LI =
9014 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
9015 Builder.CreateStore(LI, DeviceMap.second.second);
9016 }
9017 }
9018
9019 // If device pointer privatization is required, emit the body of the
9020 // region here. It will have to be duplicated: with and without
9021 // privatization.
9022 InsertPointOrErrorTy AfterIP =
9023 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9024 if (!AfterIP)
9025 return AfterIP.takeError();
9026 Builder.restoreIP(*AfterIP);
9027 }
9028 return Error::success();
9029 };
9030
9031 // If we need device pointer privatization, we need to emit the body of the
9032 // region with no privatization in the 'else' branch of the conditional.
9033 // Otherwise, we don't have to do anything.
9034 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9035 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9036 InsertPointOrErrorTy AfterIP =
9037 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9038 if (!AfterIP)
9039 return AfterIP.takeError();
9040 Builder.restoreIP(*AfterIP);
9041 return Error::success();
9042 };
9043
9044 // Generate code for the closing of the data region.
9045 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9046 ArrayRef<BasicBlock *> DeallocBlocks) {
9047 TargetDataRTArgs RTArgs;
9048 Info.EmitDebug = !MapInfo->Names.empty();
9049 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9050
9051 // Emit the number of elements in the offloading arrays.
9052 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9053
9054 // Source location for the ident struct
9055 if (!SrcLocInfo) {
9056 uint32_t SrcLocStrSize;
9057 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9058 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9059 }
9060
9061 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9062 PointerNum, RTArgs.BasePointersArray,
9063 RTArgs.PointersArray, RTArgs.SizesArray,
9064 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9065 RTArgs.MappersArray};
9066 Function *EndMapperFunc =
9067 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9068
9069 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9070 return Error::success();
9071 };
9072
9073 // We don't have to do anything to close the region if the if clause evaluates
9074 // to false.
9075 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9076 ArrayRef<BasicBlock *> DeallocBlocks) {
9077 return Error::success();
9078 };
9079
9080 Error Err = [&]() -> Error {
9081 if (BodyGenCB) {
9082 Error Err = [&]() {
9083 if (IfCond)
9084 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9085 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9086 }();
9087
9088 if (Err)
9089 return Err;
9090
9091 // If we don't require privatization of device pointers, we emit the body
9092 // in between the runtime calls. This avoids duplicating the body code.
9093 InsertPointOrErrorTy AfterIP =
9094 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9095 if (!AfterIP)
9096 return AfterIP.takeError();
9097 restoreIPandDebugLoc(Builder, *AfterIP);
9098
9099 if (IfCond)
9100 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9101 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9102 }
9103 if (IfCond)
9104 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9105 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9106 }();
9107
9108 if (Err)
9109 return Err;
9110
9111 return Builder.saveIP();
9112}
9113
9116 bool IsGPUDistribute) {
9117 assert((IVSize == 32 || IVSize == 64) &&
9118 "IV size is not compatible with the omp runtime");
9119 RuntimeFunction Name;
9120 if (IsGPUDistribute)
9121 Name = IVSize == 32
9122 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9123 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9124 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9125 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9126 else
9127 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9128 : omp::OMPRTL___kmpc_for_static_init_4u)
9129 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9130 : omp::OMPRTL___kmpc_for_static_init_8u);
9131
9132 return getOrCreateRuntimeFunction(M, Name);
9133}
9134
9136 bool IVSigned) {
9137 assert((IVSize == 32 || IVSize == 64) &&
9138 "IV size is not compatible with the omp runtime");
9139 RuntimeFunction Name = IVSize == 32
9140 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9141 : omp::OMPRTL___kmpc_dispatch_init_4u)
9142 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9143 : omp::OMPRTL___kmpc_dispatch_init_8u);
9144
9145 return getOrCreateRuntimeFunction(M, Name);
9146}
9147
9149 bool IVSigned) {
9150 assert((IVSize == 32 || IVSize == 64) &&
9151 "IV size is not compatible with the omp runtime");
9152 RuntimeFunction Name = IVSize == 32
9153 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9154 : omp::OMPRTL___kmpc_dispatch_next_4u)
9155 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9156 : omp::OMPRTL___kmpc_dispatch_next_8u);
9157
9158 return getOrCreateRuntimeFunction(M, Name);
9159}
9160
9162 bool IVSigned) {
9163 assert((IVSize == 32 || IVSize == 64) &&
9164 "IV size is not compatible with the omp runtime");
9165 RuntimeFunction Name = IVSize == 32
9166 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9167 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9168 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9169 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9170
9171 return getOrCreateRuntimeFunction(M, Name);
9172}
9173
9175 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9176}
9177
9179 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9180 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9181
9182 DISubprogram *NewSP = Func->getSubprogram();
9183 if (!NewSP)
9184 return;
9185
9187
9188 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9189 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9190 // Only use cached variable if the arg number matches. This is important
9191 // so that DIVariable created for privatized variables are not discarded.
9192 if (NewVar && (arg == NewVar->getArg()))
9193 return NewVar;
9194
9196 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9197 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9198 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9199 return NewVar;
9200 };
9201
9202 auto UpdateDebugRecord = [&](auto *DR) {
9203 DILocalVariable *OldVar = DR->getVariable();
9204 unsigned ArgNo = 0;
9205 for (auto Loc : DR->location_ops()) {
9206 auto Iter = ValueReplacementMap.find(Loc);
9207 if (Iter != ValueReplacementMap.end()) {
9208 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9209 ArgNo = std::get<1>(Iter->second) + 1;
9210 }
9211 }
9212 if (ArgNo != 0)
9213 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9214 };
9215
9217 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9218 if (DVR->getNumVariableLocationOps() != 1u) {
9219 DVR->setKillLocation();
9220 return;
9221 }
9222 Value *Loc = DVR->getVariableLocationOp(0u);
9223 BasicBlock *CurBB = DVR->getParent();
9224 BasicBlock *RequiredBB = nullptr;
9225
9226 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9227 RequiredBB = LocInst->getParent();
9228 else if (isa<llvm::Argument>(Loc))
9229 RequiredBB = &DVR->getFunction()->getEntryBlock();
9230
9231 if (RequiredBB && RequiredBB != CurBB) {
9232 assert(!RequiredBB->empty());
9233 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9234 RequiredBB->back().getIterator());
9235 DVRsToDelete.push_back(DVR);
9236 }
9237 };
9238
9239 // The location and scope of variable intrinsics and records still point to
9240 // the parent function of the target region. Update them.
9241 for (Instruction &I : instructions(Func)) {
9243 "Unexpected debug intrinsic");
9244 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9245 UpdateDebugRecord(&DVR);
9246 MoveDebugRecordToCorrectBlock(&DVR);
9247 }
9248 }
9249 for (auto *DVR : DVRsToDelete)
9250 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9251 // An extra argument is passed to the device. Create the debug data for it.
9252 if (OMPBuilder.Config.isTargetDevice()) {
9253 DICompileUnit *CU = NewSP->getUnit();
9254 Module *M = Func->getParent();
9255 DIBuilder DB(*M, true, CU);
9256 DIType *VoidPtrTy =
9257 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9258 unsigned ArgNo = Func->arg_size();
9259 DILocalVariable *Var = DB.createParameterVariable(
9260 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9261 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9262 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9263 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9264 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9265 &(*Func->begin()));
9266 }
9267}
9268
9270 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9271 return cast<Operator>(V)->getOperand(0);
9272 return V;
9273}
9274
9276 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9278 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9281 DebugLoc OutlinedFnLoc) {
9282 SmallVector<Type *> ParameterTypes;
9283 if (OMPBuilder.Config.isTargetDevice()) {
9284 // All parameters to target devices are passed as pointers
9285 // or i64. This assumes 64-bit address spaces/pointers.
9286 for (auto &Arg : Inputs)
9287 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9288 ? Arg->getType()
9289 : Type::getInt64Ty(Builder.getContext()));
9290 } else {
9291 for (auto &Arg : Inputs)
9292 ParameterTypes.push_back(Arg->getType());
9293 }
9294
9295 // The implicit dyn_ptr argument is always the last parameter on both host
9296 // and device so the argument counts match without runtime manipulation.
9297 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9298 ParameterTypes.push_back(PtrTy);
9299
9300 auto BB = Builder.GetInsertBlock();
9301 auto M = BB->getModule();
9302 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9303 /*isVarArg*/ false);
9304 auto Func =
9305 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9306
9307 // Forward target-cpu and target-features function attributes from the
9308 // original function to the new outlined function.
9309 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9310
9311 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9312 if (TargetCpuAttr.isStringAttribute())
9313 Func->addFnAttr(TargetCpuAttr);
9314
9315 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9316 if (TargetFeaturesAttr.isStringAttribute())
9317 Func->addFnAttr(TargetFeaturesAttr);
9318
9319 if (OMPBuilder.Config.isTargetDevice()) {
9320 Value *ExecMode =
9321 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9322 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9323 }
9324
9325 // Save insert point.
9326 IRBuilder<>::InsertPointGuard IPG(Builder);
9327 // We will generate the entries in the outlined function but the debug
9328 // location is still pointing to the parent function, which is the wrong
9329 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9330 // position scoped to the subprogram that will be attached to the outlined
9331 // function, so it is what everything emitted below needs.
9332 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9333
9334 // Generate the region into the function.
9335 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9336 Builder.SetInsertPoint(EntryBB);
9337
9338 // Insert target init call in the device compilation pass. On the host
9339 // (e.g. a non-GPU offload target), there is no runtime init/deinit
9340 // sequence, but the runtime still needs a '<kernel>_kernel_environment'
9341 // global to know how the kernel was configured, so emit it directly.
9342 if (OMPBuilder.Config.isTargetDevice())
9343 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9344 else
9345 OMPBuilder.emitKernelEnvironment(Builder, DefaultAttrs);
9346
9347 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9348
9349 // As we embed the user code in the middle of our target region after we
9350 // generate entry code, we must move what allocas we can into the entry
9351 // block to avoid possible breaking optimisations for device
9352 if (OMPBuilder.Config.isTargetDevice())
9354
9355 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9356 BasicBlock *OutlinedBodyBB =
9357 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9359 Builder.saveIP(),
9360 OpenMPIRBuilder::InsertPointTy(OutlinedBodyBB, OutlinedBodyBB->begin()),
9361 ExitBB);
9362 if (!AfterIP)
9363 return AfterIP.takeError();
9364 Builder.SetInsertPoint(ExitBB);
9365 // The body callback builds the body with its own IRBuilder and cannot reach
9366 // this one directly. But a body holding another OpenMP construct, a nested
9367 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9368 // this Builder pointing at the wrong debug location, or at none at all. The
9369 // epilogue below belongs to the target construct rather than to whatever the
9370 // body emitted last, so re-establish the location the prologue was emitted
9371 // with.
9372 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9373
9374 // Insert target deinit call in the device compilation pass.
9375 if (OMPBuilder.Config.isTargetDevice())
9376 OMPBuilder.createTargetDeinit(Builder);
9377
9378 // Insert return instruction.
9379 Builder.CreateRetVoid();
9380
9381 // New Alloca IP at entry point of created device function.
9382 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9383 auto AllocaIP = Builder.saveIP();
9384
9385 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrDbg());
9386
9387 // Do not include the artificial dyn_ptr argument.
9388 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9389
9391
9392 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9393 // Things like GEP's can come in the form of Constants. Constants and
9394 // ConstantExpr's do not have access to the knowledge of what they're
9395 // contained in, so we must dig a little to find an instruction so we
9396 // can tell if they're used inside of the function we're outlining. We
9397 // also replace the original constant expression with a new instruction
9398 // equivalent; an instruction as it allows easy modification in the
9399 // following loop, as we can now know the constant (instruction) is
9400 // owned by our target function and replaceUsesOfWith can now be invoked
9401 // on it (cannot do this with constants it seems). A brand new one also
9402 // allows us to be cautious as it is perhaps possible the old expression
9403 // was used inside of the function but exists and is used externally
9404 // (unlikely by the nature of a Constant, but still).
9405 // NOTE: We cannot remove dead constants that have been rewritten to
9406 // instructions at this stage, we run the risk of breaking later lowering
9407 // by doing so as we could still be in the process of lowering the module
9408 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9409 // constants we have created rewritten versions of.
9410 if (auto *Const = dyn_cast<Constant>(Input))
9411 convertUsersOfConstantsToInstructions(Const, Func, false);
9412
9413 // Collect users before iterating over them to avoid invalidating the
9414 // iteration in case a user uses Input more than once (e.g. a call
9415 // instruction).
9416 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9417 // Collect all the instructions
9419 if (auto *Instr = dyn_cast<Instruction>(User))
9420 if (Instr->getFunction() == Func)
9421 Instr->replaceUsesOfWith(Input, InputCopy);
9422 };
9423
9424 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9425
9426 // Rewrite uses of input valus to parameters.
9427 for (auto InArg : zip(Inputs, ArgRange)) {
9428 Value *Input = std::get<0>(InArg);
9429 Argument &Arg = std::get<1>(InArg);
9430 Value *InputCopy = nullptr;
9431
9432 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9433 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(),
9434 OpenMPIRBuilder::InsertPointTy(ExitBB, ExitBB->begin()));
9435 if (!AfterIP)
9436 return AfterIP.takeError();
9437 Builder.restoreIP(*AfterIP);
9438 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9439
9440 // In certain cases a Global may be set up for replacement, however, this
9441 // Global may be used in multiple arguments to the kernel, just segmented
9442 // apart, for example, if we have a global array, that is sectioned into
9443 // multiple mappings (technically not legal in OpenMP, but there is a case
9444 // in Fortran for Common Blocks where this is neccesary), we will end up
9445 // with GEP's into this array inside the kernel, that refer to the Global
9446 // but are technically separate arguments to the kernel for all intents and
9447 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9448 // index, it will fold into an referal to the Global, if we then encounter
9449 // this folded GEP during replacement all of the references to the
9450 // Global in the kernel will be replaced with the argument we have generated
9451 // that corresponds to it, including any other GEP's that refer to the
9452 // Global that may be other arguments. This will invalidate all of the other
9453 // preceding mapped arguments that refer to the same global that may be
9454 // separate segments. To prevent this, we defer global processing until all
9455 // other processing has been performed.
9458 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9459 continue;
9460 }
9461
9463 continue;
9464
9465 ReplaceValue(Input, InputCopy, Func);
9466 }
9467
9468 // Replace all of our deferred Input values, currently just Globals.
9469 for (auto Deferred : DeferredReplacement)
9470 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9471
9472 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9473 ValueReplacementMap);
9474 return Func;
9475}
9476/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9477/// of pointers containing shared data between the parent task and the created
9478/// task.
9480 IRBuilderBase &Builder,
9481 Value *TaskWithPrivates,
9482 Type *TaskWithPrivatesTy) {
9483
9484 Type *TaskTy = OMPIRBuilder.Task;
9485 LLVMContext &Ctx = Builder.getContext();
9486 Value *TaskT =
9487 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9488 Value *Shareds = TaskT;
9489 // TaskWithPrivatesTy can be one of the following
9490 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9491 // %struct.privates }
9492 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9493 //
9494 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9495 // its first member has to be the task descriptor. TaskTy is the type of the
9496 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9497 // first member of TaskT, gives us the pointer to shared data.
9498 if (TaskWithPrivatesTy != TaskTy)
9499 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9500 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9501}
9502/// Create an entry point for a target task with the following.
9503/// It'll have the following signature
9504/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9505/// This function is called from emitTargetTask once the
9506/// code to launch the target kernel has been outlined already.
9507/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9508/// into the task structure so that the deferred target task can access this
9509/// data even after the stack frame of the generating task has been rolled
9510/// back. Offloading arrays contain base pointers, pointers, sizes etc
9511/// of the data that the target kernel will access. These in effect are the
9512/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9514 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9515 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9516 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9517
9518 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9519 // This is because PrivatesTy is the type of the structure in which
9520 // we pass the offloading arrays to the deferred target task.
9521 assert((!NumOffloadingArrays || PrivatesTy) &&
9522 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9523 "to privatize");
9524
9525 Module &M = OMPBuilder.M;
9526 // KernelLaunchFunction is the target launch function, i.e.
9527 // the function that sets up kernel arguments and calls
9528 // __tgt_target_kernel to launch the kernel on the device.
9529 //
9530 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9531
9532 // StaleCI is the CallInst which is the call to the outlined
9533 // target kernel launch function. If there are local live-in values
9534 // that the outlined function uses then these are aggregated into a structure
9535 // which is passed as the second argument. If there are no local live-in
9536 // values or if all values used by the outlined kernel are global variables,
9537 // then there's only one argument, the threadID. So, StaleCI can be
9538 //
9539 // %structArg = alloca { ptr, ptr }, align 8
9540 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9541 // store ptr %20, ptr %gep_, align 8
9542 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9543 // store ptr %21, ptr %gep_8, align 8
9544 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9545 //
9546 // OR
9547 //
9548 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9550 StaleCI->getIterator());
9551
9552 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9553
9554 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9555 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9556 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9557
9558 auto ProxyFnTy =
9559 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9560 /* isVarArg */ false);
9561 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9562 ".omp_target_task_proxy_func", M);
9563 Value *ThreadId = ProxyFn->getArg(0);
9564 Value *TaskWithPrivates = ProxyFn->getArg(1);
9565 ThreadId->setName("thread.id");
9566 TaskWithPrivates->setName("task");
9567
9568 bool HasShareds = SharedArgsOperandNo > 0;
9569 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9570 IRBuilder<>::InsertPointGuard IPG(Builder);
9571 BasicBlock *EntryBB =
9572 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9573 Builder.SetInsertPoint(EntryBB);
9574 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9575
9576 SmallVector<Value *> KernelLaunchArgs;
9577 KernelLaunchArgs.reserve(StaleCI->arg_size());
9578 KernelLaunchArgs.push_back(ThreadId);
9579
9580 if (HasOffloadingArrays) {
9581 assert(TaskTy != TaskWithPrivatesTy &&
9582 "If there are offloading arrays to pass to the target"
9583 "TaskTy cannot be the same as TaskWithPrivatesTy");
9584 (void)TaskTy;
9585 Value *Privates =
9586 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9587 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9588 KernelLaunchArgs.push_back(
9589 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9590 }
9591
9592 if (HasShareds) {
9593 auto *ArgStructAlloca =
9594 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9595 assert(ArgStructAlloca &&
9596 "Unable to find the alloca instruction corresponding to arguments "
9597 "for extracted function");
9598 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9599 std::optional<TypeSize> ArgAllocSize =
9600 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9601 assert(ArgStructType && ArgAllocSize &&
9602 "Unable to determine size of arguments for extracted function");
9603 uint64_t StructSize = ArgAllocSize->getFixedValue();
9604
9605 AllocaInst *NewArgStructAlloca =
9606 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9607
9608 Value *SharedsSize = Builder.getInt64(StructSize);
9609
9611 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9612
9613 Builder.CreateMemCpy(
9614 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9615 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9616 KernelLaunchArgs.push_back(NewArgStructAlloca);
9617 }
9618 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9619 Builder.CreateRetVoid();
9620 return ProxyFn;
9621}
9623
9624 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9625 return GEP->getSourceElementType();
9626 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9627 return Alloca->getAllocatedType();
9628
9629 llvm_unreachable("Unhandled Instruction type");
9630 return nullptr;
9631}
9632// This function returns a struct that has at most two members.
9633// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9634// descriptor. The second member, if needed, is a struct containing arrays
9635// that need to be passed to the offloaded target kernel. For example,
9636// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9637// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9638// respectively, then the types created by this function are
9639//
9640// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9641// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9642// %struct.privates }
9643// %struct.task_with_privates is returned by this function.
9644// If there aren't any offloading arrays to pass to the target kernel,
9645// %struct.kmp_task_ompbuilder_t is returned.
9646static StructType *
9648 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9649
9650 if (OffloadingArraysToPrivatize.empty())
9651 return OMPIRBuilder.Task;
9652
9653 SmallVector<Type *, 4> StructFieldTypes;
9654 for (Value *V : OffloadingArraysToPrivatize) {
9655 assert(V->getType()->isPointerTy() &&
9656 "Expected pointer to array to privatize. Got a non-pointer value "
9657 "instead");
9658 Type *ArrayTy = getOffloadingArrayType(V);
9659 assert(ArrayTy && "ArrayType cannot be nullptr");
9660 StructFieldTypes.push_back(ArrayTy);
9661 }
9662 StructType *PrivatesStructTy =
9663 StructType::create(StructFieldTypes, "struct.privates");
9664 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9665 "struct.task_with_privates");
9666}
9668 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9669 TargetRegionEntryInfo &EntryInfo,
9671 Function *&OutlinedFn, Constant *&OutlinedFnID,
9675 DebugLoc OutlinedFnLoc) {
9676
9677 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9678 [&](StringRef EntryFnName) {
9679 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9680 EntryFnName, Inputs, CBFunc,
9681 ArgAccessorFuncCB, OutlinedFnLoc);
9682 };
9683
9684 return OMPBuilder.emitTargetRegionFunction(
9685 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9686 OutlinedFnID);
9687}
9688
9690 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9692 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9693 bool HasNoWait) {
9694
9695 // The following explains the code-gen scenario for the `target` directive. A
9696 // similar scneario is followed for other device-related directives (e.g.
9697 // `target enter data`) but in similar fashion since we only need to emit task
9698 // that encapsulates the proper runtime call.
9699 //
9700 // When we arrive at this function, the target region itself has been
9701 // outlined into the function OutlinedFn.
9702 // So at ths point, for
9703 // --------------------------------------------------------------
9704 // void user_code_that_offloads(...) {
9705 // omp target depend(..) map(from:a) map(to:b) private(i)
9706 // do i = 1, 10
9707 // a(i) = b(i) + n
9708 // }
9709 //
9710 // --------------------------------------------------------------
9711 //
9712 // we have
9713 //
9714 // --------------------------------------------------------------
9715 //
9716 // void user_code_that_offloads(...) {
9717 // %.offload_baseptrs = alloca [2 x ptr], align 8
9718 // %.offload_ptrs = alloca [2 x ptr], align 8
9719 // %.offload_mappers = alloca [2 x ptr], align 8
9720 // ;; target region has been outlined and now we need to
9721 // ;; offload to it via a target task.
9722 // }
9723 // void outlined_device_function(ptr a, ptr b, ptr n) {
9724 // n = *n_ptr;
9725 // do i = 1, 10
9726 // a(i) = b(i) + n
9727 // }
9728 //
9729 // We have to now do the following
9730 // (i) Make an offloading call to outlined_device_function using the OpenMP
9731 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9732 // emitted by emitKernelLaunch
9733 // (ii) Create a task entry point function that calls kernel_launch_function
9734 // and is the entry point for the target task. See
9735 // '@.omp_target_task_proxy_func in the pseudocode below.
9736 // (iii) Create a task with the task entry point created in (ii)
9737 //
9738 // That is we create the following
9739 // struct task_with_privates {
9740 // struct kmp_task_ompbuilder_t task_struct;
9741 // struct privates {
9742 // [2 x ptr] ; baseptrs
9743 // [2 x ptr] ; ptrs
9744 // [2 x i64] ; sizes
9745 // }
9746 // }
9747 // void user_code_that_offloads(...) {
9748 // %.offload_baseptrs = alloca [2 x ptr], align 8
9749 // %.offload_ptrs = alloca [2 x ptr], align 8
9750 // %.offload_sizes = alloca [2 x i64], align 8
9751 //
9752 // %structArg = alloca { ptr, ptr, ptr }, align 8
9753 // %strucArg[0] = a
9754 // %strucArg[1] = b
9755 // %strucArg[2] = &n
9756 //
9757 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9758 // sizeof(kmp_task_ompbuilder_t),
9759 // sizeof(structArg),
9760 // @.omp_target_task_proxy_func,
9761 // ...)
9762 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9763 // sizeof(structArg))
9764 // memcpy(target_task_with_privates->privates->baseptrs,
9765 // offload_baseptrs, sizeof(offload_baseptrs)
9766 // memcpy(target_task_with_privates->privates->ptrs,
9767 // offload_ptrs, sizeof(offload_ptrs)
9768 // memcpy(target_task_with_privates->privates->sizes,
9769 // offload_sizes, sizeof(offload_sizes)
9770 // dependencies_array = ...
9771 // ;; if nowait not present
9772 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9773 // call @__kmpc_omp_task_begin_if0(...)
9774 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9775 // %target_task_with_privates)
9776 // call @__kmpc_omp_task_complete_if0(...)
9777 // }
9778 //
9779 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9780 // ptr %task) {
9781 // %structArg = alloca {ptr, ptr, ptr}
9782 // %task_ptr = getelementptr(%task, 0, 0)
9783 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9784 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9785 //
9786 // %offloading_arrays = getelementptr(%task, 0, 1)
9787 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9788 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9789 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9790 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9791 // %offload_sizes, %structArg)
9792 // }
9793 //
9794 // We need the proxy function because the signature of the task entry point
9795 // expected by kmpc_omp_task is always the same and will be different from
9796 // that of the kernel_launch function.
9797 //
9798 // kernel_launch_function is generated by emitKernelLaunch and has the
9799 // always_inline attribute. For this example, it'll look like so:
9800 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9801 // %offload_sizes, %structArg) alwaysinline {
9802 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9803 // ; load aggregated data from %structArg
9804 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9805 // ; offload_sizes
9806 // call i32 @__tgt_target_kernel(...,
9807 // outlined_device_function,
9808 // ptr %kernel_args)
9809 // }
9810 // void outlined_device_function(ptr a, ptr b, ptr n) {
9811 // n = *n_ptr;
9812 // do i = 1, 10
9813 // a(i) = b(i) + n
9814 // }
9815 //
9816 BasicBlock *TargetTaskBodyBB =
9817 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9818 BasicBlock *TargetTaskAllocaBB =
9819 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9820
9821 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB,
9822 TargetTaskAllocaBB->begin());
9823 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB, TargetTaskBodyBB->begin());
9824
9825 auto OI = std::make_unique<OutlineInfo>();
9826 OI->EntryBB = TargetTaskAllocaBB;
9827 OI->OuterAllocBB = AllocaIP.getBlock();
9828
9829 // Add the thread ID argument.
9831 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9832 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9833
9834 // Generate the task body which will subsequently be outlined.
9835 Builder.restoreIP(TargetTaskBodyIP);
9836 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9837 return Err;
9838
9839 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9840 // it is given. These blocks are enumerated by
9841 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9842 // to be outside the region. In other words, OI.ExitBlock is expected to be
9843 // the start of the region after the outlining. We used to set OI.ExitBlock
9844 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9845 // except when the task body is a single basic block. In that case,
9846 // OI.ExitBlock is set to the single task body block and will get left out of
9847 // the outlining process. So, simply create a new empty block to which we
9848 // uncoditionally branch from where TaskBodyCB left off
9849 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9850 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9851 /*IsFinished=*/true);
9852
9853 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9854 bool NeedsTargetTask = HasNoWait && DeviceID;
9855 if (NeedsTargetTask) {
9856 for (auto *V :
9857 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9858 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9859 RTArgs.SizesArray}) {
9861 OffloadingArraysToPrivatize.push_back(V);
9862 OI->ExcludeArgsFromAggregate.push_back(V);
9863 }
9864 }
9865 }
9866 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9867 DeviceID, OffloadingArraysToPrivatize](
9868 Function &OutlinedFn) mutable {
9869 assert(OutlinedFn.hasOneUse() &&
9870 "there must be a single user for the outlined function");
9871
9872 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9873
9874 // The first argument of StaleCI is always the thread id.
9875 // The next few arguments are the pointers to offloading arrays
9876 // if any. (see OffloadingArraysToPrivatize)
9877 // Finally, all other local values that are live-in into the outlined region
9878 // end up in a structure whose pointer is passed as the last argument. This
9879 // piece of data is passed in the "shared" field of the task structure. So,
9880 // we know we have to pass shareds to the task if the number of arguments is
9881 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9882 // thread id. Further, for safety, we assert that the number of arguments of
9883 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9884 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9885 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9886 assert((!HasShareds ||
9887 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9888 "Wrong number of arguments for StaleCI when shareds are present");
9889 int SharedArgOperandNo =
9890 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9891
9892 StructType *TaskWithPrivatesTy =
9893 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9894 StructType *PrivatesTy = nullptr;
9895
9896 if (!OffloadingArraysToPrivatize.empty())
9897 PrivatesTy =
9898 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9899
9901 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9902 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9903
9904 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9905 << "\n");
9906
9907 Builder.SetInsertPoint(StaleCI);
9908
9909 // Gather the arguments for emitting the runtime call.
9910 uint32_t SrcLocStrSize;
9911 Constant *SrcLocStr =
9913 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9914
9915 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9916 //
9917 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9918 // the DeviceID to the deferred task and also since
9919 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9920 Function *TaskAllocFn =
9921 !NeedsTargetTask
9922 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9924 OMPRTL___kmpc_omp_target_task_alloc);
9925
9926 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9927 // call.
9928 Value *ThreadID = getOrCreateThreadID(Ident);
9929
9930 // Argument - `sizeof_kmp_task_t` (TaskSize)
9931 // Tasksize refers to the size in bytes of kmp_task_t data structure
9932 // plus any other data to be passed to the target task, if any, which
9933 // is packed into a struct. kmp_task_t and the struct so created are
9934 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9935 Value *TaskSize = Builder.getInt64(
9936 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9937
9938 // Argument - `sizeof_shareds` (SharedsSize)
9939 // SharedsSize refers to the shareds array size in the kmp_task_t data
9940 // structure.
9941 Value *SharedsSize = Builder.getInt64(0);
9942 if (HasShareds) {
9943 auto *ArgStructAlloca =
9944 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9945 assert(ArgStructAlloca &&
9946 "Unable to find the alloca instruction corresponding to arguments "
9947 "for extracted function");
9948 std::optional<TypeSize> ArgAllocSize =
9949 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9950 assert(ArgAllocSize &&
9951 "Unable to determine size of arguments for extracted function");
9952 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9953 }
9954
9955 // Argument - `flags`
9956 // Task is tied iff (Flags & 1) == 1.
9957 // Task is untied iff (Flags & 1) == 0.
9958 // Task is final iff (Flags & 2) == 2.
9959 // Task is not final iff (Flags & 2) == 0.
9960 // A target task is not final and is untied.
9961 Value *Flags = Builder.getInt32(0);
9962
9963 // Emit the @__kmpc_omp_task_alloc runtime call
9964 // The runtime call returns a pointer to an area where the task captured
9965 // variables must be copied before the task is run (TaskData)
9966 CallInst *TaskData = nullptr;
9967
9968 SmallVector<llvm::Value *> TaskAllocArgs = {
9969 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
9970 /*flags=*/Flags,
9971 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
9972 /*task_func=*/ProxyFn};
9973
9974 if (NeedsTargetTask) {
9975 assert(DeviceID && "Expected non-empty device ID.");
9976 TaskAllocArgs.push_back(DeviceID);
9977 }
9978
9979 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
9980
9981 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
9982 if (HasShareds) {
9983 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
9985 *this, Builder, TaskData, TaskWithPrivatesTy);
9986 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
9987 SharedsSize);
9988 }
9989 if (!OffloadingArraysToPrivatize.empty()) {
9990 Value *Privates =
9991 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
9992 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
9993 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
9994 [[maybe_unused]] Type *ArrayType =
9995 getOffloadingArrayType(PtrToPrivatize);
9996 assert(ArrayType && "ArrayType cannot be nullptr");
9997
9998 Type *ElementType = PrivatesTy->getElementType(i);
9999 assert(ElementType == ArrayType &&
10000 "ElementType should match ArrayType");
10001 (void)ArrayType;
10002
10003 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
10004 Builder.CreateMemCpy(
10005 Dst, Alignment, PtrToPrivatize, Alignment,
10006 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
10007 }
10008 }
10009
10010 Value *DepArray = nullptr;
10011 Value *NumDeps = nullptr;
10012 if (Dependencies.DepArray) {
10013 DepArray = Dependencies.DepArray;
10014 NumDeps = Dependencies.NumDeps;
10015 } else if (!Dependencies.Deps.empty()) {
10016 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
10017 NumDeps = Builder.getInt32(Dependencies.Deps.size());
10018 }
10019
10020 // ---------------------------------------------------------------
10021 // V5.2 13.8 target construct
10022 // If the nowait clause is present, execution of the target task
10023 // may be deferred. If the nowait clause is not present, the target task is
10024 // an included task.
10025 // ---------------------------------------------------------------
10026 // The above means that the lack of a nowait on the target construct
10027 // translates to '#pragma omp task if(0)'
10028 if (!NeedsTargetTask) {
10029 if (DepArray) {
10030 Function *TaskWaitFn =
10031 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10033 TaskWaitFn,
10034 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10035 /*ndeps=*/NumDeps,
10036 /*dep_list=*/DepArray,
10037 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10038 /*noalias_dep_list=*/
10040 }
10041 // Included task.
10042 Function *TaskBeginFn =
10043 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10044 Function *TaskCompleteFn =
10045 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10046 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10047 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10048 CI->setDebugLoc(StaleCI->getDebugLoc());
10049 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10050 } else if (DepArray) {
10051 // HasNoWait - meaning the task may be deferred. Call
10052 // __kmpc_omp_task_with_deps if there are dependencies,
10053 // else call __kmpc_omp_task
10054 Function *TaskFn =
10055 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10057 TaskFn,
10058 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10059 ConstantInt::get(Builder.getInt32Ty(), 0),
10061 } else {
10062 // Emit the @__kmpc_omp_task runtime call to spawn the task
10063 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10064 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10065 }
10066
10067 Builder.ClearInsertionPoint();
10068 StaleCI->eraseFromParent();
10069 for (Instruction *I : llvm::reverse(ToBeDeleted))
10070 I->eraseFromParent();
10071 };
10072 addOutlineInfo(std::move(OI));
10073
10074 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10075 << *(Builder.GetInsertBlock()) << "\n");
10076 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10077 << *(Builder.GetInsertBlock()->getParent()->getParent())
10078 << "\n");
10079 return Builder.saveIP();
10080}
10081
10083 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10084 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10085 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10086 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10087 if (Error Err =
10088 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10089 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10090 return Err;
10091 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10092 return Error::success();
10093}
10094
10095static void emitTargetCall(
10096 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride,
10101 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10105 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10106 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10107 // Generate a function call to the host fallback implementation of the target
10108 // region. This is called by the host when no offload entry was generated for
10109 // the target region and when the offloading call fails at runtime.
10110 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10112 Builder.restoreIP(IP);
10113 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10114 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10115 FallbackArgs.push_back(
10116 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10117 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10118 return Builder.saveIP();
10119 };
10120
10121 bool HasDependencies = !Dependencies.empty();
10122 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10123
10125
10126 auto TaskBodyCB =
10127 [&](Value *DeviceID, Value *RTLoc,
10128 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10129 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10130 // produce any.
10132 // emitKernelLaunch makes the necessary runtime call to offload the
10133 // kernel. We then outline all that code into a separate function
10134 // ('kernel_launch_function' in the pseudo code above). This function is
10135 // then called by the target task proxy function (see
10136 // '@.omp_target_task_proxy_func' in the pseudo code above)
10137 // "@.omp_target_task_proxy_func' is generated by
10138 // emitTargetTaskProxyFunction.
10139 if (OutlinedFnID && DeviceID)
10140 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10141 EmitTargetCallFallbackCB, KArgs,
10142 DeviceID, RTLoc, TargetTaskAllocaIP);
10143
10144 // We only need to do the outlining if `DeviceID` is set to avoid calling
10145 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10146 // generating the `else` branch of an `if` clause.
10147 //
10148 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10149 // In this case, we execute the host implementation directly.
10150 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10151 }());
10152
10153 OMPBuilder.Builder.restoreIP(AfterIP);
10154 return Error::success();
10155 };
10156
10157 auto &&EmitTargetCallElse =
10158 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10160 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10161 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10162 // produce any.
10164 if (RequiresOuterTargetTask) {
10165 // Arguments that are intended to be directly forwarded to an
10166 // emitKernelLaunch call are pased as nullptr, since
10167 // OutlinedFnID=nullptr results in that call not being done.
10169 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10170 /*RTLoc=*/nullptr, AllocaIP,
10171 Dependencies, EmptyRTArgs, HasNoWait);
10172 }
10173 return EmitTargetCallFallbackCB(Builder.saveIP());
10174 }());
10175
10176 Builder.restoreIP(AfterIP);
10177 return Error::success();
10178 };
10179
10180 auto &&EmitTargetCallThen =
10181 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10183 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10184 Info.HasNoWait = HasNoWait;
10185 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10186
10188 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10189 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10190 /*IsNonContiguous=*/true,
10191 /*ForEndCall=*/false))
10192 return Err;
10193
10194 SmallVector<Value *, 3> NumTeamsC;
10195 for (auto [DefaultVal, RuntimeVal] :
10196 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10197 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10198 : Builder.getInt32(DefaultVal));
10199
10200 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10201 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10202 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10203 if (Clause)
10204 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10205 /*isSigned=*/false);
10206 return Clause;
10207 };
10208 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10209 if (Clause)
10210 Result =
10211 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10212 Result, Clause)
10213 : Clause;
10214 };
10215
10216 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10217 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10218 SmallVector<Value *, 3> NumThreadsC;
10219 Value *MaxThreadsClause =
10220 RuntimeAttrs.TeamsThreadLimit.size() == 1
10221 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10222 : nullptr;
10223
10224 for (auto [TeamsVal, TargetVal] : zip_equal(
10225 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10226 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10227 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10228
10229 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10230 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10231
10232 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10233 }
10234
10235 unsigned NumTargetItems = Info.NumberOfPtrs;
10236 Value *RTLoc = RTLocOverride;
10237 if (!RTLoc) {
10238 uint32_t SrcLocStrSize;
10239 Constant *SrcLocStr =
10240 OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10241 RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10242 llvm::omp::IdentFlag(0), 0);
10243 }
10244
10245 Value *TripCount = RuntimeAttrs.LoopTripCount
10246 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10247 Builder.getInt64Ty(),
10248 /*isSigned=*/false)
10249 : Builder.getInt64(0);
10250
10251 // Request zero groupprivate bytes by default.
10252 if (!DynCGroupMem)
10253 DynCGroupMem = Builder.getInt32(0);
10254
10256 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10257 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10258 DynCGroupMemFallback);
10259
10260 // Assume no error was returned because TaskBodyCB and
10261 // EmitTargetCallFallbackCB don't produce any.
10263 // The presence of certain clauses on the target directive require the
10264 // explicit generation of the target task.
10265 if (RequiresOuterTargetTask)
10266 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10267 RTLoc, AllocaIP, Dependencies,
10268 KArgs.RTArgs, Info.HasNoWait);
10269
10270 return OMPBuilder.emitKernelLaunch(
10271 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10272 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10273 }());
10274
10275 Builder.restoreIP(AfterIP);
10276 return Error::success();
10277 };
10278
10279 // If we don't have an ID for the target region, it means an offload entry
10280 // wasn't created. In this case we just run the host fallback directly and
10281 // ignore any potential 'if' clauses.
10282 if (!OutlinedFnID) {
10283 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10284 return;
10285 }
10286
10287 // If there's no 'if' clause, only generate the kernel launch code path.
10288 if (!IfCond) {
10289 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10290 return;
10291 }
10292
10293 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10294 EmitTargetCallElse, AllocaIP));
10295}
10296
10298 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10299 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10300 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10301 const TargetKernelDefaultAttrs &DefaultAttrs,
10302 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10303 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10306 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10307 bool HasNowait, Value *DynCGroupMem,
10308 OMPDynGroupprivateFallbackType DynCGroupMemFallback, DebugLoc OutlinedFnLoc,
10309 Value *RTLocOverride) {
10310
10311 if (!updateToLocation(Loc))
10312 return InsertPointTy();
10313
10314 Builder.restoreIP(CodeGenIP);
10315
10316 Function *OutlinedFn;
10317 Constant *OutlinedFnID = nullptr;
10318 // The target region is outlined into its own function. The LLVM IR for
10319 // the target region itself is generated using the callbacks CBFunc
10320 // and ArgAccessorFuncCB
10322 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10323 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10324 return Err;
10325
10326 // If we are not on the target device, then we need to generate code
10327 // to make a remote call (offload) to the previously outlined function
10328 // that represents the target region. Do that now.
10329 if (!Config.isTargetDevice())
10330 emitTargetCall(*this, Builder, RTLocOverride, AllocaIP, DeallocBlocks, Info,
10331 DefaultAttrs, RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID,
10332 Inputs, GenMapInfoCB, CustomMapperCB, Dependencies,
10333 HasNowait, DynCGroupMem, DynCGroupMemFallback);
10334 return Builder.saveIP();
10335}
10336
10337std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10338 StringRef FirstSeparator,
10339 StringRef Separator) {
10340 SmallString<128> Buffer;
10341 llvm::raw_svector_ostream OS(Buffer);
10342 StringRef Sep = FirstSeparator;
10343 for (StringRef Part : Parts) {
10344 OS << Sep << Part;
10345 Sep = Separator;
10346 }
10347 return OS.str().str();
10348}
10349
10350std::string
10352 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10353 Config.separator());
10354}
10355
10357 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10358 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10359 if (Elem.second) {
10360 assert(Elem.second->getValueType() == Ty &&
10361 "OMP internal variable has different type than requested");
10362 } else {
10363 // TODO: investigate the appropriate linkage type used for the global
10364 // variable for possibly changing that to internal or private, or maybe
10365 // create different versions of the function for different OMP internal
10366 // variables.
10367 const DataLayout &DL = M.getDataLayout();
10368 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10369 // default global AS is 1.
10370 // See double-target-call-with-declare-target.f90 and
10371 // declare-target-vars-in-target-region.f90 libomptarget
10372 // tests.
10373 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10374 : M.getTargetTriple().isAMDGPU()
10375 ? 0
10376 : DL.getDefaultGlobalsAddressSpace();
10377 auto Linkage = this->M.getTargetTriple().isWasm()
10380 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10381 Constant::getNullValue(Ty), Elem.first(),
10382 /*InsertBefore=*/nullptr,
10383 GlobalValue::NotThreadLocal, AddressSpaceVal);
10384 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10385 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10386 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10387 Elem.second = GV;
10388 }
10389
10390 return Elem.second;
10391}
10392
10393Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10394 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10395 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10396 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10397}
10398
10400 LLVMContext &Ctx = Builder.getContext();
10401 Value *Null =
10402 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10403 Value *SizeGep =
10404 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10405 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10406 return SizePtrToInt;
10407}
10408
10411 std::string VarName) {
10412 llvm::Constant *MaptypesArrayInit =
10413 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10414 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10415 M, MaptypesArrayInit->getType(),
10416 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10417 VarName);
10418 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10419 return MaptypesArrayGlobal;
10420}
10421
10423 InsertPointTy AllocaIP,
10424 unsigned NumOperands,
10425 struct MapperAllocas &MapperAllocas) {
10426 if (!updateToLocation(Loc))
10427 return;
10428
10429 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10430 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10431 Builder.restoreIP(AllocaIP);
10432 AllocaInst *ArgsBase = Builder.CreateAlloca(
10433 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10434 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10435 ".offload_ptrs");
10436 AllocaInst *ArgSizes = Builder.CreateAlloca(
10437 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10439 MapperAllocas.ArgsBase = ArgsBase;
10440 MapperAllocas.Args = Args;
10441 MapperAllocas.ArgSizes = ArgSizes;
10442}
10443
10445 Function *MapperFunc, Value *SrcLocInfo,
10446 Value *MaptypesArg, Value *MapnamesArg,
10448 int64_t DeviceID, unsigned NumOperands) {
10449 if (!updateToLocation(Loc))
10450 return;
10451
10452 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10453 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10454 Value *ArgsBaseGEP =
10455 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10456 {Builder.getInt32(0), Builder.getInt32(0)});
10457 Value *ArgsGEP =
10458 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10459 {Builder.getInt32(0), Builder.getInt32(0)});
10460 Value *ArgSizesGEP =
10461 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10462 {Builder.getInt32(0), Builder.getInt32(0)});
10463 Value *NullPtr =
10464 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10465 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10466 Builder.getInt32(NumOperands),
10467 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10468 MaptypesArg, MapnamesArg, NullPtr});
10469}
10470
10472 TargetDataRTArgs &RTArgs,
10473 TargetDataInfo &Info,
10474 bool ForEndCall) {
10475 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10476 "expected region end call to runtime only when end call is separate");
10477 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10478 auto VoidPtrTy = UnqualPtrTy;
10479 auto VoidPtrPtrTy = UnqualPtrTy;
10480 auto Int64Ty = Type::getInt64Ty(M.getContext());
10481 auto Int64PtrTy = UnqualPtrTy;
10482
10483 if (!Info.NumberOfPtrs) {
10484 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10485 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10486 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10487 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10488 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10489 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10490 return;
10491 }
10492
10493 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10494 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10495 Info.RTArgs.BasePointersArray,
10496 /*Idx0=*/0, /*Idx1=*/0);
10497 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10498 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10499 /*Idx0=*/0,
10500 /*Idx1=*/0);
10501 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10502 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10503 /*Idx0=*/0, /*Idx1=*/0);
10504 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10505 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10506 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10507 : Info.RTArgs.MapTypesArray,
10508 /*Idx0=*/0,
10509 /*Idx1=*/0);
10510
10511 // Only emit the mapper information arrays if debug information is
10512 // requested.
10513 if (!Info.EmitDebug)
10514 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10515 else
10516 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10517 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10518 /*Idx0=*/0,
10519 /*Idx1=*/0);
10520 // If there is no user-defined mapper, set the mapper array to nullptr to
10521 // avoid an unnecessary data privatization
10522 if (!Info.HasMapper)
10523 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10524 else
10525 RTArgs.MappersArray =
10526 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10527}
10528
10530 InsertPointTy CodeGenIP,
10531 MapInfosTy &CombinedInfo,
10532 TargetDataInfo &Info) {
10534 CombinedInfo.NonContigInfo;
10535
10536 // Build an array of struct descriptor_dim and then assign it to
10537 // offload_args.
10538 //
10539 // struct descriptor_dim {
10540 // uint64_t offset;
10541 // uint64_t count;
10542 // uint64_t stride
10543 // };
10544 Type *Int64Ty = Builder.getInt64Ty();
10546 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10547 "struct.descriptor_dim");
10548
10549 enum { OffsetFD = 0, CountFD, StrideFD };
10550 // We need two index variable here since the size of "Dims" is the same as
10551 // the size of Components, however, the size of offset, count, and stride is
10552 // equal to the size of base declaration that is non-contiguous.
10553 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10554 // Skip emitting ir if dimension size is 1 since it cannot be
10555 // non-contiguous.
10556 if (NonContigInfo.Dims[I] == 1)
10557 continue;
10558 Builder.restoreIP(AllocaIP);
10559 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10560 AllocaInst *DimsAddr =
10561 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10562 Builder.restoreIP(CodeGenIP);
10563 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10564 unsigned RevIdx = EE - II - 1;
10565 Value *DimsLVal = Builder.CreateInBoundsGEP(
10566 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10567 // Offset
10568 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10569 Builder.CreateAlignedStore(
10570 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10571 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10572 // Count
10573 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10574 Builder.CreateAlignedStore(
10575 NonContigInfo.Counts[L][RevIdx], CountLVal,
10576 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10577 // Stride
10578 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10579 Builder.CreateAlignedStore(
10580 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10581 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10582 }
10583 // args[I] = &dims
10584 Builder.restoreIP(CodeGenIP);
10585 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10586 DimsAddr, Builder.getPtrTy());
10587 Value *P = Builder.CreateConstInBoundsGEP2_32(
10588 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10589 Info.RTArgs.PointersArray, 0, I);
10590 Builder.CreateAlignedStore(
10591 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10592 ++L;
10593 }
10594}
10595
10596void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10597 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10598 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10599 BasicBlock *ExitBB, bool IsInit) {
10600 StringRef Prefix = IsInit ? ".init" : ".del";
10601
10602 // Evaluate if this is an array section.
10604 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10605 Value *IsArray =
10606 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10607 Value *DeleteBit = Builder.CreateAnd(
10608 MapType,
10609 Builder.getInt64(
10610 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10611 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10612 Value *DeleteCond;
10613 Value *Cond;
10614 if (IsInit) {
10615 // base != begin?
10616 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10617 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10618 DeleteCond = Builder.CreateIsNull(
10619 DeleteBit,
10620 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10621 } else {
10622 Cond = IsArray;
10623 DeleteCond = Builder.CreateIsNotNull(
10624 DeleteBit,
10625 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10626 }
10627 Cond = Builder.CreateAnd(Cond, DeleteCond);
10628 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10629
10630 emitBlock(BodyBB, MapperFn);
10631 // Get the array size by multiplying element size and element number (i.e., \p
10632 // Size).
10633 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10634 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10635 // memory allocation/deletion purpose only.
10636 Value *MapTypeArg = Builder.CreateAnd(
10637 MapType,
10638 Builder.getInt64(
10639 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10640 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10641 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10642 MapTypeArg = Builder.CreateOr(
10643 MapTypeArg,
10644 Builder.getInt64(
10645 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10646 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10647
10648 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10649 // data structure.
10650 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10651 ArraySize, MapTypeArg, MapName};
10653 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10654 OffloadingArgs);
10655}
10656
10659 llvm::Value *BeginArg)>
10660 GenMapInfoCB,
10661 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10662 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10663 SmallVector<Type *> Params;
10664 Params.emplace_back(Builder.getPtrTy());
10665 Params.emplace_back(Builder.getPtrTy());
10666 Params.emplace_back(Builder.getPtrTy());
10667 Params.emplace_back(Builder.getInt64Ty());
10668 Params.emplace_back(Builder.getInt64Ty());
10669 Params.emplace_back(Builder.getPtrTy());
10670
10671 auto *FnTy =
10672 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10673
10674 SmallString<64> TyStr;
10675 raw_svector_ostream Out(TyStr);
10676 Function *MapperFn =
10678 MapperFn->addFnAttr(Attribute::NoInline);
10679 MapperFn->addFnAttr(Attribute::NoUnwind);
10680 MapperFn->addParamAttr(0, Attribute::NoUndef);
10681 MapperFn->addParamAttr(1, Attribute::NoUndef);
10682 MapperFn->addParamAttr(2, Attribute::NoUndef);
10683 MapperFn->addParamAttr(3, Attribute::NoUndef);
10684 MapperFn->addParamAttr(4, Attribute::NoUndef);
10685 MapperFn->addParamAttr(5, Attribute::NoUndef);
10686
10687 // Start the mapper function code generation.
10688 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10690 Builder.SetInsertPoint(EntryBB);
10691 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10692
10693 Value *MapperHandle = MapperFn->getArg(0);
10694 Value *BaseIn = MapperFn->getArg(1);
10695 Value *BeginIn = MapperFn->getArg(2);
10696 Value *Size = MapperFn->getArg(3);
10697 Value *MapType = MapperFn->getArg(4);
10698 Value *MapName = MapperFn->getArg(5);
10699
10700 // Compute the starting and end addresses of array elements.
10701 // Prepare common arguments for array initiation and deletion.
10702 // Convert the size in bytes into the number of array elements.
10703 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10704 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10705 Value *PtrBegin = BeginIn;
10706 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10707
10708 // Emit array initiation if this is an array section and \p MapType indicates
10709 // that memory allocation is required.
10710 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10711 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10712 MapType, MapName, ElementSize, HeadBB,
10713 /*IsInit=*/true);
10714
10715 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10716
10717 // Emit the loop header block.
10718 emitBlock(HeadBB, MapperFn);
10719 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10720 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10721 // Evaluate whether the initial condition is satisfied.
10722 Value *IsEmpty =
10723 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10724 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10725
10726 // Emit the loop body block.
10727 emitBlock(BodyBB, MapperFn);
10728 BasicBlock *LastBB = BodyBB;
10729 PHINode *PtrPHI =
10730 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10731 PtrPHI->addIncoming(PtrBegin, HeadBB);
10732
10733 // Get map clause information. Fill up the arrays with all mapped variables.
10734 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10735 if (!Info)
10736 return Info.takeError();
10737
10738 // Call the runtime API __tgt_mapper_num_components to get the number of
10739 // pre-existing components.
10740 Value *OffloadingArgs[] = {MapperHandle};
10741 Value *PreviousSize = createRuntimeFunctionCall(
10742 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10743 OffloadingArgs);
10744 Value *ShiftedPreviousSize =
10745 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10746
10747 // Fill up the runtime mapper handle for all components.
10748 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10749 Value *CurBaseArg = Info->BasePointers[I];
10750 Value *CurBeginArg = Info->Pointers[I];
10751 Value *CurSizeArg = Info->Sizes[I];
10752 Value *CurNameArg = Info->Names.size()
10753 ? Info->Names[I]
10754 : Constant::getNullValue(Builder.getPtrTy());
10755
10756 Value *OriMapType = Builder.getInt64(
10757 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10758 Info->Types[I]));
10759 auto RawType =
10760 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10761 Info->Types[I]);
10762 constexpr uint64_t MemberOfMask =
10763 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10764 constexpr uint64_t AttachBit =
10765 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10766 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10767
10768 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10769 // current array element (N = __tgt_mapper_num_components() at loop body
10770 // start).
10771 //
10772 // Example 1:
10773 // struct S { int x; int *p; };
10774 //
10775 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10776 // use: S arr[2]; ... map(arr)
10777 // entries per element:
10778 //
10779 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10780 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10781 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10782 //
10783 // Example 2:
10784 // struct S1 { int x; int y; };
10785 // struct S2 { int z; S1 *s1p; };
10786 //
10787 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10788 // s2.s1p->y)
10789 // use: S2 arr[2]; ... map(arr)
10790 // entries per element:
10791 //
10792 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10793 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10794 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10795 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10796 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10797 //
10798 // x/y carry inner MEMBER_OF(2)
10799 // which is shifted by N to become MEMBER_OF(N+2).
10800 //
10801 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10802 // the combined ALLOC entry for the s1p->x..y block, and the individual
10803 // x/y entries that are MEMBER_OF that block, all describe storage
10804 // reached through the attach ptr arr[i].s1p.
10805 //
10806 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10807 // linking them to the parent struct:
10808 //
10809 // * (*) Entries with HasAttachPtr: they represent pointee data that
10810 // occupies a different storage block than the struct being mapped, so
10811 // they are not a member of it. They may still be MEMBER_OF an entry
10812 // within that pointee block, in which case those pre-existing bits are
10813 // shifted -- see (***).
10814 // * (**) ATTACH entries: they are not a member of anything — they just
10815 // link a ptr to its ptee.
10816 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10817 // its pre-shaped entries already carry their final MEMBER_OF bits.
10818 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10819 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10820 // it.
10821 //
10822 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10823 // s1p->x/y entries above), those bits are still shifted by N.
10824 Value *MemberMapType;
10825 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10826 Info->HasAttachPtr[I]) {
10827 if (RawType & MemberOfMask)
10828 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10829 else
10830 MemberMapType = OriMapType;
10831 } else {
10832 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10833 }
10834
10835 // Combine the map type inherited from user-defined mapper with that
10836 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10837 // bits of the \a MapType, which is the input argument of the mapper
10838 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10839 // bits of MemberMapType.
10840 // [OpenMP 5.0], 1.2.6. map-type decay.
10841 // | alloc | to | from | tofrom | release | delete
10842 // ----------------------------------------------------------
10843 // alloc | alloc | alloc | alloc | alloc | release | delete
10844 // to | alloc | to | alloc | to | release | delete
10845 // from | alloc | alloc | from | from | release | delete
10846 // tofrom | alloc | to | from | tofrom | release | delete
10847 Value *LeftToFrom = Builder.CreateAnd(
10848 MapType,
10849 Builder.getInt64(
10850 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10851 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10852 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10853 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10854 BasicBlock *AllocElseBB =
10855 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10856 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10857 BasicBlock *ToElseBB =
10858 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10859 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10860 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10861 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10862 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10863 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10864 emitBlock(AllocBB, MapperFn);
10865 Value *AllocMapType = Builder.CreateAnd(
10866 MemberMapType,
10867 Builder.getInt64(
10868 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10869 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10870 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10871 Builder.CreateBr(EndBB);
10872 emitBlock(AllocElseBB, MapperFn);
10873 Value *IsTo = Builder.CreateICmpEQ(
10874 LeftToFrom,
10875 Builder.getInt64(
10876 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10877 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10878 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10879 // In case of to, clear OMP_MAP_FROM.
10880 emitBlock(ToBB, MapperFn);
10881 Value *ToMapType = Builder.CreateAnd(
10882 MemberMapType,
10883 Builder.getInt64(
10884 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10885 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10886 Builder.CreateBr(EndBB);
10887 emitBlock(ToElseBB, MapperFn);
10888 Value *IsFrom = Builder.CreateICmpEQ(
10889 LeftToFrom,
10890 Builder.getInt64(
10891 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10892 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10893 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10894 // In case of from, clear OMP_MAP_TO.
10895 emitBlock(FromBB, MapperFn);
10896 Value *FromMapType = Builder.CreateAnd(
10897 MemberMapType,
10898 Builder.getInt64(
10899 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10900 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10901 // In case of tofrom, do nothing.
10902 emitBlock(EndBB, MapperFn);
10903 LastBB = EndBB;
10904 PHINode *CurMapType =
10905 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10906 CurMapType->addIncoming(AllocMapType, AllocBB);
10907 CurMapType->addIncoming(ToMapType, ToBB);
10908 CurMapType->addIncoming(FromMapType, FromBB);
10909 CurMapType->addIncoming(MemberMapType, ToElseBB);
10910
10911 // Propagate map-type-modifying bits from the outer map clause to each map
10912 // inserted by the mapper.
10913 //
10914 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10915 // list item from the map clause and to apply the clauses specified in the
10916 // declared mapper to the construct on which the map clause appears...
10917 // If any modifier with the map-type-modifying property appears in the map
10918 // clause then the effect is as if that modifier appears in each map clause
10919 // specified in the declared mapper.
10920 //
10921 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10922 //
10923 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10924 //
10925 // PRESENT is propagated only to entries that have an attach ptr
10926 // (HasAttachPtr): the pointee data, which occupies a different storage
10927 // block than the struct being mapped and so is not covered by the
10928 // present-check on the struct's own storage. A present modifier on the
10929 // outer clause must still require that pointee to be present on the device.
10930 //
10931 // This is gated on \p PropagatePresentToPointee (set by callers only for
10932 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10933 // applying to the pointee: the spec committee confirmed the divergence
10934 // between the present "motion" modifier (to/from) and the present map-type
10935 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10936 // so for 5.2 present is ignored for the pointee for both map and to/from.
10937 //
10938 // TODO: PRESENT should also be propagated to the struct's own members
10939 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10940 // member triggers the present-check. We cannot do that yet: while pointer
10941 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10942 // the whole struct (including the pointer's storage), so propagating
10943 // PRESENT to it would wrongly require the pointer's pointee to be present.
10944 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10945 // attach-style maps throughout.
10946 uint64_t ModifierBits =
10947 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10948 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10949 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10950 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10951 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10952 ModifierBits |=
10953 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10954 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10955 Value *ImportedModifierBits =
10956 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
10957 Value *CurMapTypeWithModifiers = Builder.CreateOr(
10958 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
10959
10960 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
10961 // reserved for the attach(always) map-type modifier, and other modifier
10962 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
10963 Value *FinalMapType =
10964 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
10965
10966 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
10967 CurSizeArg, FinalMapType, CurNameArg};
10968
10969 auto ChildMapperFn = CustomMapperCB(I);
10970 if (!ChildMapperFn)
10971 return ChildMapperFn.takeError();
10972 if (*ChildMapperFn) {
10973 // Call the corresponding mapper function.
10974 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
10975 ->setDoesNotThrow();
10976 } else {
10977 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10978 // data structure.
10980 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10981 OffloadingArgs);
10982 }
10983 }
10984
10985 // Update the pointer to point to the next element that needs to be mapped,
10986 // and check whether we have mapped all elements.
10987 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
10988 "omp.arraymap.next");
10989 PtrPHI->addIncoming(PtrNext, LastBB);
10990 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
10991 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
10992 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
10993
10994 emitBlock(ExitBB, MapperFn);
10995 // Emit array deletion if this is an array section and \p MapType indicates
10996 // that deletion is required.
10997 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10998 MapType, MapName, ElementSize, DoneBB,
10999 /*IsInit=*/false);
11000
11001 // Emit the function exit block.
11002 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
11003
11004 Builder.CreateRetVoid();
11005 return MapperFn;
11006}
11007
11009 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
11010 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
11011 bool IsNonContiguous,
11012 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
11013
11014 // Reset the array information.
11015 Info.clearArrayInfo();
11016 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
11017
11018 if (Info.NumberOfPtrs == 0)
11019 return Error::success();
11020
11021 Builder.restoreIP(AllocaIP);
11022 // Detect if we have any capture size requiring runtime evaluation of the
11023 // size so that a constant array could be eventually used.
11024 ArrayType *PointerArrayType =
11025 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
11026
11027 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
11028 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
11029
11030 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11031 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11032 AllocaInst *MappersArray = Builder.CreateAlloca(
11033 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11034 Info.RTArgs.MappersArray = MappersArray;
11035
11036 // If we don't have any VLA types or other types that require runtime
11037 // evaluation, we can use a constant array for the map sizes, otherwise we
11038 // need to fill up the arrays as we do for the pointers.
11039 Type *Int64Ty = Builder.getInt64Ty();
11040 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11041 ConstantInt::get(Int64Ty, 0));
11042 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11043 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11044 bool IsNonContigEntry =
11045 IsNonContiguous &&
11046 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11047 CombinedInfo.Types[I] &
11048 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11049 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11050 // descriptor_dim records), not the byte size.
11051 if (IsNonContigEntry) {
11052 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11053 "Index must be in-bounds for NON_CONTIG Dims array");
11054 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11055 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11056 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11057 continue;
11058 }
11059 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11060 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11061 ConstSizes[I] = CI;
11062 continue;
11063 }
11064 }
11065 RuntimeSizes.set(I);
11066 }
11067
11068 if (RuntimeSizes.all()) {
11069 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11070 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11071 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11072 restoreIPandDebugLoc(Builder, CodeGenIP);
11073 } else {
11074 auto *SizesArrayInit = ConstantArray::get(
11075 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11076 std::string Name = createPlatformSpecificName({"offload_sizes"});
11077 auto *SizesArrayGbl =
11078 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11079 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11080 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11081
11082 if (!RuntimeSizes.any()) {
11083 Info.RTArgs.SizesArray = SizesArrayGbl;
11084 } else {
11085 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11086 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11087 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11088 AllocaInst *Buffer = Builder.CreateAlloca(
11089 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11090 Buffer->setAlignment(OffloadSizeAlign);
11091 restoreIPandDebugLoc(Builder, CodeGenIP);
11092 Builder.CreateMemCpy(
11093 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11094 SizesArrayGbl, OffloadSizeAlign,
11095 Builder.getIntN(
11096 IndexSize,
11097 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11098
11099 Info.RTArgs.SizesArray = Buffer;
11100 }
11101 restoreIPandDebugLoc(Builder, CodeGenIP);
11102 }
11103
11104 // The map types are always constant so we don't need to generate code to
11105 // fill arrays. Instead, we create an array constant.
11107 for (auto mapFlag : CombinedInfo.Types)
11108 Mapping.push_back(
11109 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11110 mapFlag));
11111 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11112 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11113 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11114
11115 // The information types are only built if provided.
11116 if (!CombinedInfo.Names.empty()) {
11117 auto *MapNamesArrayGbl = createOffloadMapnames(
11118 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11119 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11120 Info.EmitDebug = true;
11121 } else {
11122 Info.RTArgs.MapNamesArray =
11124 Info.EmitDebug = false;
11125 }
11126
11127 // If there's a present map type modifier, it must not be applied to the end
11128 // of a region, so generate a separate map type array in that case.
11129 if (Info.separateBeginEndCalls()) {
11130 bool EndMapTypesDiffer = false;
11131 for (uint64_t &Type : Mapping) {
11132 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11133 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11134 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11135 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11136 EndMapTypesDiffer = true;
11137 }
11138 }
11139 if (EndMapTypesDiffer) {
11140 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11141 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11142 }
11143 }
11144
11145 PointerType *PtrTy = Builder.getPtrTy();
11146 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11147 Value *BPVal = CombinedInfo.BasePointers[I];
11148 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11149 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11150 0, I);
11151 Builder.CreateAlignedStore(BPVal, BP,
11152 M.getDataLayout().getPrefTypeAlign(PtrTy));
11153
11154 if (Info.requiresDevicePointerInfo()) {
11155 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11156 CodeGenIP = Builder.saveIP();
11157 Builder.restoreIP(AllocaIP);
11158 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11159 restoreIPandDebugLoc(Builder, CodeGenIP);
11160 if (DeviceAddrCB)
11161 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11162 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11163 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11164 if (DeviceAddrCB)
11165 DeviceAddrCB(I, BP);
11166 }
11167 }
11168
11169 Value *PVal = CombinedInfo.Pointers[I];
11170 Value *P = Builder.CreateConstInBoundsGEP2_32(
11171 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11172 I);
11173 // TODO: Check alignment correct.
11174 Builder.CreateAlignedStore(PVal, P,
11175 M.getDataLayout().getPrefTypeAlign(PtrTy));
11176
11177 if (RuntimeSizes.test(I)) {
11178 Value *S = Builder.CreateConstInBoundsGEP2_32(
11179 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11180 /*Idx0=*/0,
11181 /*Idx1=*/I);
11182 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11183 Int64Ty,
11184 /*isSigned=*/true),
11185 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11186 }
11187 // Fill up the mapper array.
11188 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11189 Value *MFunc = ConstantPointerNull::get(PtrTy);
11190
11191 auto CustomMFunc = CustomMapperCB(I);
11192 if (!CustomMFunc)
11193 return CustomMFunc.takeError();
11194 if (*CustomMFunc)
11195 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11196
11197 Value *MAddr = Builder.CreateInBoundsGEP(
11198 PointerArrayType, MappersArray,
11199 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11200 Builder.CreateAlignedStore(
11201 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11202 }
11203
11204 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11205 Info.NumberOfPtrs == 0)
11206 return Error::success();
11207 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11208 return Error::success();
11209}
11210
11212 BasicBlock *CurBB = Builder.GetInsertBlock();
11213
11214 if (!CurBB || CurBB->hasTerminator()) {
11215 // If there is no insert point or the previous block is already
11216 // terminated, don't touch it.
11217 } else {
11218 // Otherwise, create a fall-through branch.
11219 Builder.CreateBr(Target);
11220 }
11221
11222 Builder.ClearInsertionPoint();
11223}
11224
11226 bool IsFinished) {
11227 BasicBlock *CurBB = Builder.GetInsertBlock();
11228
11229 // Fall out of the current block (if necessary).
11230 emitBranch(BB);
11231
11232 if (IsFinished && BB->use_empty()) {
11233 BB->eraseFromParent();
11234 return;
11235 }
11236
11237 // Place the block after the current block, if possible, or else at
11238 // the end of the function.
11239 if (CurBB && CurBB->getParent())
11240 CurFn->insert(std::next(CurBB->getIterator()), BB);
11241 else
11242 CurFn->insert(CurFn->end(), BB);
11243 Builder.SetInsertPoint(BB);
11244}
11245
11247 BodyGenCallbackTy ElseGen,
11248 InsertPointTy AllocaIP,
11249 ArrayRef<BasicBlock *> DeallocBlocks) {
11250 // If the condition constant folds and can be elided, try to avoid emitting
11251 // the condition and the dead arm of the if/else.
11252 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11253 auto CondConstant = CI->getSExtValue();
11254 if (CondConstant)
11255 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11256
11257 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11258 }
11259
11260 Function *CurFn = Builder.GetInsertBlock()->getParent();
11261
11262 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11263 // emit the conditional branch.
11264 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11265 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11266 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11267 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11268 // Emit the 'then' code.
11269 emitBlock(ThenBlock, CurFn);
11270 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11271 return Err;
11272 emitBranch(ContBlock);
11273 // Emit the 'else' code if present.
11274 // There is no need to emit line number for unconditional branch.
11275 emitBlock(ElseBlock, CurFn);
11276 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11277 return Err;
11278 // There is no need to emit line number for unconditional branch.
11279 emitBranch(ContBlock);
11280 // Emit the continuation block for code after the if.
11281 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11282 return Error::success();
11283}
11284
11285bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11286 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11289 "Unexpected Atomic Ordering.");
11290
11291 bool Flush = false;
11293
11294 switch (AK) {
11295 case Read:
11298 FlushAO = AtomicOrdering::Acquire;
11299 Flush = true;
11300 }
11301 break;
11302 case Write:
11303 case Compare:
11304 case Update:
11307 FlushAO = AtomicOrdering::Release;
11308 Flush = true;
11309 }
11310 break;
11311 case Capture:
11312 switch (AO) {
11314 FlushAO = AtomicOrdering::Acquire;
11315 Flush = true;
11316 break;
11318 FlushAO = AtomicOrdering::Release;
11319 Flush = true;
11320 break;
11324 Flush = true;
11325 break;
11326 default:
11327 // do nothing - leave silently.
11328 break;
11329 }
11330 }
11331
11332 if (Flush) {
11333 // Currently Flush RT call still doesn't take memory_ordering, so for when
11334 // that happens, this tries to do the resolution of which atomic ordering
11335 // to use with but issue the flush call
11336 // TODO: pass `FlushAO` after memory ordering support is added
11337 (void)FlushAO;
11338 emitFlush(Loc);
11339 }
11340
11341 // for AO == AtomicOrdering::Monotonic and all other case combinations
11342 // do nothing
11343 return Flush;
11344}
11345
11349 AtomicOrdering AO, InsertPointTy AllocaIP) {
11350 if (!updateToLocation(Loc))
11351 return Loc.IP;
11352
11353 assert(X.Var->getType()->isPointerTy() &&
11354 "OMP Atomic expects a pointer to target memory");
11355 Type *XElemTy = X.ElemTy;
11356 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11357 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11358 "OMP atomic read expected a scalar type");
11359
11360 Value *XRead = nullptr;
11361
11362 if (XElemTy->isIntegerTy()) {
11363 LoadInst *XLD =
11364 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11365 XLD->setAtomic(AO);
11366 XRead = cast<Value>(XLD);
11367 } else if (XElemTy->isStructTy()) {
11368 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11369 // target does not support `atomicrmw` of the size of the struct
11370 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11371 OldVal->setAtomic(AO);
11372 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11373 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11374 OpenMPIRBuilder::AtomicInfo atomicInfo(
11375 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11376 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11377 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11378 XRead = AtomicLoadRes.first;
11379 OldVal->eraseFromParent();
11380 } else {
11381 // We need to perform atomic op as integer
11382 IntegerType *IntCastTy =
11383 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11384 LoadInst *XLoad =
11385 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11386 XLoad->setAtomic(AO);
11387 if (XElemTy->isFloatingPointTy()) {
11388 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11389 } else {
11390 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11391 }
11392 }
11393 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11394 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11395 return Builder.saveIP();
11396}
11397
11400 AtomicOpValue &X, Value *Expr,
11401 AtomicOrdering AO, InsertPointTy AllocaIP) {
11402 if (!updateToLocation(Loc))
11403 return Loc.IP;
11404
11405 assert(X.Var->getType()->isPointerTy() &&
11406 "OMP Atomic expects a pointer to target memory");
11407 Type *XElemTy = X.ElemTy;
11408 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11409 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11410 "OMP atomic write expected a scalar type");
11411
11412 if (XElemTy->isIntegerTy()) {
11413 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11414 XSt->setAtomic(AO);
11415 } else if (XElemTy->isStructTy()) {
11416 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11417 const DataLayout &DL = OldVal->getModule()->getDataLayout();
11418 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11419 OpenMPIRBuilder::AtomicInfo atomicInfo(
11420 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11421 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11422 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11423 OldVal->eraseFromParent();
11424 } else {
11425 // We need to bitcast and perform atomic op as integers
11426 IntegerType *IntCastTy =
11427 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11428 Value *ExprCast =
11429 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11430 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11431 XSt->setAtomic(AO);
11432 }
11433
11434 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11435 return Builder.saveIP();
11436}
11437
11440 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11441 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11442 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11443 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11444 if (!updateToLocation(Loc))
11445 return Loc.IP;
11446
11447 LLVM_DEBUG({
11448 Type *XTy = X.Var->getType();
11449 assert(XTy->isPointerTy() &&
11450 "OMP Atomic expects a pointer to target memory");
11451 Type *XElemTy = X.ElemTy;
11452 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11453 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11454 "OMP atomic update expected a scalar or struct type");
11455 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11456 (RMWOp != AtomicRMWInst::UMax) && (RMWOp != AtomicRMWInst::UMin) &&
11457 "OpenMP atomic does not support LT or GT operations");
11458 });
11459
11460 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11461 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11462 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11463 if (!AtomicResult)
11464 return AtomicResult.takeError();
11465 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11466 return Builder.saveIP();
11467}
11468
11469// FIXME: Duplicating AtomicExpand
11470Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11471 AtomicRMWInst::BinOp RMWOp) {
11472 switch (RMWOp) {
11473 case AtomicRMWInst::Add:
11474 return Builder.CreateAdd(Src1, Src2);
11475 case AtomicRMWInst::Sub:
11476 return Builder.CreateSub(Src1, Src2);
11477 case AtomicRMWInst::And:
11478 return Builder.CreateAnd(Src1, Src2);
11480 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11481 case AtomicRMWInst::Or:
11482 return Builder.CreateOr(Src1, Src2);
11483 case AtomicRMWInst::Xor:
11484 return Builder.CreateXor(Src1, Src2);
11489 case AtomicRMWInst::Max:
11490 case AtomicRMWInst::Min:
11503 llvm_unreachable("Unsupported atomic update operation");
11504 }
11505 llvm_unreachable("Unsupported atomic update operation");
11506}
11507
11509 // Loads cannot use Release or AcquireRelease ordering. This load is
11510 // just the initial value for the cmpxchg loop; the cmpxchg itself
11511 // retains the original ordering.
11512 AtomicOrdering LoadAO = AO;
11513
11514 if (AO == AtomicOrdering::Release) {
11516 } else if (AO == AtomicOrdering::AcquireRelease) {
11517 LoadAO = AtomicOrdering::Acquire;
11518 }
11519
11520 return LoadAO;
11521}
11522
11523Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11524 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11526 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11527 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11528 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11529 bool emitRMWOp = false;
11530 switch (RMWOp) {
11531 case AtomicRMWInst::Add:
11532 case AtomicRMWInst::And:
11534 case AtomicRMWInst::Or:
11535 case AtomicRMWInst::Xor:
11537 emitRMWOp = XElemTy;
11538 break;
11539 case AtomicRMWInst::Sub:
11540 emitRMWOp = (IsXBinopExpr && XElemTy);
11541 break;
11542 default:
11543 emitRMWOp = false;
11544 }
11545 emitRMWOp &= XElemTy->isIntegerTy();
11546
11547 std::pair<Value *, Value *> Res;
11548 if (emitRMWOp) {
11549 AtomicRMWInst *RMWInst =
11550 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11551 if (IsIgnoreDenormalMode)
11552 RMWInst->setMetadata(llvm::LLVMContext::MD_atomic_ignore_denormal_mode,
11553 llvm::MDNode::get(Builder.getContext(), {}));
11554 if (T.isAMDGPU()) {
11555 if (!IsFineGrainedMemory)
11556 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11557 llvm::MDNode::get(Builder.getContext(), {}));
11558 if (!IsRemoteMemory)
11559 RMWInst->setMetadata("amdgpu.no.remote.memory",
11560 llvm::MDNode::get(Builder.getContext(), {}));
11561 }
11562 Res.first = RMWInst;
11563 // not needed except in case of postfix captures. Generate anyway for
11564 // consistency with the else part. Will be removed with any DCE pass.
11565 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11566 if (RMWOp == AtomicRMWInst::Xchg)
11567 Res.second = Res.first;
11568 else
11569 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11570 } else if (XElemTy->isStructTy()) {
11571 LoadInst *OldVal =
11572 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11574 OldVal->setAtomic(LoadAO);
11575 const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
11576 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11577
11578 OpenMPIRBuilder::AtomicInfo atomicInfo(
11579 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11580 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11581 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11582 BasicBlock *CurBB = Builder.GetInsertBlock();
11583 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11584 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11585 BasicBlock *ExitBB =
11586 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11587 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11588 X->getName() + ".atomic.cont");
11589 ContBB->getTerminator()->eraseFromParent();
11590 Builder.restoreIP(AllocaIP);
11591 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11592 NewAtomicAddr->setName(X->getName() + "x.new.val");
11593 Builder.SetInsertPoint(ContBB);
11594 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11595 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11596 Value *OldExprVal = PHI;
11597 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11598 if (!CBResult)
11599 return CBResult.takeError();
11600 Value *Upd = *CBResult;
11601 Builder.CreateStore(Upd, NewAtomicAddr);
11604 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11605 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11606 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11607 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11608 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11609 OldVal->eraseFromParent();
11610 Res.first = OldExprVal;
11611 Res.second = Upd;
11612
11613 if (UnreachableInst *ExitTI =
11615 CurBBTI->eraseFromParent();
11616 Builder.SetInsertPoint(ExitBB);
11617 } else {
11618 Builder.SetInsertPoint(ExitTI);
11619 }
11620 } else {
11621 IntegerType *IntCastTy =
11622 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11623 LoadInst *OldVal =
11624 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11626 OldVal->setAtomic(LoadAO);
11627 // CurBB
11628 // | /---\
11629 // ContBB |
11630 // | \---/
11631 // ExitBB
11632 BasicBlock *CurBB = Builder.GetInsertBlock();
11633 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11634 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11635 BasicBlock *ExitBB =
11636 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11637 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11638 X->getName() + ".atomic.cont");
11639 ContBB->getTerminator()->eraseFromParent();
11640 Builder.restoreIP(AllocaIP);
11641 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11642 NewAtomicAddr->setName(X->getName() + "x.new.val");
11643 Builder.SetInsertPoint(ContBB);
11644 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11645 PHI->addIncoming(OldVal, CurBB);
11646 bool IsIntTy = XElemTy->isIntegerTy();
11647 Value *OldExprVal = PHI;
11648 if (!IsIntTy) {
11649 if (XElemTy->isFloatingPointTy()) {
11650 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11651 X->getName() + ".atomic.fltCast");
11652 } else {
11653 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11654 X->getName() + ".atomic.ptrCast");
11655 }
11656 }
11657
11658 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11659 if (!CBResult)
11660 return CBResult.takeError();
11661 Value *Upd = *CBResult;
11662 Builder.CreateStore(Upd, NewAtomicAddr);
11663 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11666 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11667 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11668 Result->setVolatile(VolatileX);
11669 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11670 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11671 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11672 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11673
11674 Res.first = OldExprVal;
11675 Res.second = Upd;
11676
11677 // set Insertion point in exit block
11678 if (UnreachableInst *ExitTI =
11680 CurBBTI->eraseFromParent();
11681 Builder.SetInsertPoint(ExitBB);
11682 } else {
11683 Builder.SetInsertPoint(ExitTI);
11684 }
11685 }
11686
11687 return Res;
11688}
11689
11692 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11693 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11694 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11695 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11696 if (!updateToLocation(Loc))
11697 return Loc.IP;
11698
11699 LLVM_DEBUG({
11700 Type *XTy = X.Var->getType();
11701 assert(XTy->isPointerTy() &&
11702 "OMP Atomic expects a pointer to target memory");
11703 Type *XElemTy = X.ElemTy;
11704 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11705 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11706 "OMP atomic capture expected a scalar or struct type");
11707 assert((RMWOp != AtomicRMWInst::Max) && (RMWOp != AtomicRMWInst::Min) &&
11708 "OpenMP atomic does not support LT or GT operations");
11709 });
11710
11711 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11712 // 'x' is simply atomically rewritten with 'expr'.
11713 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11714 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11715 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11716 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11717 if (!AtomicResult)
11718 return AtomicResult.takeError();
11719 Value *CapturedVal =
11720 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11721 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11722
11723 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11724 return Builder.saveIP();
11725}
11726
11730 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11731 bool IsFailOnly, bool IsWeak) {
11732
11734 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11735 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11736}
11737
11741 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11742 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11743
11744 if (!updateToLocation(Loc))
11745 return Loc.IP;
11746
11747 assert(X.Var->getType()->isPointerTy() &&
11748 "OMP atomic expects a pointer to target memory");
11749 // compare capture
11750 if (V.Var) {
11751 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11752 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11753 }
11754
11755 bool IsInteger = E->getType()->isIntegerTy();
11756
11757 if (Op == OMPAtomicCompareOp::EQ) {
11758 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11759 // R.Var handling.
11760 Value *OldValue = nullptr;
11761 Value *SuccessOrFail = nullptr;
11762
11763 if (!IsInteger && HandleFPNegZero) {
11764 // IEEE 754 special cases for cmpxchg (which is bitwise):
11765 // 1. -0.0 == +0.0 but they have different bit patterns.
11766 // 2. NaN != NaN but identical NaN bit patterns would match.
11767 //
11768 // CurBB:
11769 // %e_int = bitcast E to intN
11770 // %d_int = bitcast D to intN
11771 // %x_curr = load atomic intN, X
11772 // %x_fp = bitcast %x_curr to FP
11773 // %e_is_nan = fcmp uno E, E
11774 // %x_is_nan = fcmp uno %x_fp, %x_fp
11775 // %either_nan = or %e_is_nan, %x_is_nan
11776 // br %either_nan, NaNBB, NotNaNBB
11777 // NaNBB: ; NaN == anything is always false
11778 // br ExitBB
11779 // NotNaNBB:
11780 // %x_is_zero = fcmp oeq %x_fp, 0.0
11781 // %e_is_zero = fcmp oeq E, 0.0
11782 // %both_zero = and %x_is_zero, %e_is_zero
11783 // br %both_zero, ZeroBB, NormalBB
11784 // ZeroBB: ; both ±0.0 → x = d
11785 // cmpxchg X, %x_curr, %d_int
11786 // br ExitBB
11787 // NormalBB: ; original path
11788 // cmpxchg X, %e_int, %d_int
11789 // br ExitBB
11790 // ExitBB:
11791 // phi merge
11792 IntegerType *IntCastTy =
11793 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11794 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11795 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11796
11797 // Load X atomically.
11798 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11799 X.Var->getName() + ".atomic.load");
11801 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11802
11803 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11804 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11805 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11806 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11807 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11808
11809 BasicBlock *CurBB = Builder.GetInsertBlock();
11810 Function *F = CurBB->getParent();
11811 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11812 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11813 BasicBlock *ExitBB =
11814 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11816 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11817 BasicBlock *NotNaNBB = BasicBlock::Create(
11818 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11820 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11821 BasicBlock *NormalBB = BasicBlock::Create(
11822 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11823
11824 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11825 CurBB->getTerminator()->eraseFromParent();
11826 Builder.SetInsertPoint(CurBB);
11827 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11828
11829 // NaNBB: NaN == anything is always false; skip cmpxchg.
11830 Builder.SetInsertPoint(NaNBB);
11831 Builder.CreateBr(ExitBB);
11832
11833 // NotNaNBB: check both X and E for ±0.0.
11834 Builder.SetInsertPoint(NotNaNBB);
11835 Value *XIsZero =
11836 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11837 X.Var->getName() + ".atomic.xiszero");
11838 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11839 "atomic.e.iszero");
11840 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11841 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11842
11843 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11844 Builder.SetInsertPoint(ZeroBB);
11845 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11846 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11847 ResZero->setWeak(IsWeak);
11848 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11849 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11850 Builder.CreateBr(ExitBB);
11851
11852 // NormalBB: original bitwise cmpxchg.
11853 Builder.SetInsertPoint(NormalBB);
11854 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11855 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11856 ResNormal->setWeak(IsWeak);
11857 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11858 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11859 Builder.CreateBr(ExitBB);
11860
11861 // ExitBB: merge results from NaN, Zero, and Normal paths.
11862 Builder.SetInsertPoint(ExitBB, ExitBB->begin());
11863 PHINode *OldIntPHI =
11864 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11865 OldIntPHI->addIncoming(XCurr, NaNBB);
11866 OldIntPHI->addIncoming(OldZero, ZeroBB);
11867 OldIntPHI->addIncoming(OldNormal, NormalBB);
11868 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11869 X.Var->getName() + ".atomic.ok");
11870 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11871 SuccessPHI->addIncoming(OkZero, ZeroBB);
11872 SuccessPHI->addIncoming(OkNormal, NormalBB);
11873
11874 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11875 CurBBTI->eraseFromParent();
11876 Builder.SetInsertPoint(ExitBB);
11877 } else {
11878 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11879 }
11880
11881 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11882 X.Var->getName() + ".atomic.old.fp");
11883 SuccessOrFail = SuccessPHI;
11884 } else {
11885 AtomicCmpXchgInst *Result = nullptr;
11886 if (!IsInteger) {
11887 IntegerType *IntCastTy =
11888 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11889 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11890 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11891 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11892 MaybeAlign(), AO, Failure);
11893 } else {
11894 Result =
11895 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11896 }
11897 Result->setWeak(IsWeak);
11898
11899 if (V.Var) {
11900 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11901 if (!IsInteger)
11902 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11903 assert(OldValue->getType() == V.ElemTy &&
11904 "OldValue and V must be of same type");
11905 if (IsPostfixUpdate) {
11906 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11907 } else {
11908 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11909 if (IsFailOnly) {
11910 BasicBlock *CurBB = Builder.GetInsertBlock();
11911 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11912 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11913 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11914 CurBBTI, X.Var->getName() + ".atomic.exit");
11915 BasicBlock *ContBB = CurBB->splitBasicBlock(
11916 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11917 ContBB->getTerminator()->eraseFromParent();
11918 CurBB->getTerminator()->eraseFromParent();
11919
11920 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11921
11922 Builder.SetInsertPoint(ContBB);
11923 Builder.CreateStore(OldValue, V.Var);
11924 Builder.CreateBr(ExitBB);
11925
11926 if (UnreachableInst *ExitTI =
11928 CurBBTI->eraseFromParent();
11929 Builder.SetInsertPoint(ExitBB);
11930 } else {
11931 Builder.SetInsertPoint(ExitTI);
11932 }
11933 } else {
11934 Value *CapturedValue =
11935 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11936 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11937 }
11938 }
11939 }
11940 // The comparison result has to be stored.
11941 if (R.Var) {
11942 assert(R.Var->getType()->isPointerTy() &&
11943 "r.var must be of pointer type");
11944 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
11945
11946 Value *SuccessFailureVal =
11947 Builder.CreateExtractValue(Result, /*Idxs=*/1);
11948 Value *ResultCast =
11949 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
11950 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
11951 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
11952 }
11953 }
11954
11955 // For the HandleFPNegZero path, handle V.Var and R.Var using the
11956 // pre-computed OldValue and SuccessOrFail.
11957 if (HandleFPNegZero && !IsInteger) {
11958 if (V.Var) {
11959 assert(OldValue->getType() == V.ElemTy &&
11960 "OldValue and V must be of same type");
11961 if (IsPostfixUpdate) {
11962 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11963 } else {
11964 if (IsFailOnly) {
11965 BasicBlock *CurBB = Builder.GetInsertBlock();
11966 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11967 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11968 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11969 CurBBTI, X.Var->getName() + ".atomic.exit");
11970 BasicBlock *ContBB = CurBB->splitBasicBlock(
11971 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11972 ContBB->getTerminator()->eraseFromParent();
11973 CurBB->getTerminator()->eraseFromParent();
11974
11975 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11976
11977 Builder.SetInsertPoint(ContBB);
11978 Builder.CreateStore(OldValue, V.Var);
11979 Builder.CreateBr(ExitBB);
11980
11981 if (UnreachableInst *ExitTI =
11983 CurBBTI->eraseFromParent();
11984 Builder.SetInsertPoint(ExitBB);
11985 } else {
11986 Builder.SetInsertPoint(ExitTI);
11987 }
11988 } else {
11989 Value *CapturedValue =
11990 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11991 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11992 }
11993 }
11994 }
11995 // The comparison result has to be stored.
11996 if (R.Var) {
11997 assert(R.Var->getType()->isPointerTy() &&
11998 "r.var must be of pointer type");
11999 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12000
12001 Value *ResultCast = R.IsSigned
12002 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
12003 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
12004 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12005 }
12006 }
12007 } else {
12008 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
12009 "Op should be either max or min at this point");
12010 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
12011
12012 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
12013 // Let's take max as example.
12014 // OpenMP form:
12015 // x = x > expr ? expr : x;
12016 // LLVM form:
12017 // *ptr = *ptr > val ? *ptr : val;
12018 // We need to transform to LLVM form.
12019 // x = x <= expr ? x : expr;
12021 if (IsXBinopExpr) {
12022 if (IsInteger) {
12023 if (X.IsSigned)
12024 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
12026 else
12027 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
12029 } else {
12030 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12032 }
12033 } else {
12034 if (IsInteger) {
12035 if (X.IsSigned)
12036 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12038 else
12039 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12041 } else {
12042 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12044 }
12045 }
12046
12047 AtomicRMWInst *OldValue =
12048 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12049 if (V.Var) {
12050 Value *CapturedValue = nullptr;
12051 if (IsPostfixUpdate) {
12052 CapturedValue = OldValue;
12053 } else {
12054 CmpInst::Predicate Pred;
12055 switch (NewOp) {
12056 case AtomicRMWInst::Max:
12057 Pred = CmpInst::ICMP_SGT;
12058 break;
12060 Pred = CmpInst::ICMP_UGT;
12061 break;
12063 Pred = CmpInst::FCMP_OGT;
12064 break;
12065 case AtomicRMWInst::Min:
12066 Pred = CmpInst::ICMP_SLT;
12067 break;
12069 Pred = CmpInst::ICMP_ULT;
12070 break;
12072 Pred = CmpInst::FCMP_OLT;
12073 break;
12074 default:
12075 llvm_unreachable("unexpected comparison op");
12076 }
12077 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12078 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12079 }
12080 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12081 }
12082 }
12083
12084 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12085
12086 return Builder.saveIP();
12087}
12088
12091 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12092 Value *NumTeamsUpper, Value *ThreadLimit,
12093 Value *IfExpr) {
12094 if (!updateToLocation(Loc))
12095 return InsertPointTy();
12096
12097 uint32_t SrcLocStrSize;
12098 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12099 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12100 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12101
12102 // Outer allocation basicblock is the entry block of the current function.
12103 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12104 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12105 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12106 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12107 }
12108
12109 // The current basic block is split into four basic blocks. After outlining,
12110 // they will be mapped as follows:
12111 // ```
12112 // def current_fn() {
12113 // current_basic_block:
12114 // br label %teams.exit
12115 // teams.exit:
12116 // ; instructions after teams
12117 // }
12118 //
12119 // def outlined_fn() {
12120 // teams.alloca:
12121 // br label %teams.body
12122 // teams.body:
12123 // ; instructions within teams body
12124 // }
12125 // ```
12126 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12127 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12128 BasicBlock *AllocaBB =
12129 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12130
12131 bool SubClausesPresent =
12132 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12133 // Push num_teams
12134 if (!Config.isTargetDevice() && SubClausesPresent) {
12135 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12136 "if lowerbound is non-null, then upperbound must also be non-null "
12137 "for bounds on num_teams");
12138
12139 if (NumTeamsUpper == nullptr)
12140 NumTeamsUpper = Builder.getInt32(0);
12141
12142 if (NumTeamsLower == nullptr)
12143 NumTeamsLower = NumTeamsUpper;
12144
12145 if (IfExpr) {
12146 assert(IfExpr->getType()->isIntegerTy() &&
12147 "argument to if clause must be an integer value");
12148
12149 // upper = ifexpr ? upper : 1
12150 if (IfExpr->getType() != Int1)
12151 IfExpr = Builder.CreateICmpNE(IfExpr,
12152 ConstantInt::get(IfExpr->getType(), 0));
12153 NumTeamsUpper = Builder.CreateSelect(
12154 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12155
12156 // lower = ifexpr ? lower : 1
12157 NumTeamsLower = Builder.CreateSelect(
12158 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12159 }
12160
12161 if (ThreadLimit == nullptr)
12162 ThreadLimit = Builder.getInt32(0);
12163
12164 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12165 // truncate or sign extend the passed values to match the int32 parameters.
12166 Value *NumTeamsLowerInt32 =
12167 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12168 Value *NumTeamsUpperInt32 =
12169 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12170 Value *ThreadLimitInt32 =
12171 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12172
12173 Value *ThreadNum = getOrCreateThreadID(Ident);
12174
12176 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12177 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12178 ThreadLimitInt32});
12179 }
12180 // Generate the body of teams.
12181 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12182 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12183 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12184 return Err;
12185
12186 auto OI = std::make_unique<OutlineInfo>();
12187 OI->EntryBB = AllocaBB;
12188 OI->ExitBB = ExitBB;
12189 OI->OuterAllocBB = &OuterAllocaBB;
12190
12191 // Insert fake values for global tid and bound tid.
12193 InsertPointTy OuterAllocaIP(&OuterAllocaBB, OuterAllocaBB.begin());
12194 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12195 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12196 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12197 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12198
12199 auto HostPostOutlineCB = [this, Ident,
12200 ToBeDeleted](Function &OutlinedFn) mutable {
12201 // The stale call instruction will be replaced with a new call instruction
12202 // for runtime call with the outlined function.
12203
12204 assert(OutlinedFn.hasOneUse() &&
12205 "there must be a single user for the outlined function");
12206 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12207 ToBeDeleted.push_back(StaleCI);
12208
12209 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12210 "Outlined function must have two or three arguments only");
12211
12212 bool HasShared = OutlinedFn.arg_size() == 3;
12213
12214 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12215 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12216 if (HasShared)
12217 OutlinedFn.getArg(2)->setName("data");
12218
12219 // Call to the runtime function for teams in the current function.
12220 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12221 "outlined function.");
12222 Builder.SetInsertPoint(StaleCI);
12223 SmallVector<Value *> Args = {
12224 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12225 if (HasShared)
12226 Args.push_back(StaleCI->getArgOperand(2));
12229 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12230 Args);
12231
12232 Builder.ClearInsertionPoint();
12233 for (Instruction *I : llvm::reverse(ToBeDeleted))
12234 I->eraseFromParent();
12235 };
12236
12237 if (!Config.isTargetDevice())
12238 OI->PostOutlineCB = HostPostOutlineCB;
12239
12240 addOutlineInfo(std::move(OI));
12241
12242 Builder.SetInsertPoint(ExitBB);
12243
12244 return Builder.saveIP();
12245}
12246
12248 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12249 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12250 if (!updateToLocation(Loc))
12251 return InsertPointTy();
12252
12253 BasicBlock *OuterAllocaBB = OuterAllocIP.getBlock();
12254
12255 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12256 BasicBlock *BodyBB =
12257 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12258 Builder.SetInsertPoint(BodyBB, BodyBB->begin());
12259 }
12260 BasicBlock *ExitBB =
12261 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12262 BasicBlock *BodyBB =
12263 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12264 BasicBlock *AllocaBB =
12265 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12266
12267 // Generate the body of distribute clause
12268 InsertPointTy AllocaIP(AllocaBB, AllocaBB->begin());
12269 InsertPointTy CodeGenIP(BodyBB, BodyBB->begin());
12270 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12271 return Err;
12272
12273 // When using target we use different runtime functions which require a
12274 // callback.
12275 if (Config.isTargetDevice()) {
12276 auto OI = std::make_unique<OutlineInfo>();
12277 OI->OuterAllocBB = OuterAllocIP.getBlock();
12278 OI->EntryBB = AllocaBB;
12279 OI->ExitBB = ExitBB;
12280 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12281 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12282
12283 addOutlineInfo(std::move(OI));
12284 }
12285 Builder.SetInsertPoint(ExitBB);
12286
12287 return Builder.saveIP();
12288}
12289
12292 std::string VarName) {
12293 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12295 Names.size()),
12296 Names);
12297 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12298 M, MapNamesArrayInit->getType(),
12299 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12300 VarName);
12301 return MapNamesArrayGlobal;
12302}
12303
12304// Create all simple and struct types exposed by the runtime and remember
12305// the llvm::PointerTypes of them for easy access later.
12306void OpenMPIRBuilder::initializeTypes(Module &M) {
12307 LLVMContext &Ctx = M.getContext();
12308 StructType *T;
12309 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12310 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12311#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12312#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12313 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12314 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12315#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12316 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12317 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12318#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12319 T = StructType::getTypeByName(Ctx, StructName); \
12320 if (!T) \
12321 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12322 VarName = T; \
12323 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12324#include "llvm/Frontend/OpenMP/OMPKinds.def"
12325}
12326
12329 SmallVectorImpl<BasicBlock *> &BlockVector) {
12331 BlockSet.insert(EntryBB);
12332 BlockSet.insert(ExitBB);
12333
12334 Worklist.push_back(EntryBB);
12335 while (!Worklist.empty()) {
12336 BasicBlock *BB = Worklist.pop_back_val();
12337 BlockVector.push_back(BB);
12338 for (BasicBlock *SuccBB : successors(BB))
12339 if (BlockSet.insert(SuccBB).second)
12340 Worklist.push_back(SuccBB);
12341 }
12342}
12343
12344std::unique_ptr<CodeExtractor>
12346 bool ArgsInZeroAddressSpace,
12347 Twine Suffix) {
12348 return std::make_unique<CodeExtractor>(
12349 Blocks, /* DominatorTree */ nullptr,
12350 /* AggregateArgs */ true,
12351 /* BlockFrequencyInfo */ nullptr,
12352 /* BranchProbabilityInfo */ nullptr,
12353 /* AssumptionCache */ nullptr,
12354 /* AllowVarArgs */ true,
12355 /* AllowAlloca */ true,
12356 /* AllocationBlock*/ OuterAllocBB,
12357 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12358 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12359}
12360
12361std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12362 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12363 return std::make_unique<DeviceSharedMemCodeExtractor>(
12364 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12365 /* AggregateArgs */ true,
12366 /* BlockFrequencyInfo */ nullptr,
12367 /* BranchProbabilityInfo */ nullptr,
12368 /* AssumptionCache */ nullptr,
12369 /* AllowVarArgs */ true,
12370 /* AllowAlloca */ true,
12371 /* AllocationBlock*/ OuterAllocBB,
12372 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12374 : OuterDeallocBBs,
12375 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12376}
12377
12379 uint64_t Size, int32_t Flags,
12381 StringRef Name) {
12382 if (!Config.isGPU()) {
12385 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12386 return;
12387 }
12388 // TODO: Add support for global variables on the device after declare target
12389 // support.
12390 Function *Fn = dyn_cast<Function>(Addr);
12391 if (!Fn)
12392 return;
12393
12394 // Add a function attribute for the kernel.
12395 Fn->addFnAttr("kernel");
12396 if (T.isAMDGCN())
12397 Fn->addFnAttr("uniform-work-group-size");
12398 Fn->addFnAttr(Attribute::MustProgress);
12399}
12400
12401// We only generate metadata for function that contain target regions.
12404
12405 // If there are no entries, we don't need to do anything.
12406 if (OffloadInfoManager.empty())
12407 return;
12408
12409 LLVMContext &C = M.getContext();
12412 16>
12413 OrderedEntries(OffloadInfoManager.size());
12414
12415 // Auxiliary methods to create metadata values and strings.
12416 auto &&GetMDInt = [this](unsigned V) {
12417 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12418 };
12419
12420 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12421
12422 // Create the offloading info metadata node.
12423 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12424 auto &&TargetRegionMetadataEmitter =
12425 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12426 const TargetRegionEntryInfo &EntryInfo,
12428 // Generate metadata for target regions. Each entry of this metadata
12429 // contains:
12430 // - Entry 0 -> Kind of this type of metadata (0).
12431 // - Entry 1 -> Device ID of the file where the entry was identified.
12432 // - Entry 2 -> File ID of the file where the entry was identified.
12433 // - Entry 3 -> Mangled name of the function where the entry was
12434 // identified.
12435 // - Entry 4 -> Line in the file where the entry was identified.
12436 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12437 // - Entry 6 -> Order the entry was created.
12438 // The first element of the metadata node is the kind.
12439 Metadata *Ops[] = {
12440 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12441 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12442 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12443 GetMDInt(E.getOrder())};
12444
12445 // Save this entry in the right position of the ordered entries array.
12446 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12447
12448 // Add metadata to the named metadata node.
12449 MD->addOperand(MDNode::get(C, Ops));
12450 };
12451
12452 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12453
12454 // Create function that emits metadata for each device global variable entry;
12455 auto &&DeviceGlobalVarMetadataEmitter =
12456 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12457 StringRef MangledName,
12459 // Generate metadata for global variables. Each entry of this metadata
12460 // contains:
12461 // - Entry 0 -> Kind of this type of metadata (1).
12462 // - Entry 1 -> Mangled name of the variable.
12463 // - Entry 2 -> Declare target kind.
12464 // - Entry 3 -> Order the entry was created.
12465 // The first element of the metadata node is the kind.
12466 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12467 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12468
12469 // Save this entry in the right position of the ordered entries array.
12470 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12471 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12472
12473 // Add metadata to the named metadata node.
12474 MD->addOperand(MDNode::get(C, Ops));
12475 };
12476
12477 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12478 DeviceGlobalVarMetadataEmitter);
12479
12480 for (const auto &E : OrderedEntries) {
12481 assert(E.first && "All ordered entries must exist!");
12482 if (const auto *CE =
12484 E.first)) {
12485 if (!CE->getID() || !CE->getAddress()) {
12486 // Do not blame the entry if the parent funtion is not emitted.
12487 TargetRegionEntryInfo EntryInfo = E.second;
12488 StringRef FnName = EntryInfo.ParentName;
12489 if (!M.getNamedValue(FnName))
12490 continue;
12491 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12492 continue;
12493 }
12494 createOffloadEntry(CE->getID(), CE->getAddress(),
12495 /*Size=*/0, CE->getFlags(),
12497 } else if (const auto *CE = dyn_cast<
12499 E.first)) {
12502 CE->getFlags());
12503 switch (Flags) {
12506 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12507 continue;
12508 if (!CE->getAddress()) {
12509 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12510 continue;
12511 }
12512 // The vaiable has no definition - no need to add the entry.
12513 if (CE->getVarSize() == 0)
12514 continue;
12515 break;
12517 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12518 (!Config.isTargetDevice() && CE->getAddress())) &&
12519 "Declaret target link address is set.");
12520 if (Config.isTargetDevice())
12521 continue;
12522 if (!CE->getAddress()) {
12524 continue;
12525 }
12526 break;
12529 if (!CE->getAddress()) {
12530 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12531 continue;
12532 }
12533 break;
12534 default:
12535 break;
12536 }
12537
12538 // Hidden or internal symbols on the device are not externally visible.
12539 // We should not attempt to register them by creating an offloading
12540 // entry. Indirect variables are handled separately on the device.
12541 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12542 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12543 (Flags !=
12545 Flags != OffloadEntriesInfoManager::
12546 OMPTargetGlobalVarEntryIndirectVTable))
12547 continue;
12548
12549 // Indirect globals need to use a special name that doesn't match the name
12550 // of the associated host global.
12552 Flags ==
12554 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12555 Flags, CE->getLinkage(), CE->getVarName());
12556 else
12557 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12558 Flags, CE->getLinkage());
12559
12560 } else {
12561 llvm_unreachable("Unsupported entry kind.");
12562 }
12563 }
12564
12565 // Emit requires directive globals to a special entry so the runtime can
12566 // register them when the device image is loaded.
12567 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12568 // entries should be redesigned to better suit this use-case.
12569 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12573 ".requires", /*Size=*/0,
12575 Config.getRequiresFlags());
12576}
12577
12580 unsigned FileID, unsigned Line, unsigned Count) {
12581 raw_svector_ostream OS(Name);
12582 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12583 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12584 if (Count)
12585 OS << "_" << Count;
12586}
12587
12589 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12590 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12592 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12593 EntryInfo.Line, NewCount);
12594}
12595
12598 vfs::FileSystem &VFS,
12599 StringRef ParentName) {
12600 sys::fs::UniqueID ID(0xdeadf17e, 0);
12601 auto FileIDInfo = CallBack();
12602 uint64_t FileID = 0;
12603 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12604 ID = Status->getUniqueID();
12605 FileID = Status->getUniqueID().getFile();
12606 } else {
12607 // If the inode ID could not be determined, create a hash value
12608 // the current file name and use that as an ID.
12609 FileID = hash_value(std::get<0>(FileIDInfo));
12610 }
12611
12612 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12613 std::get<1>(FileIDInfo));
12614}
12615
12617 unsigned Offset = 0;
12618 for (uint64_t Remain =
12619 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12621 !(Remain & 1); Remain = Remain >> 1)
12622 Offset++;
12623 return Offset;
12624}
12625
12628 // Rotate by getFlagMemberOffset() bits.
12629 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12630 << getFlagMemberOffset());
12631}
12632
12635 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12636 // If the entry is PTR_AND_OBJ but has not been marked with the special
12637 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12638 // marked as MEMBER_OF.
12639 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12641 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12644 return;
12645
12646 // Entries with ATTACH are not members-of anything. They are handled
12647 // separately by the runtime after other maps have been handled.
12648 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12650 return;
12651
12652 // Reset the placeholder value to prepare the flag for the assignment of the
12653 // proper MEMBER_OF value.
12654 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12655 Flags |= MemberOfFlag;
12656}
12657
12661 bool IsDeclaration, bool IsExternallyVisible,
12662 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12663 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12664 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12665 std::function<Constant *()> GlobalInitializer,
12666 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12667 // TODO: convert this to utilise the IRBuilder Config rather than
12668 // a passed down argument.
12669 if (OpenMPSIMD)
12670 return nullptr;
12671
12674 CaptureClause ==
12676 Config.hasRequiresUnifiedSharedMemory())) {
12677 SmallString<64> PtrName;
12678 {
12679 raw_svector_ostream OS(PtrName);
12680 OS << MangledName;
12681 if (!IsExternallyVisible)
12682 OS << format("_%x", EntryInfo.FileID);
12683 OS << "_decl_tgt_ref_ptr";
12684 }
12685
12686 Value *Ptr = M.getNamedValue(PtrName);
12687
12688 if (!Ptr) {
12689 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12690 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12691
12692 auto *GV = cast<GlobalVariable>(Ptr);
12693 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12694
12695 if (!Config.isTargetDevice()) {
12696 if (GlobalInitializer)
12697 GV->setInitializer(GlobalInitializer());
12698 else
12699 GV->setInitializer(GlobalValue);
12700 }
12701
12703 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12704 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12705 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12706 }
12707
12708 return cast<Constant>(Ptr);
12709 }
12710
12711 return nullptr;
12712}
12713
12717 bool IsDeclaration, bool IsExternallyVisible,
12718 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12719 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12720 std::vector<Triple> TargetTriple,
12721 std::function<Constant *()> GlobalInitializer,
12722 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12723 Constant *Addr) {
12725 (TargetTriple.empty() && !Config.isTargetDevice()))
12726 return;
12727
12729 StringRef VarName;
12730 int64_t VarSize;
12732
12734 CaptureClause ==
12736 !Config.hasRequiresUnifiedSharedMemory()) {
12738 VarName = MangledName;
12739 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12740
12741 if (!IsDeclaration)
12742 VarSize = divideCeil(
12743 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12744 else
12745 VarSize = 0;
12746 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12747
12748 // This is a workaround carried over from Clang which prevents undesired
12749 // optimisation of internal variables.
12750 if (Config.isTargetDevice() &&
12751 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12752 // Do not create a "ref-variable" if the original is not also available
12753 // on the host.
12754 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12755 return;
12756
12757 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12758
12759 if (!M.getNamedValue(RefName)) {
12760 Constant *AddrRef =
12761 getOrCreateInternalVariable(Addr->getType(), RefName);
12762 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12763 GvAddrRef->setConstant(true);
12764 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12765 GvAddrRef->setInitializer(Addr);
12766 GeneratedRefs.push_back(GvAddrRef);
12767 }
12768 }
12769 } else {
12772 else
12774
12775 if (Config.isTargetDevice()) {
12776 VarName = (Addr) ? Addr->getName() : "";
12777 Addr = nullptr;
12778 } else {
12780 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12781 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12782 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12783 VarName = (Addr) ? Addr->getName() : "";
12784 }
12785 VarSize = M.getDataLayout().getPointerSize();
12787 }
12788
12789 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12790 Flags, Linkage);
12791}
12792
12793/// Loads all the offload entries information from the host IR
12794/// metadata.
12796 // If we are in target mode, load the metadata from the host IR. This code has
12797 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12798
12799 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12800 if (!MD)
12801 return;
12802
12803 for (MDNode *MN : MD->operands()) {
12804 auto &&GetMDInt = [MN](unsigned Idx) {
12805 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12806 return cast<ConstantInt>(V->getValue())->getZExtValue();
12807 };
12808
12809 auto &&GetMDString = [MN](unsigned Idx) {
12810 auto *V = cast<MDString>(MN->getOperand(Idx));
12811 return V->getString();
12812 };
12813
12814 switch (GetMDInt(0)) {
12815 default:
12816 llvm_unreachable("Unexpected metadata!");
12817 break;
12818 case OffloadEntriesInfoManager::OffloadEntryInfo::
12819 OffloadingEntryInfoTargetRegion: {
12820 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12821 /*DeviceID=*/GetMDInt(1),
12822 /*FileID=*/GetMDInt(2),
12823 /*Line=*/GetMDInt(4),
12824 /*Count=*/GetMDInt(5));
12825 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12826 /*Order=*/GetMDInt(6));
12827 break;
12828 }
12829 case OffloadEntriesInfoManager::OffloadEntryInfo::
12830 OffloadingEntryInfoDeviceGlobalVar:
12831 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12832 /*MangledName=*/GetMDString(1),
12834 /*Flags=*/GetMDInt(2)),
12835 /*Order=*/GetMDInt(3));
12836 break;
12837 }
12838 }
12839}
12840
12842 StringRef HostFilePath) {
12843 if (HostFilePath.empty())
12844 return;
12845
12846 auto Buf = VFS.getBufferForFile(HostFilePath);
12847 if (std::error_code Err = Buf.getError()) {
12848 report_fatal_error(("error opening host file from host file path inside of "
12849 "OpenMPIRBuilder: " +
12850 Err.message())
12851 .c_str());
12852 }
12853
12854 LLVMContext Ctx;
12856 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12857 if (std::error_code Err = M.getError()) {
12859 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12860 .c_str());
12861 }
12862
12863 loadOffloadInfoMetadata(*M.get());
12864}
12865
12868 llvm::StringRef Name) {
12869 Builder.restoreIP(Loc.IP);
12870
12871 BasicBlock *CurBB = Builder.GetInsertBlock();
12872 assert(CurBB &&
12873 "expected a valid insertion block for creating an iterator loop");
12874 Function *F = CurBB->getParent();
12875
12876 InsertPointTy SplitIP = Builder.saveIP();
12877 if (SplitIP.getPoint() == CurBB->end())
12878 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12879 SplitIP = InsertPointTy(CurBB, Terminator->getIterator());
12880
12881 BasicBlock *ContBB =
12882 splitBB(SplitIP, /*CreateBranch=*/false,
12883 Builder.getCurrentDebugLocation(), "omp.it.cont");
12884
12885 CanonicalLoopInfo *CLI =
12886 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12887 /*PreInsertBefore=*/ContBB,
12888 /*PostInsertBefore=*/ContBB, Name);
12889
12890 // Enter loop from original block.
12891 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12892
12893 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12894 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12895 T->eraseFromParent();
12896
12897 InsertPointTy BodyIP = CLI->getBodyIP();
12898 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12899 return Err;
12900
12901 // Body must either fallthrough to the latch or branch directly to it.
12902 if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
12903 auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
12904 if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
12906 "iterator bodygen must terminate the canonical body with an "
12907 "unconditional branch to the loop latch",
12909 }
12910 } else {
12911 // Ensure we end the loop body by jumping to the latch.
12912 Builder.SetInsertPoint(CLI->getBody());
12913 Builder.CreateBr(CLI->getLatch());
12914 }
12915
12916 // Link After -> ContBB
12917 Builder.SetInsertPoint(CLI->getAfter(), CLI->getAfter()->begin());
12918 if (!CLI->getAfter()->hasTerminator())
12919 Builder.CreateBr(ContBB);
12920
12921 return InsertPointTy{ContBB, ContBB->begin()};
12922}
12923
12924/// Mangle the parameter part of the vector function name according to
12925/// their OpenMP classification. The mangling function is defined in
12926/// section 4.5 of the AAVFABI(2021Q1).
12927static std::string mangleVectorParameters(
12929 SmallString<256> Buffer;
12930 llvm::raw_svector_ostream Out(Buffer);
12931 for (const auto &ParamAttr : ParamAttrs) {
12932 switch (ParamAttr.Kind) {
12934 Out << 'l';
12935 break;
12937 Out << 'R';
12938 break;
12940 Out << 'U';
12941 break;
12943 Out << 'L';
12944 break;
12946 Out << 'u';
12947 break;
12949 Out << 'v';
12950 break;
12951 }
12952 if (ParamAttr.HasVarStride)
12953 Out << "s" << ParamAttr.StrideOrArg;
12954 else if (ParamAttr.Kind ==
12956 ParamAttr.Kind ==
12958 ParamAttr.Kind ==
12960 ParamAttr.Kind ==
12962 // Don't print the step value if it is not present or if it is
12963 // equal to 1.
12964 if (ParamAttr.StrideOrArg < 0)
12965 Out << 'n' << -ParamAttr.StrideOrArg;
12966 else if (ParamAttr.StrideOrArg != 1)
12967 Out << ParamAttr.StrideOrArg;
12968 }
12969
12970 if (!!ParamAttr.Alignment)
12971 Out << 'a' << ParamAttr.Alignment;
12972 }
12973
12974 return std::string(Out.str());
12975}
12976
12978 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
12980 struct ISADataTy {
12981 char ISA;
12982 unsigned VecRegSize;
12983 };
12984 ISADataTy ISAData[] = {
12985 {'b', 128}, // SSE
12986 {'c', 256}, // AVX
12987 {'d', 256}, // AVX2
12988 {'e', 512}, // AVX512
12989 };
12991 switch (Branch) {
12993 Masked.push_back('N');
12994 Masked.push_back('M');
12995 break;
12997 Masked.push_back('N');
12998 break;
13000 Masked.push_back('M');
13001 break;
13002 }
13003 for (char Mask : Masked) {
13004 for (const ISADataTy &Data : ISAData) {
13006 llvm::raw_svector_ostream Out(Buffer);
13007 Out << "_ZGV" << Data.ISA << Mask;
13008 if (!VLENVal) {
13009 assert(NumElts && "Non-zero simdlen/cdtsize expected");
13010 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
13011 } else {
13012 Out << VLENVal;
13013 }
13014 Out << mangleVectorParameters(ParamAttrs);
13015 Out << '_' << Fn->getName();
13016 Fn->addFnAttr(Out.str());
13017 }
13018 }
13019}
13020
13021// Function used to add the attribute. The parameter `VLEN` is templated to
13022// allow the use of `x` when targeting scalable functions for SVE.
13023template <typename T>
13024static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
13025 char ISA, StringRef ParSeq,
13026 StringRef MangledName, bool OutputBecomesInput,
13027 llvm::Function *Fn) {
13028 SmallString<256> Buffer;
13029 llvm::raw_svector_ostream Out(Buffer);
13030 Out << Prefix << ISA << LMask << VLEN;
13031 if (OutputBecomesInput)
13032 Out << 'v';
13033 Out << ParSeq << '_' << MangledName;
13034 Fn->addFnAttr(Out.str());
13035}
13036
13037// Helper function to generate the Advanced SIMD names depending on the value
13038// of the NDS when simdlen is not present.
13039static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13040 StringRef Prefix, char ISA,
13041 StringRef ParSeq, StringRef MangledName,
13042 bool OutputBecomesInput,
13043 llvm::Function *Fn) {
13044 switch (NDS) {
13045 case 8:
13046 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13047 OutputBecomesInput, Fn);
13048 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13049 OutputBecomesInput, Fn);
13050 break;
13051 case 16:
13052 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13053 OutputBecomesInput, Fn);
13054 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13055 OutputBecomesInput, Fn);
13056 break;
13057 case 32:
13058 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13059 OutputBecomesInput, Fn);
13060 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13061 OutputBecomesInput, Fn);
13062 break;
13063 case 64:
13064 case 128:
13065 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13066 OutputBecomesInput, Fn);
13067 break;
13068 default:
13069 llvm_unreachable("Scalar type is too wide.");
13070 }
13071}
13072
13073/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13075 llvm::Function *Fn, unsigned UserVLEN,
13077 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13078 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13079
13080 // Sort out parameter sequence.
13081 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13082 StringRef Prefix = "_ZGV";
13083 StringRef MangledName = Fn->getName();
13084
13085 // Generate simdlen from user input (if any).
13086 if (UserVLEN) {
13087 if (ISA == 's') {
13088 // SVE generates only a masked function.
13089 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13090 OutputBecomesInput, Fn);
13091 return;
13092 }
13093
13094 switch (Branch) {
13096 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13097 OutputBecomesInput, Fn);
13098 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13099 OutputBecomesInput, Fn);
13100 break;
13102 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13103 OutputBecomesInput, Fn);
13104 break;
13106 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13107 OutputBecomesInput, Fn);
13108 break;
13109 }
13110 return;
13111 }
13112
13113 if (ISA == 's') {
13114 // SVE, section 3.4.1, item 1.
13115 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13116 OutputBecomesInput, Fn);
13117 return;
13118 }
13119
13120 switch (Branch) {
13122 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13123 MangledName, OutputBecomesInput, Fn);
13124 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13125 MangledName, OutputBecomesInput, Fn);
13126 break;
13128 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13129 MangledName, OutputBecomesInput, Fn);
13130 break;
13132 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13133 MangledName, OutputBecomesInput, Fn);
13134 break;
13135 }
13136}
13137
13138//===----------------------------------------------------------------------===//
13139// OffloadEntriesInfoManager
13140//===----------------------------------------------------------------------===//
13141
13143 return OffloadEntriesTargetRegion.empty() &&
13144 OffloadEntriesDeviceGlobalVar.empty();
13145}
13146
13147unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13148 const TargetRegionEntryInfo &EntryInfo) const {
13149 auto It = OffloadEntriesTargetRegionCount.find(
13150 getTargetRegionEntryCountKey(EntryInfo));
13151 if (It == OffloadEntriesTargetRegionCount.end())
13152 return 0;
13153 return It->second;
13154}
13155
13156void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13157 const TargetRegionEntryInfo &EntryInfo) {
13158 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13159 EntryInfo.Count + 1;
13160}
13161
13162/// Initialize target region entry.
13164 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13165 OffloadEntriesTargetRegion[EntryInfo] =
13166 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13168 ++OffloadingEntriesNum;
13169}
13170
13172 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13174 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13175
13176 // Update the EntryInfo with the next available count for this location.
13177 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13178
13179 // If we are emitting code for a target, the entry is already initialized,
13180 // only has to be registered.
13181 if (OMPBuilder->Config.isTargetDevice()) {
13182 // This could happen if the device compilation is invoked standalone.
13183 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13184 return;
13185 }
13186 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13187 Entry.setAddress(Addr);
13188 Entry.setID(ID);
13189 Entry.setFlags(Flags);
13190 } else {
13192 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13193 return;
13194 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13195 "Target region entry already registered!");
13196 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13197 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13198 ++OffloadingEntriesNum;
13199 }
13200 incrementTargetRegionEntryInfoCount(EntryInfo);
13201}
13202
13204 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13205
13206 // Update the EntryInfo with the next available count for this location.
13207 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13208
13209 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13210 if (It == OffloadEntriesTargetRegion.end()) {
13211 return false;
13212 }
13213 // Fail if this entry is already registered.
13214 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13215 return false;
13216 return true;
13217}
13218
13220 const OffloadTargetRegionEntryInfoActTy &Action) {
13221 // Scan all target region entries and perform the provided action.
13222 for (const auto &It : OffloadEntriesTargetRegion) {
13223 Action(It.first, It.second);
13224 }
13225}
13226
13228 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13229 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13230 ++OffloadingEntriesNum;
13231}
13232
13234 StringRef VarName, Constant *Addr, int64_t VarSize,
13236 if (OMPBuilder->Config.isTargetDevice()) {
13237 // This could happen if the device compilation is invoked standalone.
13238 if (!hasDeviceGlobalVarEntryInfo(VarName))
13239 return;
13240 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13241 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13242 if (Entry.getVarSize() == 0) {
13243 Entry.setVarSize(VarSize);
13244 Entry.setLinkage(Linkage);
13245 }
13246 return;
13247 }
13248 Entry.setVarSize(VarSize);
13249 Entry.setLinkage(Linkage);
13250 Entry.setAddress(Addr);
13251 } else {
13252 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13253 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13254 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13255 "Entry not initialized!");
13256 if (Entry.getVarSize() == 0) {
13257 Entry.setVarSize(VarSize);
13258 Entry.setLinkage(Linkage);
13259 }
13260 return;
13261 }
13263 Flags ==
13265 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13266 Addr, VarSize, Flags, Linkage,
13267 VarName.str());
13268 else
13269 OffloadEntriesDeviceGlobalVar.try_emplace(
13270 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13271 ++OffloadingEntriesNum;
13272 }
13273}
13274
13277 // Scan all target region entries and perform the provided action.
13278 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13279 Action(E.getKey(), E.getValue());
13280}
13281
13282//===----------------------------------------------------------------------===//
13283// CanonicalLoopInfo
13284//===----------------------------------------------------------------------===//
13285
13286void CanonicalLoopInfo::collectControlBlocks(
13288 // We only count those BBs as control block for which we do not need to
13289 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13290 // flow. For consistency, this also means we do not add the Body block, which
13291 // is just the entry to the body code.
13292 BBs.reserve(BBs.size() + 6);
13293 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13294}
13295
13297 assert(isValid() && "Requires a valid canonical loop");
13298 for (BasicBlock *Pred : predecessors(Header)) {
13299 if (Pred != Latch)
13300 return Pred;
13301 }
13302 llvm_unreachable("Missing preheader");
13303}
13304
13305void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13306 assert(isValid() && "Requires a valid canonical loop");
13307
13308 Instruction *CmpI = &getCond()->front();
13309 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13310 CmpI->setOperand(1, TripCount);
13311
13312#ifndef NDEBUG
13313 assertOK();
13314#endif
13315}
13316
13317void CanonicalLoopInfo::mapIndVar(
13318 llvm::function_ref<Value *(Instruction *)> Updater) {
13319 assert(isValid() && "Requires a valid canonical loop");
13320
13321 Instruction *OldIV = getIndVar();
13322
13323 // Record all uses excluding those introduced by the updater. Uses by the
13324 // CanonicalLoopInfo itself to keep track of the number of iterations are
13325 // excluded.
13326 SmallVector<Use *> ReplacableUses;
13327 for (Use &U : OldIV->uses()) {
13328 auto *User = dyn_cast<Instruction>(U.getUser());
13329 if (!User)
13330 continue;
13331 if (User->getParent() == getCond())
13332 continue;
13333 if (User->getParent() == getLatch())
13334 continue;
13335 ReplacableUses.push_back(&U);
13336 }
13337
13338 // Run the updater that may introduce new uses
13339 Value *NewIV = Updater(OldIV);
13340
13341 // Replace the old uses with the value returned by the updater.
13342 for (Use *U : ReplacableUses)
13343 U->set(NewIV);
13344
13345#ifndef NDEBUG
13346 assertOK();
13347#endif
13348}
13349
13351#ifndef NDEBUG
13352 // No constraints if this object currently does not describe a loop.
13353 if (!isValid())
13354 return;
13355
13356 BasicBlock *Preheader = getPreheader();
13357 BasicBlock *Body = getBody();
13358 BasicBlock *After = getAfter();
13359
13360 // Verify standard control-flow we use for OpenMP loops.
13361 assert(Preheader);
13362 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13363 "Preheader must terminate with unconditional branch");
13364 assert(Preheader->getSingleSuccessor() == Header &&
13365 "Preheader must jump to header");
13366
13367 assert(Header);
13368 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13369 "Header must terminate with unconditional branch");
13370 assert(Header->getSingleSuccessor() == Cond &&
13371 "Header must jump to exiting block");
13372
13373 assert(Cond);
13374 assert(Cond->getSinglePredecessor() == Header &&
13375 "Exiting block only reachable from header");
13376
13377 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13378 "Exiting block must terminate with conditional branch");
13379 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13380 "Exiting block's first successor jump to the body");
13381 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13382 "Exiting block's second successor must exit the loop");
13383
13384 assert(Body);
13385 assert(Body->getSinglePredecessor() == Cond &&
13386 "Body only reachable from exiting block");
13387 assert(!isa<PHINode>(Body->front()));
13388
13389 assert(Latch);
13390 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13391 "Latch must terminate with unconditional branch");
13392 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13393 // TODO: To support simple redirecting of the end of the body code that has
13394 // multiple; introduce another auxiliary basic block like preheader and after.
13395 assert(Latch->getSinglePredecessor() != nullptr);
13396 assert(!isa<PHINode>(Latch->front()));
13397
13398 assert(Exit);
13399 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13400 "Exit block must terminate with unconditional branch");
13401 assert(Exit->getSingleSuccessor() == After &&
13402 "Exit block must jump to after block");
13403
13404 assert(After);
13405 assert(After->getSinglePredecessor() == Exit &&
13406 "After block only reachable from exit block");
13407 assert(After->empty() || !isa<PHINode>(After->front()));
13408
13409 Instruction *IndVar = getIndVar();
13410 assert(IndVar && "Canonical induction variable not found?");
13411 assert(isa<IntegerType>(IndVar->getType()) &&
13412 "Induction variable must be an integer");
13413 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13414 "Induction variable must be a PHI in the loop header");
13415 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13416 assert(
13417 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13418 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13419
13420 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13421 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13422 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13423 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13424 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13425 ->isOne());
13426
13427 Value *TripCount = getTripCount();
13428 assert(TripCount && "Loop trip count not found?");
13429 assert(IndVar->getType() == TripCount->getType() &&
13430 "Trip count and induction variable must have the same type");
13431
13432 auto *CmpI = cast<CmpInst>(&Cond->front());
13433 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13434 "Exit condition must be a signed less-than comparison");
13435 assert(CmpI->getOperand(0) == IndVar &&
13436 "Exit condition must compare the induction variable");
13437 assert(CmpI->getOperand(1) == TripCount &&
13438 "Exit condition must compare with the trip count");
13439#endif
13440}
13441
13443 Header = nullptr;
13444 Cond = nullptr;
13445 Latch = nullptr;
13446 Exit = nullptr;
13447}
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.
InsertPoint - A saved insertion point.
Definition IRBuilder.h:246
BasicBlock * getBlock() const
Definition IRBuilder.h:261
bool isSet() const
Returns true if this insert point is set.
Definition IRBuilder.h:259
BasicBlock::iterator getPoint() const
Definition IRBuilder.h:262
Common base class shared among various IRBuilders.
Definition IRBuilder.h:114
InsertPoint saveIP() const
Returns the current insert point.
Definition IRBuilder.h:266
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:278
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2903
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:1767
iterator_range< op_iterator > operands()
Definition Metadata.h:1863
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 void createTaskwait(const LocationDescription &Loc, DependenciesInfo Dependencies={})
Generator for 'omp taskwait'.
LLVM_ABI Constant * getAddrOfDeclareTargetVar(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, Type *LlvmPtrTy, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage)
Retrieve (or create if non-existent) the address of a declare target variable, used in conjunction wi...
origPtr *with the address space normalization required by the runtime entry point *The NULL descriptor makes the runtime walk the enclosing taskgroups to *find the matching task_reduction registration for the item The lookups *are emitted at p Loc
EmitMetadataErrorKind
The kind of errors that can occur when emitting the offload entries and metadata.
unsigned getOpcode() const
Return the opcode for this Instruction or ConstantExpr.
Definition Operator.h:43
The optimization diagnostic interface.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
Pseudo-analysis pass that exposes the PassInstrumentation to pass managers.
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp: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:1137
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1197
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1211
bool isSystemZ() const
Tests whether the target is SystemZ.
Definition Triple.h:1194
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:81
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
Flag
These should be considered private to the implementation of the MCInstrDesc class.
constexpr StringLiteral MaxNTID("nvvm.maxntid")
constexpr StringLiteral MaxClusterRank("nvvm.maxclusterrank")
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
LLVM_ABI GlobalVariable * emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr=nullptr)
Definition Utility.cpp:104
OpenMPOffloadMappingFlags
Values for bit flags used to specify the mapping type for offloading.
@ OMP_MAP_PTR_AND_OBJ
The element being mapped is a pointer-pointee pair; both the pointer and the pointee should be mapped...
@ OMP_MAP_MEMBER_OF
The 16 MSBs of the flags indicate whether the entry is member of some struct/class.
IdentFlag
IDs for all omp runtime library ident_t flag encodings (see their defintion in openmp/runtime/src/kmp...
RuntimeFunction
IDs for all omp runtime library (RTL) functions.
constexpr const GV & getAMDGPUGridValues()
static constexpr GV SPIRVGridValues
For generic SPIR-V GPUs.
OMPDynGroupprivateFallbackType
The fallback types for the dyn_groupprivate clause.
static constexpr GV NVPTXGridValues
For Nvidia GPUs.
@ OMP_TGT_EXEC_MODE_SPMD_NO_LOOP
Function * Kernel
Summary of a kernel (=entry point for target offloading).
Definition OpenMPOpt.h:21
WorksharingLoopType
A type of worksharing loop construct.
EnumSet< Property, Property_enumSize > Properties
OMPAtomicCompareOp
Atomic compare operations. Currently OpenMP only supports ==, >, and <.
NodeAddr< PhiNode * > Phi
Definition RDFGraph.h:390
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h: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),...