LLVM 24.0.0git
OMPIRBuilder.cpp
Go to the documentation of this file.
1//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// This file implements the OpenMPIRBuilder class, which is used as a
11/// convenient way to create LLVM instructions for OpenMP directives.
12///
13//===----------------------------------------------------------------------===//
14
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/StringRef.h"
31#include "llvm/IR/Attributes.h"
32#include "llvm/IR/BasicBlock.h"
33#include "llvm/IR/CFG.h"
34#include "llvm/IR/CallingConv.h"
35#include "llvm/IR/Constant.h"
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/DIBuilder.h"
40#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
44#include "llvm/IR/LLVMContext.h"
45#include "llvm/IR/MDBuilder.h"
46#include "llvm/IR/Metadata.h"
48#include "llvm/IR/PassManager.h"
50#include "llvm/IR/Value.h"
53#include "llvm/Support/Error.h"
64
65#include <cstdint>
66#include <optional>
67
68#define DEBUG_TYPE "openmp-ir-builder"
69
70using namespace llvm;
71using namespace omp;
72
73static cl::opt<bool>
74 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
75 cl::desc("Use optimistic attributes describing "
76 "'as-if' properties of runtime calls."),
77 cl::init(false));
78
80 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
81 cl::desc("Factor for the unroll threshold to account for code "
82 "simplifications still taking place"),
83 cl::init(1.5));
84
86 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
87 cl::desc("Use a default max threads if none is provided."), cl::init(true));
88
89#ifndef NDEBUG
90/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
91/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
92/// an InsertPoint stores the instruction before something is inserted. For
93/// instance, if both point to the same instruction, two IRBuilders alternating
94/// creating instruction will cause the instructions to be interleaved.
97 if (!IP1.isValid() || !IP2.isValid())
98 return false;
99 return IP1 == IP2;
100}
101
103 // Valid ordered/unordered and base algorithm combinations.
104 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
105 case OMPScheduleType::UnorderedStaticChunked:
106 case OMPScheduleType::UnorderedStatic:
107 case OMPScheduleType::UnorderedDynamicChunked:
108 case OMPScheduleType::UnorderedGuidedChunked:
109 case OMPScheduleType::UnorderedRuntime:
110 case OMPScheduleType::UnorderedAuto:
111 case OMPScheduleType::UnorderedTrapezoidal:
112 case OMPScheduleType::UnorderedGreedy:
113 case OMPScheduleType::UnorderedBalanced:
114 case OMPScheduleType::UnorderedGuidedIterativeChunked:
115 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
116 case OMPScheduleType::UnorderedSteal:
117 case OMPScheduleType::UnorderedStaticBalancedChunked:
118 case OMPScheduleType::UnorderedGuidedSimd:
119 case OMPScheduleType::UnorderedRuntimeSimd:
120 case OMPScheduleType::OrderedStaticChunked:
121 case OMPScheduleType::OrderedStatic:
122 case OMPScheduleType::OrderedDynamicChunked:
123 case OMPScheduleType::OrderedGuidedChunked:
124 case OMPScheduleType::OrderedRuntime:
125 case OMPScheduleType::OrderedAuto:
126 case OMPScheduleType::OrderdTrapezoidal:
127 case OMPScheduleType::NomergeUnorderedStaticChunked:
128 case OMPScheduleType::NomergeUnorderedStatic:
129 case OMPScheduleType::NomergeUnorderedDynamicChunked:
130 case OMPScheduleType::NomergeUnorderedGuidedChunked:
131 case OMPScheduleType::NomergeUnorderedRuntime:
132 case OMPScheduleType::NomergeUnorderedAuto:
133 case OMPScheduleType::NomergeUnorderedTrapezoidal:
134 case OMPScheduleType::NomergeUnorderedGreedy:
135 case OMPScheduleType::NomergeUnorderedBalanced:
136 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
137 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
138 case OMPScheduleType::NomergeUnorderedSteal:
139 case OMPScheduleType::NomergeOrderedStaticChunked:
140 case OMPScheduleType::NomergeOrderedStatic:
141 case OMPScheduleType::NomergeOrderedDynamicChunked:
142 case OMPScheduleType::NomergeOrderedGuidedChunked:
143 case OMPScheduleType::NomergeOrderedRuntime:
144 case OMPScheduleType::NomergeOrderedAuto:
145 case OMPScheduleType::NomergeOrderedTrapezoidal:
146 case OMPScheduleType::OrderedDistributeChunked:
147 case OMPScheduleType::OrderedDistribute:
148 break;
149 default:
150 return false;
151 }
152
153 // Must not set both monotonicity modifiers at the same time.
154 OMPScheduleType MonotonicityFlags =
155 SchedType & OMPScheduleType::MonotonicityMask;
156 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
157 return false;
158
159 return true;
160}
161#endif
162
163/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
164/// debug location when the insert point is at the end of a block. It picks a
165/// location scoped to the current function: the block's last instruction
166/// location if the block is non-empty, otherwise a location synthesized from
167/// the function's subprogram (when the function has debug info).
170 Builder.restoreIP(IP);
171 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
172 // set the debug location from that instruction, so leave it alone.
173 llvm::BasicBlock *BB = Builder.GetInsertBlock();
174 if (Builder.GetInsertPoint() != BB->end())
175 return;
176
177 // At the end of a block, pick a location guaranteed to belong to the current
178 // insertion function's subprogram. Prefer the block's own last instruction;
179 // otherwise synthesize a location from the function's subprogram.
180 if (!BB->empty())
181 Builder.SetCurrentDebugLocation(BB->back().getDebugLoc());
182 else if (llvm::DISubprogram *FSP =
183 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
184 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
185 Builder.SetCurrentDebugLocation(
186 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
187 }
188}
189
190static bool hasGridValue(const Triple &T) {
191 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
192}
193
194static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
195 if (T.isAMDGPU()) {
196 StringRef Features =
197 Kernel->getFnAttribute("target-features").getValueAsString();
198 if (Features.count("+wavefrontsize64"))
201 }
202 if (T.isNVPTX())
204 if (T.isSPIRV())
206 llvm_unreachable("No grid value available for this architecture!");
207}
208
209/// Determine which scheduling algorithm to use, determined from schedule clause
210/// arguments.
211static OMPScheduleType
212getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
213 bool HasSimdModifier, bool HasDistScheduleChunks) {
214 // Currently, the default schedule it static.
215 switch (ClauseKind) {
216 case OMP_SCHEDULE_Default:
217 case OMP_SCHEDULE_Static:
218 return HasChunks ? OMPScheduleType::BaseStaticChunked
219 : OMPScheduleType::BaseStatic;
220 case OMP_SCHEDULE_Dynamic:
221 return OMPScheduleType::BaseDynamicChunked;
222 case OMP_SCHEDULE_Guided:
223 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
224 : OMPScheduleType::BaseGuidedChunked;
225 case OMP_SCHEDULE_Auto:
227 case OMP_SCHEDULE_Runtime:
228 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
229 : OMPScheduleType::BaseRuntime;
230 case OMP_SCHEDULE_Distribute:
231 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
232 : OMPScheduleType::BaseDistribute;
233 }
234 llvm_unreachable("unhandled schedule clause argument");
235}
236
237/// Adds ordering modifier flags to schedule type.
238static OMPScheduleType
240 bool HasOrderedClause) {
241 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
242 OMPScheduleType::None &&
243 "Must not have ordering nor monotonicity flags already set");
244
245 OMPScheduleType OrderingModifier = HasOrderedClause
246 ? OMPScheduleType::ModifierOrdered
247 : OMPScheduleType::ModifierUnordered;
248 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
249
250 // Unsupported combinations
251 if (OrderingScheduleType ==
252 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
253 return OMPScheduleType::OrderedGuidedChunked;
254 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
255 OMPScheduleType::ModifierOrdered))
256 return OMPScheduleType::OrderedRuntime;
257
258 return OrderingScheduleType;
259}
260
261/// Adds monotonicity modifier flags to schedule type.
262static OMPScheduleType
264 bool HasSimdModifier, bool HasMonotonic,
265 bool HasNonmonotonic, bool HasOrderedClause) {
266 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
267 OMPScheduleType::None &&
268 "Must not have monotonicity flags already set");
269 assert((!HasMonotonic || !HasNonmonotonic) &&
270 "Monotonic and Nonmonotonic are contradicting each other");
271
272 if (HasMonotonic) {
273 return ScheduleType | OMPScheduleType::ModifierMonotonic;
274 } else if (HasNonmonotonic) {
275 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
276 } else {
277 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
278 // If the static schedule kind is specified or if the ordered clause is
279 // specified, and if the nonmonotonic modifier is not specified, the
280 // effect is as if the monotonic modifier is specified. Otherwise, unless
281 // the monotonic modifier is specified, the effect is as if the
282 // nonmonotonic modifier is specified.
283 OMPScheduleType BaseScheduleType =
284 ScheduleType & ~OMPScheduleType::ModifierMask;
285 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
286 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
287 HasOrderedClause) {
288 // The monotonic is used by default in openmp runtime library, so no need
289 // to set it.
290 return ScheduleType;
291 } else {
292 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
293 }
294 }
295}
296
297/// Determine the schedule type using schedule and ordering clause arguments.
298static OMPScheduleType
299computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
300 bool HasSimdModifier, bool HasMonotonicModifier,
301 bool HasNonmonotonicModifier, bool HasOrderedClause,
302 bool HasDistScheduleChunks) {
304 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
305 OMPScheduleType OrderedSchedule =
306 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
308 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
309 HasNonmonotonicModifier, HasOrderedClause);
310
312 return Result;
313}
314
315/// Given a function, if it represents the entry point of a target kernel, this
316/// returns the execution mode flags associated with that kernel.
317static std::optional<omp::OMPTgtExecModeFlags>
319 CallInst *TargetInitCall = nullptr;
320 for (Instruction &Inst : Kernel.getEntryBlock()) {
321 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
322 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
323 TargetInitCall = Call;
324 break;
325 }
326 }
327 }
328
329 if (!TargetInitCall)
330 return std::nullopt;
331
332 // Get the kernel mode information from the global variable associated to the
333 // first argument to the call to __kmpc_target_init. Refer to
334 // createTargetInit() to see how this is initialized.
335 Value *InitOperand = TargetInitCall->getArgOperand(0);
336 GlobalVariable *KernelEnv = nullptr;
337 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
338 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
339 else
340 KernelEnv = cast<GlobalVariable>(InitOperand);
341 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
342 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
343 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
344 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
345}
346
347static bool isGenericKernel(Function &Fn) {
348 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
350 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
351}
352
353/// Make \p Source branch to \p Target.
354///
355/// Handles two situations:
356/// * \p Source already has an unconditional branch.
357/// * \p Source is a degenerate block (no terminator because the BB is
358/// the current head of the IR construction).
360 if (Instruction *Term = Source->getTerminatorOrNull()) {
361 auto *Br = cast<UncondBrInst>(Term);
362 BasicBlock *Succ = Br->getSuccessor();
363 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
364 Br->setSuccessor(Target);
365 return;
366 }
367
368 auto *NewBr = UncondBrInst::Create(Target, Source);
369 NewBr->setDebugLoc(DL);
370}
371
373 bool CreateBranch, DebugLoc DL) {
374 assert(New->getFirstInsertionPt() == New->begin() &&
375 "Target BB must not have PHI nodes");
376
377 // Move instructions to new block.
378 BasicBlock *Old = IP.getNodeParent();
379 // If the `Old` block is empty then there are no instructions to move. But in
380 // the new debug scheme, it could have trailing debug records which will be
381 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
382 // reasons:
383 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
384 // 2. Even if `New` is not empty, the rationale to move those records to `New`
385 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
386 // assumes that `Old` is optimized out and is going away. This is not the case
387 // here. The `Old` block is still being used e.g. a branch instruction is
388 // added to it later in this function.
389 // So we call `BasicBlock::splice` only when `Old` is not empty.
390 if (!Old->empty())
391 New->splice(New->begin(), Old, IP, Old->end());
392
393 if (CreateBranch) {
394 auto *NewBr = UncondBrInst::Create(New, Old);
395 NewBr->setDebugLoc(DL);
396 }
397}
398
399void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
400 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
401 BasicBlock *Old = Builder.GetInsertBlock();
402
403 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
404 if (CreateBranch)
405 Builder.SetInsertPoint(Old->getTerminator());
406 else
407 Builder.SetInsertPoint(Old);
408
409 // SetInsertPoint also updates the Builder's debug location, but we want to
410 // keep the one the Builder was configured to use.
411 Builder.SetCurrentDebugLocation(DebugLoc);
412}
413
415 DebugLoc DL, llvm::Twine Name) {
416 BasicBlock *Old = IP.getNodeParent();
418 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
419 Old->getParent(), Old->getNextNode());
420 spliceBB(IP, New, CreateBranch, DL);
421 New->replaceSuccessorsPhiUsesWith(Old, New);
422 return New;
423}
424
425BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
426 llvm::Twine Name) {
427 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
428 BasicBlock *Old = Builder.GetInsertBlock();
429 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
430 if (CreateBranch)
431 Builder.SetInsertPoint(Old->getTerminator());
432 else
433 Builder.SetInsertPoint(Old);
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 *Old = Builder.GetInsertBlock();
444 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
445 if (CreateBranch)
446 Builder.SetInsertPoint(Old->getTerminator());
447 else
448 Builder.SetInsertPoint(Old);
449 // SetInsertPoint also updates the Builder's debug location, but we want to
450 // keep the one the Builder was configured to use.
451 Builder.SetCurrentDebugLocation(DebugLoc);
452 return New;
453}
454
456 llvm::Twine Suffix) {
457 BasicBlock *Old = Builder.GetInsertBlock();
458 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
459}
460
461// This function creates a fake integer value and a fake use for the integer
462// value. It returns the fake value created. This is useful in modeling the
463// extra arguments to the outlined functions.
465 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
467 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
468 const Twine &Name = "", bool AsPtr = true,
469 bool Is64Bit = false) {
470 Builder.restoreIP(OuterAllocaIP);
471 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
472 Instruction *FakeVal;
473 AllocaInst *FakeValAddr =
474 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
475 ToBeDeleted.push_back(FakeValAddr);
476
477 if (AsPtr) {
478 FakeVal = FakeValAddr;
479 // The runtime passes these extra arguments to the outlined function as
480 // generic pointers, so cast away a non-zero alloca address space.
481 if (FakeValAddr->getAddressSpace() != 0) {
482 FakeVal = cast<Instruction>(Builder.CreateAddrSpaceCast(
483 FakeValAddr, Builder.getPtrTy(), Name + ".ascast"));
484 ToBeDeleted.push_back(FakeVal);
485 }
486 } else {
487 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
488 ToBeDeleted.push_back(FakeVal);
489 }
490
491 // Generate a fake use of this value
492 Builder.restoreIP(InnerAllocaIP);
493 Instruction *UseFakeVal;
494 if (AsPtr) {
495 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
496 } else {
497 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
498 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
499 }
500 ToBeDeleted.push_back(UseFakeVal);
501 return FakeVal;
502}
503
504//===----------------------------------------------------------------------===//
505// OpenMPIRBuilderConfig
506//===----------------------------------------------------------------------===//
507
508namespace {
510/// Values for bit flags for marking which requires clauses have been used.
511enum OpenMPOffloadingRequiresDirFlags {
512 /// flag undefined.
513 OMP_REQ_UNDEFINED = 0x000,
514 /// no requires directive present.
515 OMP_REQ_NONE = 0x001,
516 /// reverse_offload clause.
517 OMP_REQ_REVERSE_OFFLOAD = 0x002,
518 /// unified_address clause.
519 OMP_REQ_UNIFIED_ADDRESS = 0x004,
520 /// unified_shared_memory clause.
521 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
522 /// dynamic_allocators clause.
523 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
524 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
525};
526
527class OMPCodeExtractor : public CodeExtractor {
528public:
529 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
530 DominatorTree *DT = nullptr, bool AggregateArgs = false,
531 BlockFrequencyInfo *BFI = nullptr,
532 BranchProbabilityInfo *BPI = nullptr,
533 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
534 bool AllowAlloca = false,
535 BasicBlock *AllocationBlock = nullptr,
536 ArrayRef<BasicBlock *> DeallocationBlocks = {},
537 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
538 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
539 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
540 ArgsInZeroAddressSpace),
541 OMPBuilder(OMPBuilder) {}
542
543 virtual ~OMPCodeExtractor() = default;
544
545protected:
546 OpenMPIRBuilder &OMPBuilder;
547};
548
549class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
550public:
551 using OMPCodeExtractor::OMPCodeExtractor;
552 virtual ~DeviceSharedMemCodeExtractor() = default;
553
554protected:
555 virtual Instruction *
556 allocateVar(IRBuilder<>::InsertPoint AllocaIP, DebugLoc DL, Type *VarType,
557 const Twine &Name = Twine(""),
558 AddrSpaceCastInst **CastedAlloc = nullptr) override {
559 return OMPBuilder.createOMPAllocShared({AllocaIP, DL}, VarType, Name);
560 }
561
562 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
563 DebugLoc DL, Value *Var,
564 Type *VarType) override {
565 return OMPBuilder.createOMPFreeShared({DeallocIP, DL}, Var, VarType);
566 }
567};
568
569/// Helper storing information about regions to outline using device shared
570/// memory for intermediate allocations.
571struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
572 OpenMPIRBuilder &OMPBuilder;
573
574 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
575 : OMPBuilder(OMPBuilder) {}
576 virtual ~DeviceSharedMemOutlineInfo() = default;
577
578 virtual std::unique_ptr<CodeExtractor>
579 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
580 bool ArgsInZeroAddressSpace,
581 Twine Suffix = Twine("")) override;
582};
583
584} // anonymous namespace
585
587 : RequiresFlags(OMP_REQ_UNDEFINED) {}
588
591 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
592 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
595 RequiresFlags(OMP_REQ_UNDEFINED) {
596 if (HasRequiresReverseOffload)
597 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
598 if (HasRequiresUnifiedAddress)
599 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
600 if (HasRequiresUnifiedSharedMemory)
601 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
602 if (HasRequiresDynamicAllocators)
603 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
604}
605
607 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
608}
609
611 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
612}
613
615 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
616}
617
619 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
620}
621
623 return hasRequiresFlags() ? RequiresFlags
624 : static_cast<int64_t>(OMP_REQ_NONE);
625}
626
628 if (Value)
629 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
630 else
631 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
632}
633
635 if (Value)
636 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
637 else
638 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
639}
640
642 if (Value)
643 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
644 else
645 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
646}
647
649 if (Value)
650 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
651 else
652 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
653}
654
655//===----------------------------------------------------------------------===//
656// OpenMPIRBuilder
657//===----------------------------------------------------------------------===//
658
661 SmallVector<Value *> &ArgsVector) {
663 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
664 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
665 constexpr size_t MaxDim = 3;
666 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
667
668 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
669
670 Value *DynCGroupMemFallbackFlag =
671 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
672 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
673
674 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
675 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
676
677 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
678 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
679
680 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
681 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
682 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
683
684 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
685
686 Value *NumTeams3D =
687 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
688 Value *NumThreads3D =
689 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
690 for (unsigned I :
691 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
692 NumTeams3D =
693 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
694 for (unsigned I :
695 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
696 NumThreads3D =
697 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
698
699 ArgsVector = {Version,
700 PointerNum,
701 KernelArgs.RTArgs.BasePointersArray,
702 KernelArgs.RTArgs.PointersArray,
703 KernelArgs.RTArgs.SizesArray,
704 KernelArgs.RTArgs.MapTypesArray,
705 KernelArgs.RTArgs.MapNamesArray,
706 KernelArgs.RTArgs.MappersArray,
707 KernelArgs.NumIterations,
708 Flags,
709 NumTeams3D,
710 NumThreads3D,
711 KernelArgs.DynCGroupMem};
712}
713
715 LLVMContext &Ctx = Fn.getContext();
716
717 // Get the function's current attributes.
718 auto Attrs = Fn.getAttributes();
719 auto FnAttrs = Attrs.getFnAttrs();
720 auto RetAttrs = Attrs.getRetAttrs();
722 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
723 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
724
725 // Add AS to FnAS while taking special care with integer extensions.
726 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
727 bool Param = true) -> void {
728 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
729 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
730 if (HasSignExt || HasZeroExt) {
731 assert(AS.getNumAttributes() == 1 &&
732 "Currently not handling extension attr combined with others.");
733 if (Param) {
734 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
735 FnAS = FnAS.addAttribute(Ctx, AK);
736 } else if (auto AK =
737 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
738 FnAS = FnAS.addAttribute(Ctx, AK);
739 } else {
740 FnAS = FnAS.addAttributes(Ctx, AS);
741 }
742 };
743
744#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
745#include "llvm/Frontend/OpenMP/OMPKinds.def"
746
747 // Add attributes to the function declaration.
748 switch (FnID) {
749#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
750 case Enum: \
751 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
752 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
753 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
754 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
755 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
756 break;
757#include "llvm/Frontend/OpenMP/OMPKinds.def"
758 default:
759 // Attributes are optional.
760 break;
761 }
762}
763
766 FunctionType *FnTy = nullptr;
767 Function *Fn = nullptr;
768
769 // Try to find the declation in the module first.
770 switch (FnID) {
771#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
772 case Enum: \
773 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
774 IsVarArg); \
775 Fn = M.getFunction(Str); \
776 break;
777#include "llvm/Frontend/OpenMP/OMPKinds.def"
778 }
779
780 if (!Fn) {
781 // Create a new declaration if we need one.
782 switch (FnID) {
783#define OMP_RTL(Enum, Str, ...) \
784 case Enum: \
785 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
786 break;
787#include "llvm/Frontend/OpenMP/OMPKinds.def"
788 }
789 Fn->setCallingConv(Config.getRuntimeCC());
790 // Add information if the runtime function takes a callback function
791 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
792 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
793 LLVMContext &Ctx = Fn->getContext();
794 MDBuilder MDB(Ctx);
795 // Annotate the callback behavior of the runtime function:
796 // - The callback callee is argument number 2 (microtask).
797 // - The first two arguments of the callback callee are unknown (-1).
798 // - All variadic arguments to the runtime function are passed to the
799 // callback callee.
800 Fn->addMetadata(
801 LLVMContext::MD_callback,
803 2, {-1, -1}, /* VarArgsArePassed */ true)}));
804 }
805 }
806
807 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
808 << " with type " << *Fn->getFunctionType() << "\n");
809 addAttributes(FnID, *Fn);
810
811 } else {
812 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
813 << " with type " << *Fn->getFunctionType() << "\n");
814 }
815
816 assert(Fn && "Failed to create OpenMP runtime function");
817
818 return {FnTy, Fn};
819}
820
823 if (!FiniBB) {
824 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
826 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
827 Builder.SetInsertPoint(FiniBB);
828 // FiniCB adds the branch to the exit stub.
829 if (Error Err = FiniCB(Builder.saveIP()))
830 return Err;
831 }
832 return FiniBB;
833}
834
836 BasicBlock *OtherFiniBB) {
837 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
838 if (!FiniBB) {
839 FiniBB = OtherFiniBB;
840
841 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
842 if (Error Err = FiniCB(Builder.saveIP()))
843 return Err;
844
845 return Error::success();
846 }
847
848 // Move instructions from FiniBB to the start of OtherFiniBB.
849 auto EndIt = FiniBB->end();
850 if (FiniBB->size() >= 1)
851 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
852 EndIt = Prev;
853 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
854 EndIt);
855
856 FiniBB->replaceAllUsesWith(OtherFiniBB);
857 FiniBB->eraseFromParent();
858 FiniBB = OtherFiniBB;
859 return Error::success();
860}
861
864 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
865 assert(Fn && "Failed to create OpenMP runtime function pointer");
866 return Fn;
867}
868
871 StringRef Name) {
872 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
873 Call->setCallingConv(Config.getRuntimeCC());
874 return Call;
875}
876
877void OpenMPIRBuilder::initialize() { initializeTypes(M); }
878
881 BasicBlock &EntryBlock = Function->getEntryBlock();
882 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
883
884 // Loop over blocks looking for constant allocas, skipping the entry block
885 // as any allocas there are already in the desired location.
886 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
887 Block++) {
888 for (auto Inst = Block->getReverseIterator()->begin();
889 Inst != Block->getReverseIterator()->end();) {
891 Inst++;
893 continue;
894 AllocaInst->moveBeforePreserving(MoveLocInst);
895 } else {
896 Inst++;
897 }
898 }
899 }
900}
901
904
905 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
906 // TODO: For now, we support simple static allocations, we might need to
907 // move non-static ones as well. However, this will need further analysis to
908 // move the lenght arguments as well.
910 };
911
912 for (llvm::Instruction &Inst : Block)
914 if (ShouldHoistAlloca(*AllocaInst))
915 AllocasToMove.push_back(AllocaInst);
916
917 auto InsertPoint =
918 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
919
920 for (llvm::Instruction *AllocaInst : AllocasToMove)
922}
923
925 PostDominatorTree PostDomTree(*Func);
926 for (llvm::BasicBlock &BB : *Func)
927 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
929}
930
932 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
934 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
935 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
936 // Skip functions that have not finalized yet; may happen with nested
937 // function generation.
938 if (Fn && OI->getFunction() != Fn) {
939 DeferredOutlines.push_back(std::move(OI));
940 continue;
941 }
942
943 ParallelRegionBlockSet.clear();
944 Blocks.clear();
945 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
946
947 Function *OuterFn = OI->getFunction();
948 CodeExtractorAnalysisCache CEAC(*OuterFn);
949 // If we generate code for the target device, we need to allocate
950 // struct for aggregate params in the device default alloca address space.
951 // OpenMP runtime requires that the params of the extracted functions are
952 // passed as zero address space pointers. This flag ensures that
953 // CodeExtractor generates correct code for extracted functions
954 // which are used by OpenMP runtime.
955 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
956 std::unique_ptr<CodeExtractor> Extractor =
957 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
958
959 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
960 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
961 << " Exit: " << OI->ExitBB->getName() << "\n");
962 assert(Extractor->isEligible() &&
963 "Expected OpenMP outlining to be possible!");
964
965 for (auto *V : OI->ExcludeArgsFromAggregate)
966 Extractor->excludeArgFromAggregate(V);
967
968 Function *OutlinedFn =
969 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
970
971 // Forward target-cpu, target-features attributes to the outlined function.
972 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
973 if (TargetCpuAttr.isStringAttribute())
974 OutlinedFn->addFnAttr(TargetCpuAttr);
975
976 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
977 if (TargetFeaturesAttr.isStringAttribute())
978 OutlinedFn->addFnAttr(TargetFeaturesAttr);
979
980 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
981 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
982 assert(OutlinedFn->getReturnType()->isVoidTy() &&
983 "OpenMP outlined functions should not return a value!");
984
985 // For compability with the clang CG we move the outlined function after the
986 // one with the parallel region.
987 OutlinedFn->removeFromParent();
988 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
989
990 // Remove the artificial entry introduced by the extractor right away, we
991 // made our own entry block after all.
992 {
993 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
994 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
995 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
996 // Move instructions from the to-be-deleted ArtificialEntry to the entry
997 // basic block of the parallel region. CodeExtractor generates
998 // instructions to unwrap the aggregate argument and may sink
999 // allocas/bitcasts for values that are solely used in the outlined region
1000 // and do not escape.
1001 assert(!ArtificialEntry.empty() &&
1002 "Expected instructions to add in the outlined region entry");
1003 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
1004 End = ArtificialEntry.rend();
1005 It != End;) {
1006 Instruction &I = *It;
1007 It++;
1008
1009 if (I.isTerminator()) {
1010 // Absorb any debug value that terminator may have
1011 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1012 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1013 continue;
1014 }
1015
1016 I.moveBeforePreserving(*OI->EntryBB,
1017 OI->EntryBB->getFirstInsertionPt());
1018 }
1019
1020 OI->EntryBB->moveBefore(&ArtificialEntry);
1021 ArtificialEntry.eraseFromParent();
1022 }
1023 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1024 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1025
1026 // Run a user callback, e.g. to add attributes.
1027 if (OI->PostOutlineCB)
1028 OI->PostOutlineCB(*OutlinedFn);
1029
1030 if (OI->FixUpNonEntryAllocas)
1032 }
1033
1034 // Remove work items that have been completed.
1035 OutlineInfos = std::move(DeferredOutlines);
1036
1037 // The createTarget functions embeds user written code into
1038 // the target region which may inject allocas which need to
1039 // be moved to the entry block of our target or risk malformed
1040 // optimisations by later passes (however, we do not block on
1041 // that here, it's up to the inserter to the list to do so).
1042 // This notbaly has to occur after the OutlinedInfo candidates
1043 // have been extracted so we have an end product that will not
1044 // be implicitly adversely affected by any raises unless
1045 // intentionally appended to the list.
1046 // NOTE: This only does so for ConstantData, it could be extended
1047 // to ConstantExpr's with further effort, however, they should
1048 // largely be folded when they get here. Extending it to runtime
1049 // defined/read+writeable allocation sizes would be non-trivial
1050 // (need to factor in movement of any stores to variables the
1051 // allocation size depends on, as well as the usual loads,
1052 // otherwise it'll yield the wrong result after movement) and
1053 // likely be more suitable as an LLVM optimisation pass.
1056
1057 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1058 [](EmitMetadataErrorKind Kind,
1059 const TargetRegionEntryInfo &EntryInfo) -> void {
1060 errs() << "Error of kind: " << Kind
1061 << " when emitting offload entries and metadata during "
1062 "OMPIRBuilder finalization \n";
1063 };
1064
1065 if (!OffloadInfoManager.empty())
1067
1068 // Rewrite uses of globals to their replacement declare target globals if
1069 // we are processing a device module.
1070 if (Config.isTargetDevice())
1071 applyDeclareTargetGlobalReplacements();
1072
1073 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1074 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1075 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1076 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1077 }
1078
1079 IsFinalized = true;
1080}
1081
1082bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1083
1085 GlobalValue *Original, GlobalValue *Replacement) {
1086 assert(Original && Replacement &&
1087 "Null values provided to registerDeclareTargetGlobalReplacement");
1088 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1089}
1090
1091void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1092 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1093 GlobalValue *OldGV = R.Original;
1094 GlobalValue *NewGV = R.Replacement;
1095
1096 assert(OldGV && NewGV &&
1097 "A null value was inserted into DeclareTargetGlobalReplacements");
1098
1099 // The assert above should catch this case, but this is kept to attempt
1100 // to proceed without issue when asserts are off.
1101 if (!OldGV || !NewGV)
1102 continue;
1103
1104 // The replacement global is a reference pointer that holds the
1105 // address of the device-resident storage. Every use must load the
1106 // reference pointer first and use the loaded address.
1107 //
1108 // Constant expression users (e.g. a constant GEP embedded in another
1109 // global's initializer or in an instruction) cannot have a load inserted
1110 // in place, so first expand any constant-expression users that live inside
1111 // functions into instructions. Any remaining constant users are handled
1112 // via a direct constant rewrite below as we cannot materialize a load
1113 // there.
1114 //
1115 // NOTE: We extend the constant rewrite to module scope, as we replace all
1116 // usages.
1117 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1119 /*RestrictToFunc=*/nullptr,
1120 /*RemoveDeadConstants=*/false);
1121
1122 IRBuilderBase::InsertPointGuard Guard(Builder);
1124 for (User *U : Users) {
1125 auto *Insn = dyn_cast<Instruction>(U);
1126 if (!Insn)
1127 continue;
1128
1129 // A PHI node cannot have a load inserted immediately before it, as PHIs
1130 // must remain grouped at the top of their basic block. So we need to
1131 // make sure any loads we emit are generated in the preceding edge, a
1132 // PHI may reference the global on more than one edge, so every matching
1133 // slot must be handled.
1134 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1135 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1136 if (PHI->getIncomingValue(I) != OldGV)
1137 continue;
1138
1139 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1140 Builder.SetInsertPoint(IncomingBB->getTerminator());
1141 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1142 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1143 PHI->setIncomingValue(I, EdgeLoad);
1144 }
1145 continue;
1146 }
1147
1148 Builder.SetInsertPoint(Insn);
1149 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1150 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1151
1152 // The replacement declare target global lives in the default address
1153 // space, whereas the original global may reside in a non-default
1154 // address space. In that case the initial lowering may have
1155 // emitted an addrspacecast that is no longer valid. Replace the
1156 // whole addrspacecast with the load and erase it rather than
1157 // feeding the load back into the (now pointless) cast.
1158 // NOTE: If we end up with replacement declare target globals in
1159 // non-zero AS's the below will need some minor extensions to have the
1160 // option to alter the address space cast to the new address space where
1161 // required rather than just replacing it.
1162 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1163 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1164 assert(NewGVAS == 0 &&
1165 "Non-default address space declare target global");
1166 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1167 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1168 if (DestAS == 0 && NewGVAS != OldGVAS) {
1169 ASC->replaceAllUsesWith(Load);
1170 ASC->eraseFromParent();
1171 continue;
1172 }
1173 }
1174
1175 Insn->replaceUsesOfWith(OldGV, Load);
1176 }
1177 }
1178
1180}
1181
1183 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1184}
1185
1187 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1188 auto *GV =
1189 new GlobalVariable(M, I32Ty,
1190 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1191 ConstantInt::get(I32Ty, Value), Name);
1192 GV->setVisibility(GlobalValue::HiddenVisibility);
1193
1194 return GV;
1195}
1196
1198 if (List.empty())
1199 return;
1200
1201 // Convert List to what ConstantArray needs.
1203 UsedArray.resize(List.size());
1204 for (unsigned I = 0, E = List.size(); I != E; ++I)
1206 cast<Constant>(&*List[I]), Builder.getPtrTy());
1207
1208 if (UsedArray.empty())
1209 return;
1210 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1211
1212 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1213 ConstantArray::get(ATy, UsedArray), Name);
1214
1215 GV->setSection("llvm.metadata");
1216}
1217
1220 OMPTgtExecModeFlags Mode) {
1221 auto *Int8Ty = Builder.getInt8Ty();
1222 auto *GVMode = new GlobalVariable(
1223 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1224 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1225 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1226 return GVMode;
1227}
1228
1230 uint32_t SrcLocStrSize,
1231 IdentFlag LocFlags,
1232 unsigned Reserve2Flags) {
1233 // Enable "C-mode".
1234 LocFlags |= OMP_IDENT_FLAG_KMPC;
1235
1236 Constant *&Ident =
1237 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1238 if (!Ident) {
1239 Constant *I32Null = ConstantInt::getNullValue(Int32);
1240 Constant *IdentData[] = {I32Null,
1241 ConstantInt::get(Int32, uint32_t(LocFlags)),
1242 ConstantInt::get(Int32, Reserve2Flags),
1243 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1244
1245 size_t SrcLocStrArgIdx = 4;
1246 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1248 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1249 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1250 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1251 Constant *Initializer =
1252 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1253
1254 // Look for existing encoding of the location + flags, not needed but
1255 // minimizes the difference to the existing solution while we transition.
1256 for (GlobalVariable &GV : M.globals())
1257 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1258 if (GV.getInitializer() == Initializer)
1259 Ident = &GV;
1260
1261 if (!Ident) {
1262 auto *GV = new GlobalVariable(
1263 M, OpenMPIRBuilder::Ident,
1264 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1266 M.getDataLayout().getDefaultGlobalsAddressSpace());
1267 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1268 GV->setAlignment(Align(8));
1269 Ident = GV;
1270 }
1271 }
1272
1273 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1274}
1275
1277 uint32_t &SrcLocStrSize) {
1278 SrcLocStrSize = LocStr.size();
1279 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1280 if (!SrcLocStr) {
1281 Constant *Initializer =
1282 ConstantDataArray::getString(M.getContext(), LocStr);
1283
1284 // Look for existing encoding of the location, not needed but minimizes the
1285 // difference to the existing solution while we transition.
1286 for (GlobalVariable &GV : M.globals())
1287 if (GV.isConstant() && GV.hasInitializer() &&
1288 GV.getInitializer() == Initializer)
1289 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1290
1291 SrcLocStr = Builder.CreateGlobalString(
1292 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1293 &M);
1294 }
1295 return SrcLocStr;
1296}
1297
1299 StringRef FileName,
1300 unsigned Line, unsigned Column,
1301 uint32_t &SrcLocStrSize) {
1302 SmallString<128> Buffer;
1303 Buffer.push_back(';');
1304 Buffer.append(FileName);
1305 Buffer.push_back(';');
1306 Buffer.append(FunctionName);
1307 Buffer.push_back(';');
1308 Buffer.append(std::to_string(Line));
1309 Buffer.push_back(';');
1310 Buffer.append(std::to_string(Column));
1311 Buffer.push_back(';');
1312 Buffer.push_back(';');
1313 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1314}
1315
1316Constant *
1318 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1319 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1320}
1321
1323 uint32_t &SrcLocStrSize,
1324 const Function *F) {
1325 DILocation *DIL = DL.get();
1326 if (!DIL)
1327 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1328 StringRef FileName =
1329 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1330 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1331 if (Function.empty() && F)
1332 Function = F->getName();
1333 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1334 DIL->getColumn(), SrcLocStrSize);
1335}
1336
1338 uint32_t &SrcLocStrSize) {
1339 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1340 Loc.IP.getNodeParent()->getParent());
1341}
1342
1345 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1346 "omp_global_thread_num");
1347}
1348
1349OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1350 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1351 ArrayRef<Type *> ResultPtrTys,
1352 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1353 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1354 "expected one result pointer type per in_reduction item");
1355 if (!updateToLocation(Loc))
1356 return Loc.IP;
1357 if (OrigPtrs.empty())
1358 return Builder.saveIP();
1359
1360 // Compute the executing thread's gtid once for the whole target body and
1361 // reuse it for every in_reduction lookup, so a target with several
1362 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1363 // item.
1364 uint32_t SrcLocStrSize;
1365 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1366 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1367 Value *Gtid = getOrCreateThreadID(Ident);
1368
1369 // The runtime entry point takes (and returns) a generic, default-address-
1370 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1371 // taskgroups to find the matching task_reduction registration for the item.
1372 Type *PtrTy = PointerType::getUnqual(M.getContext());
1373 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1374 FunctionCallee GetThData =
1375 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1376
1377 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1378 // Normalize a non-default-address-space original pointer to the generic
1379 // address space before the call.
1380 Value *OrigPtr = OrigPtrs[Idx];
1381 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1382 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1383 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1384
1385 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1386 "omp.inred.priv");
1387
1388 // Cast the returned private pointer back to the requested address space
1389 // when it differs.
1390 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1391 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1392 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1393
1394 MapPrivateCB(Idx, Priv);
1395 }
1396 return Builder.saveIP();
1397}
1398
1401 bool ForceSimpleCall, bool CheckCancelFlag) {
1402 if (!updateToLocation(Loc))
1403 return Loc.IP;
1404
1405 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1406 // __kmpc_barrier(loc, thread_id);
1407
1408 IdentFlag BarrierLocFlags;
1409 switch (Kind) {
1410 case OMPD_for:
1411 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1412 break;
1413 case OMPD_sections:
1414 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1415 break;
1416 case OMPD_single:
1417 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1418 break;
1419 case OMPD_barrier:
1420 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1421 break;
1422 default:
1423 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1424 break;
1425 }
1426
1427 uint32_t SrcLocStrSize;
1428 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1429 Value *Args[] = {
1430 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1431 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1432
1433 // If we are in a cancellable parallel region, barriers are cancellation
1434 // points.
1435 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1436 bool UseCancelBarrier =
1437 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1438
1440 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1441 ? OMPRTL___kmpc_cancel_barrier
1442 : OMPRTL___kmpc_barrier),
1443 Args);
1444
1445 if (UseCancelBarrier && CheckCancelFlag)
1446 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1447 return Err;
1448
1449 return Builder.saveIP();
1450}
1451
1454 Value *IfCondition,
1455 omp::Directive CanceledDirective) {
1456 if (!updateToLocation(Loc))
1457 return Loc.IP;
1458
1459 // LLVM utilities like blocks with terminators.
1460 auto *UI = Builder.CreateUnreachable();
1461
1462 Instruction *ThenTI = UI, *ElseTI = nullptr;
1463 if (IfCondition) {
1464 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1465
1466 // Even if the if condition evaluates to false, this should count as a
1467 // cancellation point
1468 Builder.SetInsertPoint(ElseTI);
1469 auto ElseIP = Builder.saveIP();
1470
1472 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1473 if (!IPOrErr)
1474 return IPOrErr;
1475 }
1476
1477 Builder.SetInsertPoint(ThenTI);
1478
1479 Value *CancelKind = nullptr;
1480 switch (CanceledDirective) {
1481#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1482 case DirectiveEnum: \
1483 CancelKind = Builder.getInt32(Value); \
1484 break;
1485#include "llvm/Frontend/OpenMP/OMPKinds.def"
1486 default:
1487 llvm_unreachable("Unknown cancel kind!");
1488 }
1489
1490 uint32_t SrcLocStrSize;
1491 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1492 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1493 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1495 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1496
1497 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1498 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1499 return Err;
1500
1501 // Update the insertion point and remove the terminator we introduced.
1502 Builder.SetInsertPoint(UI->getParent());
1503 UI->eraseFromParent();
1504
1505 return Builder.saveIP();
1506}
1507
1510 omp::Directive CanceledDirective) {
1511 if (!updateToLocation(Loc))
1512 return Loc.IP;
1513
1514 // LLVM utilities like blocks with terminators.
1515 auto *UI = Builder.CreateUnreachable();
1516 Builder.SetInsertPoint(UI);
1517
1518 Value *CancelKind = nullptr;
1519 switch (CanceledDirective) {
1520#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1521 case DirectiveEnum: \
1522 CancelKind = Builder.getInt32(Value); \
1523 break;
1524#include "llvm/Frontend/OpenMP/OMPKinds.def"
1525 default:
1526 llvm_unreachable("Unknown cancel kind!");
1527 }
1528
1529 uint32_t SrcLocStrSize;
1530 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1531 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1532 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1534 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1535
1536 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1537 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1538 return Err;
1539
1540 // Update the insertion point and remove the terminator we introduced.
1541 Builder.SetInsertPoint(UI->getParent());
1542 UI->eraseFromParent();
1543
1544 return Builder.saveIP();
1545}
1546
1548 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1549 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1550 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1551 if (!updateToLocation(Loc))
1552 return Loc.IP;
1553
1554 Builder.restoreIP(AllocaIP);
1555 auto *KernelArgsPtr =
1556 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1558
1559 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1560 llvm::Value *Arg =
1561 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1562 Builder.CreateAlignedStore(
1563 KernelArgs[I], Arg,
1564 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1565 }
1566
1567 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1568 NumThreads, HostPtr, KernelArgsPtr};
1569
1571 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1572 OffloadingArgs);
1573
1574 return Builder.saveIP();
1575}
1576
1578 const LocationDescription &Loc, Value *OutlinedFnID,
1579 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1580 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1581
1582 if (!updateToLocation(Loc))
1583 return Loc.IP;
1584
1585 // On top of the arrays that were filled up, the target offloading call
1586 // takes as arguments the device id as well as the host pointer. The host
1587 // pointer is used by the runtime library to identify the current target
1588 // region, so it only has to be unique and not necessarily point to
1589 // anything. It could be the pointer to the outlined function that
1590 // implements the target region, but we aren't using that so that the
1591 // compiler doesn't need to keep that, and could therefore inline the host
1592 // function if proven worthwhile during optimization.
1593
1594 // From this point on, we need to have an ID of the target region defined.
1595 assert(OutlinedFnID && "Invalid outlined function ID!");
1596 (void)OutlinedFnID;
1597
1598 // Return value of the runtime offloading call.
1599 Value *Return = nullptr;
1600
1601 // Arguments for the target kernel.
1602 SmallVector<Value *> ArgsVector;
1603 getKernelArgsVector(Args, Builder, ArgsVector);
1604
1605 // The target region is an outlined function launched by the runtime
1606 // via calls to __tgt_target_kernel().
1607 //
1608 // Note that on the host and CPU targets, the runtime implementation of
1609 // these calls simply call the outlined function without forking threads.
1610 // The outlined functions themselves have runtime calls to
1611 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1612 // the compiler in emitTeamsCall() and emitParallelCall().
1613 //
1614 // In contrast, on the NVPTX target, the implementation of
1615 // __tgt_target_teams() launches a GPU kernel with the requested number
1616 // of teams and threads so no additional calls to the runtime are required.
1617 // Check the error code and execute the host version if required.
1618 Builder.restoreIP(emitTargetKernel(
1619 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1620 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1621
1622 BasicBlock *OffloadFailedBlock =
1623 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1624 BasicBlock *OffloadContBlock =
1625 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1626 Value *Failed = Builder.CreateIsNotNull(Return);
1627 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1628
1629 auto CurFn = Builder.GetInsertBlock()->getParent();
1630 emitBlock(OffloadFailedBlock, CurFn);
1631 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1632 if (!AfterIP)
1633 return AfterIP.takeError();
1634 Builder.restoreIP(*AfterIP);
1635 emitBranch(OffloadContBlock);
1636 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1637 return Builder.saveIP();
1638}
1639
1641 Value *CancelFlag, omp::Directive CanceledDirective) {
1642 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1643 "Unexpected cancellation!");
1644
1645 // For a cancel barrier we create two new blocks.
1646 BasicBlock *BB = Builder.GetInsertBlock();
1647 BasicBlock *NonCancellationBlock;
1648 if (Builder.GetInsertPoint() == BB->end()) {
1649 // TODO: This branch will not be needed once we moved to the
1650 // OpenMPIRBuilder codegen completely.
1651 NonCancellationBlock = BasicBlock::Create(
1652 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1653 } else {
1654 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1656 Builder.SetInsertPoint(BB);
1657 }
1658 BasicBlock *CancellationBlock = BasicBlock::Create(
1659 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1660
1661 // Jump to them based on the return value.
1662 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1663 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1664 /* TODO weight */ nullptr, nullptr);
1665
1666 // From the cancellation block we finalize all variables and go to the
1667 // post finalization block that is known to the FiniCB callback.
1668 auto &FI = FinalizationStack.back();
1669 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1670 if (!FiniBBOrErr)
1671 return FiniBBOrErr.takeError();
1672 Builder.SetInsertPoint(CancellationBlock);
1673 Builder.CreateBr(*FiniBBOrErr);
1674
1675 // The continuation block is where code generation continues.
1676 Builder.SetInsertPoint(NonCancellationBlock->begin());
1677 return Error::success();
1678}
1679
1680/// Create wrapper function used to gather the outlined function's argument
1681/// structure from a shared buffer and to forward them to it when running in
1682/// Generic mode.
1683///
1684/// The outlined function is expected to receive 2 integer arguments followed by
1685/// an optional pointer argument to an argument structure holding the rest.
1687 Function &OutlinedFn) {
1688 size_t NumArgs = OutlinedFn.arg_size();
1689 assert((NumArgs == 2 || NumArgs == 3) &&
1690 "expected a 2-3 argument parallel outlined function");
1691 bool UseArgStruct = NumArgs == 3;
1692
1693 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1694 IRBuilder<>::InsertPointGuard IPG(Builder);
1695 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1696 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1697 /*isVarArg=*/false);
1698 auto *WrapperFn =
1700 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1701
1702 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1703 WrapperFn->addParamAttr(0, Attribute::ZExt);
1704 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1705
1706 BasicBlock *EntryBB =
1707 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1708 Builder.SetInsertPoint(EntryBB);
1709
1710 // Allocation.
1711 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1712 /*ArraySize=*/nullptr, "addr");
1713 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1714 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1715 AddrAlloca->getName() + ".ascast");
1716
1717 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1718 /*ArraySize=*/nullptr, "zero");
1719 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1720 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1721 ZeroAlloca->getName() + ".ascast");
1722
1723 Value *ArgsAlloca = nullptr;
1724 if (UseArgStruct) {
1725 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1726 /*ArraySize=*/nullptr, "global_args");
1727 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1728 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1729 ArgsAlloca->getName() + ".ascast");
1730 }
1731
1732 // Initialization.
1733 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1734 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1735 if (UseArgStruct) {
1736 Builder.CreateCall(
1737 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1738 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1739 {ArgsAlloca});
1740 }
1741
1742 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1743
1744 // Load structArg from global_args.
1745 if (UseArgStruct) {
1746 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1747 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1748 {Builder.getInt64(0)});
1749 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1750 Args.push_back(StructArg);
1751 }
1752
1753 // Call the outlined function holding the parallel body.
1754 Builder.CreateCall(&OutlinedFn, Args);
1755 Builder.CreateRetVoid();
1756
1757 return WrapperFn;
1758}
1759
1760// Callback used to create OpenMP runtime calls to support
1761// omp parallel clause for the device.
1762// We need to use this callback to replace call to the OutlinedFn in OuterFn
1763// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1765 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1766 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1767 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1768 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1769 assert(OutlinedFn.arg_size() >= 2 &&
1770 "Expected at least tid and bounded tid as arguments");
1771 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1772
1773 // Add some known attributes.
1774 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1775 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1776 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1777 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1778 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1779 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1780
1781 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1782 assert(CI && "Expected call instruction to outlined function");
1783 CI->getParent()->setName("omp_parallel");
1784
1785 Builder.SetInsertPoint(CI);
1786 Type *PtrTy = OMPIRBuilder->VoidPtr;
1787
1788 // Add alloca for kernel args
1789 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1790 Builder.SetInsertPoint(OuterAllocaBB->getFirstInsertionPt());
1791 AllocaInst *ArgsAlloca =
1792 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1793 Value *Args = ArgsAlloca;
1794 // Add address space cast if array for storing arguments is not allocated
1795 // in address space 0
1796 if (ArgsAlloca->getAddressSpace())
1797 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1798 Builder.restoreIP(CurrentIP);
1799
1800 // Store captured vars which are used by kmpc_parallel_60
1801 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1802 Value *V = *(CI->arg_begin() + 2 + Idx);
1803 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1804 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1805 Builder.CreateStore(V, StoreAddress);
1806 }
1807
1808 Value *Cond =
1809 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1810 : Builder.getInt32(1);
1811 Value *NumThreadsArg =
1812 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1813 : Builder.getInt32(-1);
1814
1815 // If this is not a Generic kernel, we can skip generating the wrapper.
1816 Value *WrapperFn;
1817 if (isGenericKernel(*OuterFn))
1818 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1819 else
1820 WrapperFn = Constant::getNullValue(PtrTy);
1821
1822 // Build kmpc_parallel_60 call
1823 Value *Parallel60CallArgs[] = {
1824 /* identifier*/ Ident,
1825 /* global thread num*/ ThreadID,
1826 /* if expression */ Cond,
1827 /* number of threads */ NumThreadsArg,
1828 /* Proc bind */ Builder.getInt32(-1),
1829 /* outlined function */ &OutlinedFn,
1830 /* wrapper function */ WrapperFn,
1831 /* arguments of the outlined funciton*/ Args,
1832 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1833 /* strict for number of threads */ Builder.getInt32(0)};
1834
1835 FunctionCallee RTLFn =
1836 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1837
1838 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1839
1840 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1841 << *Builder.GetInsertBlock()->getParent() << "\n");
1842
1843 // Initialize the local TID stack location with the argument value.
1844 Builder.SetInsertPoint(PrivTID);
1845 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1846 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1847 PrivTIDAddr);
1848
1849 // Remove redundant call to the outlined function.
1850 CI->eraseFromParent();
1851
1852 for (Instruction *I : ToBeDeleted) {
1853 I->eraseFromParent();
1854 }
1855}
1856
1857// Callback used to create OpenMP runtime calls to support
1858// omp parallel clause for the host.
1859// We need to use this callback to replace call to the OutlinedFn in OuterFn
1860// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1861static void
1863 Function *OuterFn, Value *Ident, Value *IfCondition,
1864 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1865 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1866 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1867 FunctionCallee RTLFn;
1868 if (IfCondition) {
1869 RTLFn =
1870 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1871 } else {
1872 RTLFn =
1873 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1874 }
1875 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1876 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1877 LLVMContext &Ctx = F->getContext();
1878 MDBuilder MDB(Ctx);
1879 // Annotate the callback behavior of the __kmpc_fork_call:
1880 // - The callback callee is argument number 2 (microtask).
1881 // - The first two arguments of the callback callee are unknown (-1).
1882 // - All variadic arguments to the __kmpc_fork_call are passed to the
1883 // callback callee.
1884 F->addMetadata(LLVMContext::MD_callback,
1886 2, {-1, -1},
1887 /* VarArgsArePassed */ true)}));
1888 }
1889 }
1890 // Add some known attributes.
1891 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1892 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1893 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1894
1895 assert(OutlinedFn.arg_size() >= 2 &&
1896 "Expected at least tid and bounded tid as arguments");
1897 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1898
1899 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1900
1901 // CodeExtractor creates an alloca aggregate struct containing pointers to all
1902 // the captured values. The address of this struct is then passed as a
1903 // parameter to the outlined function. This parameter of the outlined function
1904 // can be marked noalias because CodeExractor + this host OpenMP lowering
1905 // maintains the following invariants:
1906 // - The caller initializes the fields before starting any callbacks.
1907 // - Neither the caller, OpenMP runtime, nor callbacks modify the structure
1908 // while callbacks are executing.
1909 // - The callbacks only load captured values from the structure; they do not
1910 // use it to write results back to the caller. This is generated code; the
1911 // user has no way to modify the structure directly.
1912 //
1913 // Multiple callbacks may read the same structure. This is allowed by noalias,
1914 // which permits aliasing accesses to memory that is not modified during the
1915 // call.
1916 //
1917 // The synchronous fork keeps the structure alive until all callbacks return,
1918 // which justifies nofreeobj. Neither attribute extends to the objects pointed
1919 // to by the captured fields.
1920 if (NumCapturedVars) {
1921 assert(NumCapturedVars == 1 && "Expected an aggregate capture argument");
1924 ->getAllocatedType()
1925 ->isStructTy() &&
1926 "Expected a stack-allocated capture structure");
1927
1928 OutlinedFn.addParamAttr(2, Attribute::NoAlias);
1929 OutlinedFn.addParamAttr(2, Attribute::NoFreeObj);
1930 }
1931
1932 CI->getParent()->setName("omp_parallel");
1933 Builder.SetInsertPoint(CI);
1934
1935 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1936 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1937 &OutlinedFn};
1938
1939 SmallVector<Value *, 16> RealArgs;
1940 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1941 if (IfCondition) {
1942 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1943 RealArgs.push_back(Cond);
1944 }
1945 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1946
1947 // __kmpc_fork_call_if always expects a void ptr as the last argument
1948 // If there are no arguments, pass a null pointer.
1949 auto PtrTy = OMPIRBuilder->VoidPtr;
1950 if (IfCondition && NumCapturedVars == 0) {
1951 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1952 RealArgs.push_back(NullPtrValue);
1953 }
1954
1955 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1956
1957 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1958 << *Builder.GetInsertBlock()->getParent() << "\n");
1959
1960 // Initialize the local TID stack location with the argument value.
1961 Builder.SetInsertPoint(PrivTID);
1962 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1963 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1964 PrivTIDAddr);
1965
1966 // Remove redundant call to the outlined function.
1967 CI->eraseFromParent();
1968
1969 for (Instruction *I : ToBeDeleted) {
1970 I->eraseFromParent();
1971 }
1972}
1973
1975 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1976 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1977 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1978 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1979 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1980
1981 if (!updateToLocation(Loc))
1982 return Loc.IP;
1983
1984 uint32_t SrcLocStrSize;
1985 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1986 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1987 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1988 (ProcBind != OMP_PROC_BIND_default);
1989 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1990 // If we generate code for the target device, we need to allocate
1991 // struct for aggregate params in the device default alloca address space.
1992 // OpenMP runtime requires that the params of the extracted functions are
1993 // passed as zero address space pointers. This flag ensures that extracted
1994 // function arguments are declared in zero address space
1995 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1996
1997 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1998 // only if we compile for host side.
1999 if (NumThreads && !Config.isTargetDevice()) {
2000 Value *Args[] = {
2001 Ident, ThreadID,
2002 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
2004 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
2005 }
2006
2007 if (ProcBind != OMP_PROC_BIND_default) {
2008 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
2009 Value *Args[] = {
2010 Ident, ThreadID,
2011 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
2013 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
2014 }
2015
2016 BasicBlock *InsertBB = Builder.GetInsertBlock();
2017 Function *OuterFn = InsertBB->getParent();
2018
2019 // Save the outer alloca block because the insertion iterator may get
2020 // invalidated and we still need this later.
2021 BasicBlock *OuterAllocaBlock = OuterAllocIP.getNodeParent();
2022
2023 // Vector to remember instructions we used only during the modeling but which
2024 // we want to delete at the end.
2026
2027 // Change the location to the outer alloca insertion point to create and
2028 // initialize the allocas we pass into the parallel region.
2029 InsertPointTy NewOuter(OuterAllocaBlock->begin());
2030 Builder.restoreIP(NewOuter);
2031 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
2032 AllocaInst *ZeroAddrAlloca =
2033 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
2034 Instruction *TIDAddr = TIDAddrAlloca;
2035 Instruction *ZeroAddr = ZeroAddrAlloca;
2036 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
2037 // Add additional casts to enforce pointers in zero address space
2038 TIDAddr = new AddrSpaceCastInst(
2039 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2040 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2041 ToBeDeleted.push_back(TIDAddr);
2042 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2043 PointerType ::get(M.getContext(), 0),
2044 "zero.addr.ascast");
2045 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2046 ToBeDeleted.push_back(ZeroAddr);
2047 }
2048
2049 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2050 // associated arguments in the outlined function, so we delete them later.
2051 ToBeDeleted.push_back(TIDAddrAlloca);
2052 ToBeDeleted.push_back(ZeroAddrAlloca);
2053
2054 // Create an artificial insertion point that will also ensure the blocks we
2055 // are about to split are not degenerated.
2056 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2057 UI->setDebugLoc(Loc.DL);
2058
2059 BasicBlock *EntryBB = UI->getParent();
2060 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2061 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2062 BasicBlock *PRegPreFiniBB =
2063 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2064 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2065
2066 auto FiniCBWrapper = [&](InsertPointTy IP) {
2067 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2068 // target to the region exit block.
2069 if (IP == IP.getNodeParent()->end()) {
2071 Builder.restoreIP(IP);
2072 Instruction *I = Builder.CreateBr(PRegExitBB);
2073 IP = I->getIterator();
2074 }
2075 assert(IP.getNodeParent()->getTerminator()->getNumSuccessors() == 1 &&
2076 IP.getNodeParent()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2077 "Unexpected insertion point for finalization call!");
2078 return FiniCB(IP);
2079 };
2080
2081 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2082
2083 // Generate the privatization allocas in the block that will become the entry
2084 // of the outlined function.
2085 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2086 InsertPointTy InnerAllocaIP = Builder.saveIP();
2087
2088 AllocaInst *PrivTIDAddr =
2089 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2090 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2091
2092 // Add some fake uses for OpenMP provided arguments.
2093 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2094 Instruction *ZeroAddrUse =
2095 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2096 ToBeDeleted.push_back(ZeroAddrUse);
2097
2098 // EntryBB
2099 // |
2100 // V
2101 // PRegionEntryBB <- Privatization allocas are placed here.
2102 // |
2103 // V
2104 // PRegionBodyBB <- BodeGen is invoked here.
2105 // |
2106 // V
2107 // PRegPreFiniBB <- The block we will start finalization from.
2108 // |
2109 // V
2110 // PRegionExitBB <- A common exit to simplify block collection.
2111 //
2112
2113 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2114
2115 // Let the caller create the body.
2116 assert(BodyGenCB && "Expected body generation callback!");
2117 InsertPointTy CodeGenIP(PRegBodyBB->begin());
2118 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2119 return Err;
2120
2121 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2122
2123 // If OuterFn is a Generic kernel, we need to use device shared memory to
2124 // allocate argument structures. Otherwise, we use stack allocations as usual.
2125 bool UsesDeviceSharedMemory =
2126 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2127 std::unique_ptr<OutlineInfo> OI =
2128 UsesDeviceSharedMemory
2129 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2130 : std::make_unique<OutlineInfo>();
2131
2132 if (Config.isTargetDevice()) {
2133 // Generate OpenMP target specific runtime call
2134 OI->PostOutlineCB = [=, ToBeDeletedVec =
2135 std::move(ToBeDeleted)](Function &OutlinedFn) {
2136 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2137 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2138 ThreadID, ToBeDeletedVec);
2139 };
2140 } else {
2141 // Generate OpenMP host runtime call
2142 OI->PostOutlineCB = [=, ToBeDeletedVec =
2143 std::move(ToBeDeleted)](Function &OutlinedFn) {
2144 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2145 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2146 };
2147 }
2148
2149 OI->FixUpNonEntryAllocas = true;
2150 OI->OuterAllocBB = OuterAllocaBlock;
2151 OI->EntryBB = PRegEntryBB;
2152 OI->ExitBB = PRegExitBB;
2153 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2154 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2155
2156 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2158 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2159
2160 CodeExtractorAnalysisCache CEAC(*OuterFn);
2161 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2162 /* AggregateArgs */ false,
2163 /* BlockFrequencyInfo */ nullptr,
2164 /* BranchProbabilityInfo */ nullptr,
2165 /* AssumptionCache */ nullptr,
2166 /* AllowVarArgs */ true,
2167 /* AllowAlloca */ true,
2168 /* AllocationBlock */ OuterAllocaBlock,
2169 /* DeallocationBlocks */ {},
2170 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2171
2172 // Find inputs to, outputs from the code region.
2173 BasicBlock *CommonExit = nullptr;
2174 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2175 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2176
2177 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2178 /*CollectGlobalInputs=*/true);
2179
2180 Inputs.remove_if([&](Value *I) {
2182 return GV->getValueType() == OpenMPIRBuilder::Ident;
2183
2184 return false;
2185 });
2186
2187 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2188
2189 FunctionCallee TIDRTLFn =
2190 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2191
2192 auto PrivHelper = [&](Value &V) -> Error {
2193 if (&V == TIDAddr || &V == ZeroAddr) {
2194 OI->ExcludeArgsFromAggregate.push_back(&V);
2195 return Error::success();
2196 }
2197
2199 for (Use &U : V.uses())
2200 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2201 if (ParallelRegionBlockSet.count(UserI->getParent()))
2202 Uses.insert(&U);
2203
2204 // __kmpc_fork_call expects extra arguments as pointers. If the input
2205 // already has a pointer type, everything is fine. Otherwise, store the
2206 // value onto stack and load it back inside the to-be-outlined region. This
2207 // will ensure only the pointer will be passed to the function.
2208 // FIXME: if there are more than 15 trailing arguments, they must be
2209 // additionally packed in a struct.
2210 Value *Inner = &V;
2211 if (!V.getType()->isPointerTy()) {
2213 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2214
2215 Builder.restoreIP(OuterAllocIP);
2216 Value *Ptr;
2217 if (UsesDeviceSharedMemory) {
2218 // Use device shared memory instead, if needed.
2219 Ptr = createOMPAllocShared(Builder, V.getType(),
2220 V.getName() + ".reloaded");
2221 for (BasicBlock *DeallocBlock : OuterDeallocBlocks) {
2222 assert(DeallocBlock->getParent() ==
2223 OuterAllocIP.getNodeParent()->getParent() &&
2224 "Dealloc block must be in the allocation's function to reuse "
2225 "its debug location");
2226 createOMPFreeShared({DeallocBlock->getFirstInsertionPt(),
2227 Builder.getCurrentDebugLocation()},
2228 Ptr, V.getType());
2229 }
2230 } else {
2231 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2232 V.getName() + ".reloaded");
2233 }
2234
2235 // Store to stack at end of the block that currently branches to the entry
2236 // block of the to-be-outlined region.
2237 Builder.SetInsertPoint(InsertBB->getTerminator()->getIterator());
2238 Builder.CreateStore(&V, Ptr);
2239
2240 // Load back next to allocations in the to-be-outlined region.
2241 Builder.restoreIP(InnerAllocaIP);
2242 Inner = Builder.CreateLoad(V.getType(), Ptr);
2243 }
2244
2245 Value *ReplacementValue = nullptr;
2246 CallInst *CI = dyn_cast<CallInst>(&V);
2247 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2248 ReplacementValue = PrivTID;
2249 } else {
2250 InsertPointOrErrorTy AfterIP =
2251 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2252 if (!AfterIP)
2253 return AfterIP.takeError();
2254 Builder.restoreIP(*AfterIP);
2255 InnerAllocaIP =
2256 InnerAllocaIP.getNodeParent()->getTerminator()->getIterator();
2257
2258 assert(ReplacementValue &&
2259 "Expected copy/create callback to set replacement value!");
2260 if (ReplacementValue == &V)
2261 return Error::success();
2262 }
2263
2264 for (Use *UPtr : Uses)
2265 UPtr->set(ReplacementValue);
2266
2267 return Error::success();
2268 };
2269
2270 // Reset the inner alloca insertion as it will be used for loading the values
2271 // wrapped into pointers before passing them into the to-be-outlined region.
2272 // Configure it to insert immediately after the fake use of zero address so
2273 // that they are available in the generated body and so that the
2274 // OpenMP-related values (thread ID and zero address pointers) remain leading
2275 // in the argument list.
2276 InnerAllocaIP = ZeroAddrUse->getNextNode()->getIterator();
2277
2278 // Reset the outer alloca insertion point to the entry of the relevant block
2279 // in case it was invalidated.
2280 OuterAllocIP = OuterAllocaBlock->getFirstInsertionPt();
2281
2282 for (Value *Input : Inputs) {
2283 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2284 if (Error Err = PrivHelper(*Input))
2285 return Err;
2286 }
2287 LLVM_DEBUG({
2288 for (Value *Output : Outputs)
2289 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2290 });
2291 assert(Outputs.empty() &&
2292 "OpenMP outlining should not produce live-out values!");
2293
2294 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2295 LLVM_DEBUG({
2296 for (auto *BB : Blocks)
2297 dbgs() << " PBR: " << BB->getName() << "\n";
2298 });
2299
2300 // Adjust the finalization stack, verify the adjustment, and call the
2301 // finalize function a last time to finalize values between the pre-fini
2302 // block and the exit block if we left the parallel "the normal way".
2303 auto FiniInfo = FinalizationStack.pop_back_val();
2304 (void)FiniInfo;
2305 assert(FiniInfo.DK == OMPD_parallel &&
2306 "Unexpected finalization stack state!");
2307
2308 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2309
2310 InsertPointTy PreFiniIP(PRegPreFiniTI->getIterator());
2311 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2312 if (!FiniBBOrErr)
2313 return FiniBBOrErr.takeError();
2314 {
2316 Builder.restoreIP(PreFiniIP);
2317 Builder.CreateBr(*FiniBBOrErr);
2318 // There's currently a branch to omp.par.exit. Delete it. We will get there
2319 // via the fini block
2320 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2321 Term->eraseFromParent();
2322 }
2323
2324 // Register the outlined info.
2325 addOutlineInfo(std::move(OI));
2326
2327 InsertPointTy AfterIP(UI->getParent()->end());
2328 UI->eraseFromParent();
2329
2330 return AfterIP;
2331}
2332
2334 // Build call void __kmpc_flush(ident_t *loc)
2335 uint32_t SrcLocStrSize;
2336 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2337 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2338
2340 Args);
2341}
2342
2344 if (!updateToLocation(Loc))
2345 return;
2346 emitFlush(Loc);
2347}
2348
2350 Value *Message) {
2351 if (!updateToLocation(Loc))
2352 return;
2353
2354 // Build call void __kmpc_error(ident_t *loc, int severity,
2355 // const char *message)
2356 uint32_t SrcLocStrSize;
2357 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2358 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2359 // Severity: 1 = warning, 2 = fatal.
2360 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2361 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2362 Value *Args[] = {Ident, Severity, MessageArg};
2363
2365 Args);
2366}
2367
2369 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2370 uint32_t SrcLocStrSize;
2371 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2372 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2373 Constant *I32Null = ConstantInt::getNullValue(Int32);
2374 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2375
2377 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2378}
2379
2385
2387 const DependData &Dep) {
2388 // Store the pointer to the variable
2389 Value *Addr = Builder.CreateStructGEP(
2390 DependInfo, Entry,
2391 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2392 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2393 Builder.CreateStore(DepValPtr, Addr);
2394 // Store the size of the variable
2395 Value *Size = Builder.CreateStructGEP(
2396 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2397 Builder.CreateStore(
2398 ConstantInt::get(SizeTy,
2399 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2400 Size);
2401 // Store the dependency kind
2402 Value *Flags = Builder.CreateStructGEP(
2403 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2404 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2405 static_cast<unsigned int>(Dep.DepKind)),
2406 Flags);
2407}
2408
2409// Processes the dependencies in Dependencies and does the following
2410// - Allocates space on the stack of an array of DependInfo objects
2411// - Populates each DependInfo object with relevant information of
2412// the corresponding dependence.
2413// - All code is inserted in the entry block of the current function.
2415 OpenMPIRBuilder &OMPBuilder,
2417 // Early return if we have no dependencies to process
2418 if (Dependencies.empty())
2419 return nullptr;
2420
2421 // Given a vector of DependData objects, in this function we create an
2422 // array on the stack that holds kmp_depend_info objects corresponding
2423 // to each dependency. This is then passed to the OpenMP runtime.
2424 // For example, if there are 'n' dependencies then the following psedo
2425 // code is generated. Assume the first dependence is on a variable 'a'
2426 //
2427 // \code{c}
2428 // DepArray = alloc(n x sizeof(kmp_depend_info);
2429 // idx = 0;
2430 // DepArray[idx].base_addr = ptrtoint(&a);
2431 // DepArray[idx].len = 8;
2432 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2433 // ++idx;
2434 // DepArray[idx].base_addr = ...;
2435 // \endcode
2436
2437 IRBuilderBase &Builder = OMPBuilder.Builder;
2438 Type *DependInfo = OMPBuilder.DependInfo;
2439
2440 Value *DepArray = nullptr;
2441 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2442 {
2443 // Use a InsertPointGuard to restore the location back along with the
2444 // insertion point.
2445 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2446 Builder.SetInsertPoint(
2447 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2448 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2449 }
2450
2451 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2452 Value *Base =
2453 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2454 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2455 }
2456 return DepArray;
2457}
2458
2460 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2461 // global_tid);
2462 uint32_t SrcLocStrSize;
2463 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2464 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2465 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2466
2467 // Ignore return result until untied tasks are supported.
2469 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2470}
2471
2473 DependenciesInfo Dependencies,
2474 bool IsNowait) {
2475 if (!updateToLocation(Loc))
2476 return;
2477
2478 Value *DepArray = nullptr;
2479 Type *DepArrayTy = nullptr;
2480 Value *NumDeps = nullptr;
2481 if (Dependencies.DepArray) {
2482 DepArray = Dependencies.DepArray;
2483 NumDeps = Dependencies.NumDeps;
2484 } else if (!Dependencies.Deps.empty()) {
2485 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2486 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2487 {
2489 BasicBlock &entryBB =
2490 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2491 Builder.SetInsertPoint(entryBB.getFirstInsertionPt());
2492 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2493 }
2494
2495 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2496 Value *Base =
2497 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2498 this->emitTaskDependency(Builder, Base, Dep);
2499 }
2500 }
2501
2502 if (DepArray) {
2503 uint32_t SrcLocStrSize;
2504 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2505 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2506 Value *Args[] = {
2507 Ident,
2508 getOrCreateThreadID(Ident),
2509 NumDeps,
2510 DepArray,
2511 ConstantInt::get(Builder.getInt32Ty(), 0),
2513 ConstantInt::get(Builder.getInt32Ty(), IsNowait)};
2516 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2517 Args);
2518 } else {
2520 }
2521}
2522
2523/// Create the task duplication function passed to kmpc_taskloop.
2524Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2525 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2526 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2527 if (!DupCB)
2529 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2530
2531 // From OpenMP Runtime p_task_dup_t:
2532 // Routine optionally generated by the compiler for setting the lastprivate
2533 // flag and calling needed constructors for private/firstprivate objects (used
2534 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2535 // lastprivate flag.
2536 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2537
2538 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2539
2540 FunctionType *DupFuncTy = FunctionType::get(
2541 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2542 /*isVarArg=*/false);
2543
2544 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2545 "omp_taskloop_dup", M);
2546 Value *DestTaskArg = DupFunction->getArg(0);
2547 Value *SrcTaskArg = DupFunction->getArg(1);
2548 Value *LastprivateFlagArg = DupFunction->getArg(2);
2549 DestTaskArg->setName("dest_task");
2550 SrcTaskArg->setName("src_task");
2551 LastprivateFlagArg->setName("lastprivate_flag");
2552
2553 IRBuilderBase::InsertPointGuard Guard(Builder);
2554 Builder.SetInsertPoint(
2555 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2556
2557 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2558 Type *TaskWithPrivatesTy =
2559 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2560 Value *TaskPrivates = Builder.CreateGEP(
2561 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2562 Value *ContextPtr = Builder.CreateGEP(
2563 PrivatesTy, TaskPrivates,
2564 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2565 return ContextPtr;
2566 };
2567
2568 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2569 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2570
2571 DestTaskContextPtr->setName("destPtr");
2572 SrcTaskContextPtr->setName("srcPtr");
2573
2574 InsertPointTy AllocaIP(DupFunction->getEntryBlock().begin());
2575 InsertPointTy CodeGenIP = Builder.saveIP();
2576 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2577 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2578 if (!AfterIPOrError)
2579 return AfterIPOrError.takeError();
2580 Builder.restoreIP(*AfterIPOrError);
2581
2582 Builder.CreateRetVoid();
2583
2584 return DupFunction;
2585}
2586
2587OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2588 const LocationDescription &Loc, InsertPointTy AllocaIP,
2589 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2590 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2591 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2592 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2593 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2594 Value *TaskContextStructPtrVal, bool FreeAgent) {
2595
2596 if (!updateToLocation(Loc))
2597 return InsertPointTy();
2598
2599 uint32_t SrcLocStrSize;
2600 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2601 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2602
2603 BasicBlock *TaskloopExitBB =
2604 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2605 BasicBlock *TaskloopBodyBB =
2606 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2607 BasicBlock *TaskloopAllocaBB =
2608 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2609
2610 InsertPointTy TaskloopAllocaIP = TaskloopAllocaBB->begin();
2611 InsertPointTy TaskloopBodyIP = TaskloopBodyBB->begin();
2612
2613 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2614 return Err;
2615
2616 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2617 if (!result) {
2618 return result.takeError();
2619 }
2620
2621 llvm::CanonicalLoopInfo *CLI = result.get();
2622 auto OI = std::make_unique<OutlineInfo>();
2623 OI->EntryBB = TaskloopAllocaBB;
2624 OI->OuterAllocBB = AllocaIP.getNodeParent();
2625 OI->ExitBB = TaskloopExitBB;
2626 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2627 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2628
2629 // Add the thread ID argument.
2630 SmallVector<Instruction *> ToBeDeleted;
2631 // dummy instruction to be used as a fake argument
2632 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2633 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2634 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2635 TaskloopAllocaIP, "lb", false, true);
2636 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2637 TaskloopAllocaIP, "ub", false, true);
2638 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2639 TaskloopAllocaIP, "step", false, true);
2640 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2641 // aggregate struct
2642 OI->Inputs.insert(FakeLB);
2643 OI->Inputs.insert(FakeUB);
2644 OI->Inputs.insert(FakeStep);
2645 if (TaskContextStructPtrVal)
2646 OI->Inputs.insert(TaskContextStructPtrVal);
2647 assert(((TaskContextStructPtrVal && DupCB) ||
2648 (!TaskContextStructPtrVal && !DupCB)) &&
2649 "Task context struct ptr and duplication callback must be both set "
2650 "or both null");
2651
2652 // It isn't safe to run the duplication bodygen callback inside the post
2653 // outlining callback so this has to be run now before we know the real task
2654 // shareds structure type.
2655 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2656 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2657 Type *FakeSharedsTy = StructType::get(
2658 Builder.getContext(),
2659 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2660 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2661 FakeSharedsTy,
2662 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2663 if (!TaskDupFnOrErr) {
2664 return TaskDupFnOrErr.takeError();
2665 }
2666 Value *TaskDupFn = *TaskDupFnOrErr;
2667
2668 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2669 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2670 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2671 FakeSharedsTy, Final, Mergeable, Priority,
2672 NumOfCollapseLoops,
2673 FreeAgent](Function &OutlinedFn) mutable {
2674 // Replace the Stale CI by appropriate RTL function call.
2675 assert(OutlinedFn.hasOneUse() &&
2676 "there must be a single user for the outlined function");
2677 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2678
2679 /* Create the casting for the Bounds Values that can be used when outlining
2680 * to replace the uses of the fakes with real values */
2681 BasicBlock *CodeReplBB = StaleCI->getParent();
2682 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2683 Value *CastedLBVal =
2684 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2685 Value *CastedUBVal =
2686 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2687 Value *CastedStepVal =
2688 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2689
2690 Builder.SetInsertPoint(StaleCI);
2691
2692 // Gather the arguments for emitting the runtime call for
2693 // @__kmpc_omp_task_alloc
2694 Function *TaskAllocFn =
2695 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2696
2697 Value *ThreadID = getOrCreateThreadID(Ident);
2698
2699 if (!NoGroup) {
2700 // Emit runtime call for @__kmpc_taskgroup
2701 Function *TaskgroupFn =
2702 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2703 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2704 }
2705
2706 // `flags` Argument Configuration
2707 // Task is tied if (Flags & 1) == 1.
2708 // Task is untied if (Flags & 1) == 0.
2709 // Task is final if (Flags & 2) == 2.
2710 // Task is not final if (Flags & 2) == 0.
2711 // Task is mergeable if (Flags & 4) == 4.
2712 // Task is not mergeable if (Flags & 4) == 0.
2713 // Task is priority if (Flags & 32) == 32.
2714 // Task is not priority if (Flags & 32) == 0.
2715 // Task is free-agent eligible if (Flags & 128) == 128.
2716 // Task is not free-agent eligible if (Flags & 128) == 0.
2717 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2718 if (Final)
2719 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2720 if (Mergeable)
2721 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2722 if (Priority)
2723 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2724 if (FreeAgent)
2725 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2726
2727 Value *TaskSize = Builder.getInt64(
2728 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2729
2730 AllocaInst *ArgStructAlloca =
2732 assert(ArgStructAlloca &&
2733 "Unable to find the alloca instruction corresponding to arguments "
2734 "for extracted function");
2735 std::optional<TypeSize> ArgAllocSize =
2736 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2737 assert(ArgAllocSize &&
2738 "Unable to determine size of arguments for extracted function");
2739 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2740
2741 // Emit the @__kmpc_omp_task_alloc runtime call
2742 // The runtime call returns a pointer to an area where the task captured
2743 // variables must be copied before the task is run (TaskData)
2744 CallInst *TaskData = Builder.CreateCall(
2745 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2746 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2747 /*task_func=*/&OutlinedFn});
2748
2749 Value *Shareds = StaleCI->getArgOperand(1);
2750 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2751 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2752 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2753 SharedsSize);
2754 // Get the pointer to loop lb, ub, step from task ptr
2755 // and set up the lowerbound,upperbound and step values
2756 llvm::Value *Lb = Builder.CreateGEP(
2757 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2758
2759 llvm::Value *Ub = Builder.CreateGEP(
2760 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2761
2762 llvm::Value *Step = Builder.CreateGEP(
2763 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2764 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2765
2766 // set up the arguments for emitting kmpc_taskloop runtime call
2767 // setting values for ifval, nogroup, sched, grainsize, task_dup
2768 Value *IfCondVal =
2769 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2770 : Builder.getInt32(1);
2771 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2772 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2773 Value *NoGroupVal = Builder.getInt32(1);
2774 Value *SchedVal = Builder.getInt32(Sched);
2775 Value *GrainSizeVal =
2776 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2777 : Builder.getInt64(0);
2778 Value *TaskDup = TaskDupFn;
2779
2780 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2781 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2782
2783 // taskloop runtime call
2784 Function *TaskloopFn =
2785 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2786 Builder.CreateCall(TaskloopFn, Args);
2787
2788 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2789 // nogroup is not defined
2790 if (!NoGroup) {
2791 Function *EndTaskgroupFn =
2792 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2793 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2794 }
2795
2796 StaleCI->eraseFromParent();
2797
2798 Builder.SetInsertPoint(TaskloopAllocaBB->begin());
2799
2800 LoadInst *SharedsOutlined =
2801 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2802 OutlinedFn.getArg(1)->replaceUsesWithIf(
2803 SharedsOutlined,
2804 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2805
2806 Value *IV = CLI->getIndVar();
2807 Type *IVTy = IV->getType();
2808 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2809
2810 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2811 // UpperBound. These GEP's can be reused for loading the tasks respective
2812 // bounds.
2813 Value *TaskLB = nullptr;
2814 Value *TaskUB = nullptr;
2815 Value *TaskStep = nullptr;
2816 Value *LoadTaskLB = nullptr;
2817 Value *LoadTaskUB = nullptr;
2818 Value *LoadTaskStep = nullptr;
2819 for (Instruction &I : *TaskloopAllocaBB) {
2820 if (I.getOpcode() == Instruction::GetElementPtr) {
2821 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2822 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2823 switch (CI->getZExtValue()) {
2824 case 0:
2825 TaskLB = &I;
2826 break;
2827 case 1:
2828 TaskUB = &I;
2829 break;
2830 case 2:
2831 TaskStep = &I;
2832 break;
2833 }
2834 }
2835 } else if (I.getOpcode() == Instruction::Load) {
2836 LoadInst &Load = cast<LoadInst>(I);
2837 if (Load.getPointerOperand() == TaskLB) {
2838 assert(TaskLB != nullptr && "Expected value for TaskLB");
2839 LoadTaskLB = &I;
2840 } else if (Load.getPointerOperand() == TaskUB) {
2841 assert(TaskUB != nullptr && "Expected value for TaskUB");
2842 LoadTaskUB = &I;
2843 } else if (Load.getPointerOperand() == TaskStep) {
2844 assert(TaskStep != nullptr && "Expected value for TaskStep");
2845 LoadTaskStep = &I;
2846 }
2847 }
2848 }
2849
2850 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2851
2852 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2853 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2854 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2855 Value *TripCountMinusOne = Builder.CreateSDiv(
2856 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2857 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2858 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2859 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2860 // set the trip count in the CLI
2861 CLI->setTripCount(CastedTripCount);
2862
2863 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
2864
2865 if (NumOfCollapseLoops > 1) {
2866 llvm::SmallVector<User *> UsersToReplace;
2867 // When using the collapse clause, the bounds of the loop have to be
2868 // adjusted to properly represent the iterator of the outer loop.
2869 Value *IVPlusTaskLB = Builder.CreateAdd(
2870 CLI->getIndVar(),
2871 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2872 // To ensure every Use is correctly captured, we first want to record
2873 // which users to replace the value in, and then replace the value.
2874 for (auto IVUse = CLI->getIndVar()->uses().begin();
2875 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2876 User *IVUser = IVUse->getUser();
2877 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2878 if (Op->getOpcode() == Instruction::URem ||
2879 Op->getOpcode() == Instruction::UDiv) {
2880 UsersToReplace.push_back(IVUser);
2881 }
2882 }
2883 }
2884 for (User *User : UsersToReplace) {
2885 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2886 }
2887 } else {
2888 // The canonical loop is generated with a fixed lower bound. We need to
2889 // update the index calculation code to use the task's lower bound. The
2890 // generated code looks like this:
2891 // %omp_loop.iv = phi ...
2892 // ...
2893 // %tmp = mul [type] %omp_loop.iv, step
2894 // %user_index = add [type] tmp, lb
2895 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2896 // of the normalised induction variable:
2897 // 1. This one: converting the normalised IV to the user IV
2898 // 2. The increment (add)
2899 // 3. The comparison against the trip count (icmp)
2900 // (1) is the only use that is a mul followed by an add so this cannot
2901 // match other IR.
2902 assert(CLI->getIndVar()->getNumUses() == 3 &&
2903 "Canonical loop should have exactly three uses of the ind var");
2904 for (User *IVUser : CLI->getIndVar()->users()) {
2905 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2906 if (Mul->getOpcode() == Instruction::Mul) {
2907 for (User *MulUser : Mul->users()) {
2908 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2909 if (Add->getOpcode() == Instruction::Add) {
2910 Add->setOperand(1, CastedTaskLB);
2911 }
2912 }
2913 }
2914 }
2915 }
2916 }
2917 }
2918
2919 FakeLB->replaceAllUsesWith(CastedLBVal);
2920 FakeUB->replaceAllUsesWith(CastedUBVal);
2921 FakeStep->replaceAllUsesWith(CastedStepVal);
2922 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2923 I->eraseFromParent();
2924 }
2925 };
2926
2927 addOutlineInfo(std::move(OI));
2928 Builder.SetInsertPoint(TaskloopExitBB->begin());
2929 return Builder.saveIP();
2930}
2931
2934 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2936 llvm::Type::getInt32Ty(M.getContext()));
2937}
2938
2940 const LocationDescription &Loc, InsertPointTy AllocaIP,
2941 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2942 bool Tied, Value *Final, Value *IfCondition,
2943 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2944 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2945
2946 if (!updateToLocation(Loc))
2947 return InsertPointTy();
2948
2949 uint32_t SrcLocStrSize;
2950 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2951 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2952 // The current basic block is split into four basic blocks. After outlining,
2953 // they will be mapped as follows:
2954 // ```
2955 // def current_fn() {
2956 // current_basic_block:
2957 // br label %task.exit
2958 // task.exit:
2959 // ; instructions after task
2960 // }
2961 // def outlined_fn() {
2962 // task.alloca:
2963 // br label %task.body
2964 // task.body:
2965 // ret void
2966 // }
2967 // ```
2968 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2969 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2970 BasicBlock *TaskAllocaBB =
2971 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2972
2973 InsertPointTy TaskAllocaIP = TaskAllocaBB->begin();
2974 InsertPointTy TaskBodyIP = TaskBodyBB->begin();
2975 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2976 return Err;
2977
2978 auto OI = std::make_unique<OutlineInfo>();
2979 OI->EntryBB = TaskAllocaBB;
2980 OI->OuterAllocBB = AllocaIP.getNodeParent();
2981 OI->ExitBB = TaskExitBB;
2982 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2983 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2984
2985 // Add the thread ID argument.
2987 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2988 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2989
2990 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2991 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2992 TaskAllocaBB,
2993 ToBeDeleted](Function &OutlinedFn) mutable {
2994 // Replace the Stale CI by appropriate RTL function call.
2995 assert(OutlinedFn.hasOneUse() &&
2996 "there must be a single user for the outlined function");
2997 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2998
2999 // HasShareds is true if any variables are captured in the outlined region,
3000 // false otherwise.
3001 bool HasShareds = StaleCI->arg_size() > 1;
3002 Builder.SetInsertPoint(StaleCI);
3003
3004 // Gather the arguments for emitting the runtime call for
3005 // @__kmpc_omp_task_alloc
3006 Function *TaskAllocFn =
3007 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
3008
3009 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
3010 // call.
3011 Value *ThreadID = getOrCreateThreadID(Ident);
3012
3013 // Argument - `flags`
3014 // Task is tied iff (Flags & 1) == 1.
3015 // Task is untied iff (Flags & 1) == 0.
3016 // Task is final iff (Flags & 2) == 2.
3017 // Task is not final iff (Flags & 2) == 0.
3018 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
3019 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
3020 // Task is detachable iff (Flags & 64) == 64.
3021 // Task is not detachable iff (Flags & 64) == 0.
3022 // Task is priority iff (Flags & 32) == 32.
3023 // Task is not priority iff (Flags & 32) == 0.
3024 // Task is free-agent eligible iff (Flags & 128) == 128.
3025 // Task is not free-agent eligible iff (Flags & 128) == 0.
3026 // TODO: Handle the other flags.
3027 Value *Flags = Builder.getInt32(Tied);
3028 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
3029 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
3030 if (Final) {
3031 Value *FinalFlag =
3032 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3033 Flags = Builder.CreateOr(FinalFlag, Flags);
3034 }
3035
3036 if (Mergeable || UseMergedIf0Path)
3037 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3038 if (EventHandle)
3039 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3040 if (Priority)
3041 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3042 if (FreeAgent)
3043 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3044
3045 // Argument - `sizeof_kmp_task_t` (TaskSize)
3046 // Tasksize refers to the size in bytes of kmp_task_t data structure
3047 // including private vars accessed in task.
3048 // TODO: add kmp_task_t_with_privates (privates)
3049 Value *TaskSize = Builder.getInt64(
3050 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3051
3052 // Argument - `sizeof_shareds` (SharedsSize)
3053 // SharedsSize refers to the shareds array size in the kmp_task_t data
3054 // structure.
3055 Value *SharedsSize = Builder.getInt64(0);
3056 if (HasShareds) {
3057 AllocaInst *ArgStructAlloca =
3059 assert(ArgStructAlloca &&
3060 "Unable to find the alloca instruction corresponding to arguments "
3061 "for extracted function");
3062 std::optional<TypeSize> ArgAllocSize =
3063 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3064 assert(ArgAllocSize &&
3065 "Unable to determine size of arguments for extracted function");
3066 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3067 }
3068 // Emit the @__kmpc_omp_task_alloc runtime call
3069 // The runtime call returns a pointer to an area where the task captured
3070 // variables must be copied before the task is run (TaskData)
3072 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3073 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3074 /*task_func=*/&OutlinedFn});
3075
3076 if (Affinities.Count && Affinities.Info) {
3078 OMPRTL___kmpc_omp_reg_task_with_affinity);
3079
3080 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3081 Affinities.Count, Affinities.Info});
3082 }
3083
3084 // Emit detach clause initialization.
3085 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3086 // task_descriptor);
3087 if (EventHandle) {
3089 OMPRTL___kmpc_task_allow_completion_event);
3090 llvm::Value *EventVal =
3091 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3092 llvm::Value *EventHandleAddr =
3093 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3094 Builder.getPtrTy(0));
3095 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3096 Builder.CreateStore(EventVal, EventHandleAddr);
3097 }
3098 // Copy the arguments for outlined function
3099 if (HasShareds) {
3100 Value *Shareds = StaleCI->getArgOperand(1);
3101 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3102 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3103 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3104 SharedsSize);
3105 }
3106
3107 if (Priority) {
3108 //
3109 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3110 // we populate the priority information into the "kmp_task_t" here
3111 //
3112 // The struct "kmp_task_t" definition is available in kmp.h
3113 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3114 // data2 is used for priority
3115 //
3116 Type *Int32Ty = Builder.getInt32Ty();
3117 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3118 // kmp_task_t* => { ptr }
3119 Type *TaskPtr = StructType::get(VoidPtr);
3120 Value *TaskGEP =
3121 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3122 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3123 Type *TaskStructType = StructType::get(
3124 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3125 Value *PriorityData = Builder.CreateInBoundsGEP(
3126 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3127 // kmp_cmplrdata_t => { ptr, ptr }
3128 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3129 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3130 PriorityData, {Zero, Zero});
3131 Builder.CreateStore(Priority, CmplrData);
3132 }
3133
3134 Value *DepArray = nullptr;
3135 Value *NumDeps = nullptr;
3136 if (Dependencies.DepArray) {
3137 DepArray = Dependencies.DepArray;
3138 NumDeps = Dependencies.NumDeps;
3139 } else if (!Dependencies.Deps.empty()) {
3140 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3141 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3142 }
3143
3144 // In the presence of the `if` clause, the following IR is generated:
3145 // ...
3146 // %data = call @__kmpc_omp_task_alloc(...)
3147 // br i1 %if_condition, label %then, label %else
3148 // then:
3149 // call @__kmpc_omp_task(...)
3150 // br label %exit
3151 // else:
3152 // ;; Wait for resolution of dependencies, if any, before
3153 // ;; beginning the task
3154 // call @__kmpc_omp_wait_deps(...)
3155 // call @__kmpc_omp_task_begin_if0(...)
3156 // call @outlined_fn(...)
3157 // call @__kmpc_omp_task_complete_if0(...)
3158 // br label %exit
3159 // exit:
3160 // ...
3161 if (IfCondition && !UseMergedIf0Path) {
3162 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3163 // terminator.
3164 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3165 Instruction *IfTerminator =
3166 Builder.GetInsertPoint()->getParent()->getTerminator();
3167 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3168 Builder.SetInsertPoint(IfTerminator);
3169 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3170 &ElseTI);
3171 Builder.SetInsertPoint(ElseTI);
3172
3173 if (DepArray) {
3174 Function *TaskWaitFn =
3175 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3177 TaskWaitFn,
3178 {Ident, ThreadID, NumDeps, DepArray,
3179 ConstantInt::get(Builder.getInt32Ty(), 0),
3181 }
3182 Function *TaskBeginFn =
3183 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3184 Function *TaskCompleteFn =
3185 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3186 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3187 CallInst *CI = nullptr;
3188 if (HasShareds)
3189 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3190 else
3191 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3192 CI->setDebugLoc(StaleCI->getDebugLoc());
3193 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3194 Builder.SetInsertPoint(ThenTI);
3195 }
3196
3197 if (DepArray) {
3198 Function *TaskFn =
3199 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3201 TaskFn,
3202 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3203 ConstantInt::get(Builder.getInt32Ty(), 0),
3205
3206 } else {
3207 // Emit the @__kmpc_omp_task runtime call to spawn the task
3208 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3209 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3210 }
3211
3212 StaleCI->eraseFromParent();
3213
3214 Builder.SetInsertPoint(TaskAllocaBB->begin());
3215 if (HasShareds) {
3216 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3217 OutlinedFn.getArg(1)->replaceUsesWithIf(
3218 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3219 }
3220
3221 // The insert point may refer to one of the instructions about to be
3222 // deleted. It is not needed anymore so clear it instead of leaving it
3223 // dangling.
3224 Builder.ClearInsertionPoint();
3225 for (Instruction *I : llvm::reverse(ToBeDeleted))
3226 I->eraseFromParent();
3227 };
3228
3229 addOutlineInfo(std::move(OI));
3230 Builder.SetInsertPoint(TaskExitBB->begin());
3231
3232 return Builder.saveIP();
3233}
3234
3236 const LocationDescription &Loc, InsertPointTy AllocaIP,
3237 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3238 if (!updateToLocation(Loc))
3239 return InsertPointTy();
3240
3241 uint32_t SrcLocStrSize;
3242 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3243 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3244 Value *ThreadID = getOrCreateThreadID(Ident);
3245
3246 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3247 Function *TaskgroupFn =
3248 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3249 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3250
3251 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3252 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3253 return Err;
3254
3255 Builder.SetInsertPoint(TaskgroupExitBB);
3256 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3257 Function *EndTaskgroupFn =
3258 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3259 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3260
3261 return Builder.saveIP();
3262}
3263
3265 const LocationDescription &Loc, InsertPointTy AllocaIP,
3267 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3268 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3269
3270 if (!updateToLocation(Loc))
3271 return Loc.IP;
3272
3273 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3274
3275 // Each section is emitted as a switch case
3276 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3277 // -> OMP.createSection() which generates the IR for each section
3278 // Iterate through all sections and emit a switch construct:
3279 // switch (IV) {
3280 // case 0:
3281 // <SectionStmt[0]>;
3282 // break;
3283 // ...
3284 // case <NumSection> - 1:
3285 // <SectionStmt[<NumSection> - 1]>;
3286 // break;
3287 // }
3288 // ...
3289 // section_loop.after:
3290 // <FiniCB>;
3291 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3292 Builder.restoreIP(CodeGenIP);
3294 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3295 Function *CurFn = Continue->getParent();
3296 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3297
3298 unsigned CaseNumber = 0;
3299 for (auto SectionCB : SectionCBs) {
3301 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3302 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3303 Builder.SetInsertPoint(CaseBB);
3304 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3305 if (Error Err = SectionCB(InsertPointTy(), CaseEndBr->getIterator(), {}))
3306 return Err;
3307 CaseNumber++;
3308 }
3309 // remove the existing terminator from body BB since there can be no
3310 // terminators after switch/case
3311 return Error::success();
3312 };
3313 // Loop body ends here
3314 // LowerBound, UpperBound, and STride for createCanonicalLoop
3315 Type *I32Ty = Type::getInt32Ty(M.getContext());
3316 Value *LB = ConstantInt::get(I32Ty, 0);
3317 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3318 Value *ST = ConstantInt::get(I32Ty, 1);
3320 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3321 if (!LoopInfo)
3322 return LoopInfo.takeError();
3323
3324 InsertPointOrErrorTy WsloopIP =
3325 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3326 WorksharingLoopType::ForStaticLoop, !IsNowait);
3327 if (!WsloopIP)
3328 return WsloopIP.takeError();
3329 InsertPointTy AfterIP = *WsloopIP;
3330
3331 BasicBlock *LoopFini = AfterIP.getNodeParent()->getSinglePredecessor();
3332 assert(LoopFini && "Bad structure of static workshare loop finalization");
3333
3334 // Apply the finalization callback in LoopAfterBB
3335 auto FiniInfo = FinalizationStack.pop_back_val();
3336 assert(FiniInfo.DK == OMPD_sections &&
3337 "Unexpected finalization stack state!");
3338 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3339 return Err;
3340
3341 return AfterIP;
3342}
3343
3346 BodyGenCallbackTy BodyGenCB,
3347 FinalizeCallbackTy FiniCB) {
3348 if (!updateToLocation(Loc))
3349 return Loc.IP;
3350
3351 auto FiniCBWrapper = [&](InsertPointTy IP) {
3352 if (IP != IP.getNodeParent()->end())
3353 return FiniCB(IP);
3354 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3355 // will fail because that function requires the Finalization Basic Block to
3356 // have a terminator, which is already removed by EmitOMPRegionBody.
3357 // IP is currently at cancelation block.
3358 // We need to backtrack to the condition block to fetch
3359 // the exit block and create a branch from cancelation
3360 // to exit block.
3362 Builder.restoreIP(IP);
3363 auto *CaseBB = Loc.IP.getNodeParent();
3364 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3365 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3366 Instruction *I = Builder.CreateBr(ExitBB);
3367 IP = I->getIterator();
3368 return FiniCB(IP);
3369 };
3370
3371 Directive OMPD = Directive::OMPD_sections;
3372 // Since we are using Finalization Callback here, HasFinalize
3373 // and IsCancellable have to be true
3374 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3375 /*Conditional*/ false, /*hasFinalize*/ true,
3376 /*IsCancellable*/ true);
3377}
3378
3384
3385Value *OpenMPIRBuilder::getGPUThreadID() {
3388 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3389 {});
3390}
3391
3392Value *OpenMPIRBuilder::getGPUWarpSize() {
3394 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3395}
3396
3397Value *OpenMPIRBuilder::getNVPTXWarpID() {
3398 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3399 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3400}
3401
3402Value *OpenMPIRBuilder::getNVPTXLaneID() {
3403 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3404 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3405 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3406 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3407 "nvptx_lane_id");
3408}
3409
3410Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3411 Type *ToType) {
3412 Type *FromType = From->getType();
3413 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3414 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3415 assert(FromSize > 0 && "From size must be greater than zero");
3416 assert(ToSize > 0 && "To size must be greater than zero");
3417 if (FromType == ToType)
3418 return From;
3419 if (FromSize == ToSize)
3420 return Builder.CreateBitCast(From, ToType);
3421 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3422 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3423 InsertPointTy SaveIP = Builder.saveIP();
3424 Builder.restoreIP(AllocaIP);
3425 Value *CastItem = Builder.CreateAlloca(ToType);
3426 Builder.restoreIP(SaveIP);
3427
3428 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3429 CastItem, Builder.getPtrTy(0));
3430 Builder.CreateStore(From, ValCastItem);
3431 return Builder.CreateLoad(ToType, CastItem);
3432}
3433
3434Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3435 Value *Element,
3436 Type *ElementType,
3437 Value *Offset) {
3438 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3439 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3440
3441 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3442 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3443 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3444 Value *WarpSize =
3445 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3447 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3448 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3449 Value *WarpSizeCast =
3450 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3451 Value *ShuffleCall =
3452 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3453 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3454 // down to the requested element type, otherwise storing the result would
3455 // write past the end of an element narrower than the shuffle width.
3456 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3457}
3458
3459void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3460 Value *DstAddr, Type *ElemType,
3461 Value *Offset, Type *ReductionArrayTy,
3462 bool IsByRefElem) {
3463 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3464 // Create the loop over the big sized data.
3465 // ptr = (void*)Elem;
3466 // ptrEnd = (void*) Elem + 1;
3467 // Step = 8;
3468 // while (ptr + Step < ptrEnd)
3469 // shuffle((int64_t)*ptr);
3470 // Step = 4;
3471 // while (ptr + Step < ptrEnd)
3472 // shuffle((int32_t)*ptr);
3473 // ...
3474 Type *IndexTy = Builder.getIndexTy(
3475 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3476 Value *ElemPtr = DstAddr;
3477 Value *Ptr = SrcAddr;
3478 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3479 if (Size < IntSize)
3480 continue;
3481 Type *IntType = Builder.getIntNTy(IntSize * 8);
3482 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3483 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3484 Value *SrcAddrGEP =
3485 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3486 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3487 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3488
3489 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3490 if ((Size / IntSize) > 1) {
3491 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3492 SrcAddrGEP, Builder.getPtrTy());
3493 BasicBlock *PreCondBB =
3494 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3495 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3496 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3497 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3498 emitBlock(PreCondBB, CurFunc);
3499 PHINode *PhiSrc =
3500 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3501 PhiSrc->addIncoming(Ptr, CurrentBB);
3502 PHINode *PhiDest =
3503 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3504 PhiDest->addIncoming(ElemPtr, CurrentBB);
3505 Ptr = PhiSrc;
3506 ElemPtr = PhiDest;
3507 Value *PtrDiff = Builder.CreatePtrDiff(
3508 Builder.getInt8Ty(), PtrEnd,
3509 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3510 Builder.CreateCondBr(
3511 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3512 ExitBB);
3513 emitBlock(ThenBB, CurFunc);
3514 Value *Res = createRuntimeShuffleFunction(
3515 AllocaIP,
3516 Builder.CreateAlignedLoad(
3517 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3518 IntType, Offset);
3519 Builder.CreateAlignedStore(Res, ElemPtr,
3520 M.getDataLayout().getPrefTypeAlign(ElemType));
3521 Value *LocalPtr =
3522 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3523 Value *LocalElemPtr =
3524 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3525 PhiSrc->addIncoming(LocalPtr, ThenBB);
3526 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3527 emitBranch(PreCondBB);
3528 emitBlock(ExitBB, CurFunc);
3529 } else {
3530 // The shuffled value comes back as the chunk's integer type, so the
3531 // store covers exactly this chunk regardless of what ElemType is.
3532 Value *Res = createRuntimeShuffleFunction(
3533 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3534 Builder.CreateStore(Res, ElemPtr);
3535 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3536 ElemPtr =
3537 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3538 }
3539 Size = Size % IntSize;
3540 }
3541}
3542
3543Error OpenMPIRBuilder::emitReductionListCopy(
3544 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3545 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3546 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3547 Type *IndexTy = Builder.getIndexTy(
3548 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3549 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3550
3551 // Iterates, element-by-element, through the source Reduce list and
3552 // make a copy.
3553 for (auto En : enumerate(ReductionInfos)) {
3554 const ReductionInfo &RI = En.value();
3555 Value *SrcElementAddr = nullptr;
3556 AllocaInst *DestAlloca = nullptr;
3557 Value *DestElementAddr = nullptr;
3558 Value *DestElementPtrAddr = nullptr;
3559 // Should we shuffle in an element from a remote lane?
3560 bool ShuffleInElement = false;
3561 // Set to true to update the pointer in the dest Reduce list to a
3562 // newly created element.
3563 bool UpdateDestListPtr = false;
3564
3565 // Step 1.1: Get the address for the src element in the Reduce list.
3566 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3567 ReductionArrayTy, SrcBase,
3568 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3569 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3570
3571 // Step 1.2: Create a temporary to store the element in the destination
3572 // Reduce list.
3573 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3574 ReductionArrayTy, DestBase,
3575 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3576 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3577 switch (Action) {
3579 InsertPointTy CurIP = Builder.saveIP();
3580 Builder.restoreIP(AllocaIP);
3581
3582 Type *DestAllocaType =
3583 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3584 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3585 ".omp.reduction.element");
3586 DestAlloca->setAlignment(
3587 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3588 DestElementAddr = DestAlloca;
3589 DestElementAddr =
3590 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3591 DestElementAddr->getName() + ".ascast");
3592 Builder.restoreIP(CurIP);
3593 ShuffleInElement = true;
3594 UpdateDestListPtr = true;
3595 break;
3596 }
3598 DestElementAddr =
3599 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3600 break;
3601 }
3602 }
3603
3604 // Now that all active lanes have read the element in the
3605 // Reduce list, shuffle over the value from the remote lane.
3606 if (ShuffleInElement) {
3607 Type *ShuffleType = RI.ElementType;
3608 Value *ShuffleSrcAddr = SrcElementAddr;
3609 Value *ShuffleDestAddr = DestElementAddr;
3610 AllocaInst *LocalStorage = nullptr;
3611
3612 if (IsByRefElem) {
3613 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3614 assert(RI.ByRefAllocatedType &&
3615 "Expected by-ref allocated type to be set");
3616 // For by-ref reductions, we need to copy from the remote lane the
3617 // actual value of the partial reduction computed by that remote lane;
3618 // rather than, for example, a pointer to that data or, even worse, a
3619 // pointer to the descriptor of the by-ref reduction element.
3620 ShuffleType = RI.ByRefElementType;
3621
3622 if (RI.DataPtrPtrGen) {
3623 // Descriptor-based by-ref: extract data pointer from descriptor.
3624 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3625 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3626
3627 if (!GenResult)
3628 return GenResult.takeError();
3629
3630 ShuffleSrcAddr =
3631 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3632
3633 {
3634 InsertPointTy OldIP = Builder.saveIP();
3635 Builder.restoreIP(AllocaIP);
3636
3637 LocalStorage = Builder.CreateAlloca(ShuffleType);
3638 Builder.restoreIP(OldIP);
3639 ShuffleDestAddr = LocalStorage;
3640 }
3641 } else {
3642 // Non-descriptor by-ref: the pointer already references data
3643 // directly. Shuffle into the destination alloca.
3644 ShuffleDestAddr = DestElementAddr;
3645 }
3646 }
3647
3648 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3649 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3650
3651 if (IsByRefElem && RI.DataPtrPtrGen) {
3652 // Copy descriptor from source and update base_ptr to shuffled data
3653 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3654 DestAlloca, Builder.getPtrTy(), ".ascast");
3655
3656 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3657 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3658 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3659
3660 if (!GenResult)
3661 return GenResult.takeError();
3662 }
3663 } else {
3664 switch (RI.EvaluationKind) {
3665 case EvalKind::Scalar: {
3666 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3667 // Store the source element value to the dest element address.
3668 Builder.CreateStore(Elem, DestElementAddr);
3669 break;
3670 }
3671 case EvalKind::Complex: {
3672 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3673 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3674 Value *SrcReal = Builder.CreateLoad(
3675 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3676 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3677 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3678 Value *SrcImg = Builder.CreateLoad(
3679 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3680
3681 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3682 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3683 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3684 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3685 Builder.CreateStore(SrcReal, DestRealPtr);
3686 Builder.CreateStore(SrcImg, DestImgPtr);
3687 break;
3688 }
3689 case EvalKind::Aggregate: {
3690 Value *SizeVal = Builder.getInt64(
3691 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3692 Builder.CreateMemCpy(
3693 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3694 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3695 SizeVal, false);
3696 break;
3697 }
3698 };
3699 }
3700
3701 // Step 3.1: Modify reference in dest Reduce list as needed.
3702 // Modifying the reference in Reduce list to point to the newly
3703 // created element. The element is live in the current function
3704 // scope and that of functions it invokes (i.e., reduce_function).
3705 // RemoteReduceData[i] = (void*)&RemoteElem
3706 if (UpdateDestListPtr) {
3707 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3708 DestElementAddr, Builder.getPtrTy(),
3709 DestElementAddr->getName() + ".ascast");
3710 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3711 }
3712 }
3713
3714 return Error::success();
3715}
3716
3717Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3718 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3719 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3720 IRBuilder<>::InsertPointGuard IPG(Builder);
3721 LLVMContext &Ctx = M.getContext();
3722 FunctionType *FuncTy = FunctionType::get(
3723 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3724 /* IsVarArg */ false);
3725 Function *WcFunc =
3727 "_omp_reduction_inter_warp_copy_func", &M);
3728 WcFunc->setCallingConv(Config.getRuntimeCC());
3729 WcFunc->setAttributes(FuncAttrs);
3730 WcFunc->addParamAttr(0, Attribute::NoUndef);
3731 WcFunc->addParamAttr(1, Attribute::NoUndef);
3732 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3733 Builder.SetInsertPoint(EntryBB);
3734 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3735
3736 // ReduceList: thread local Reduce list.
3737 // At the stage of the computation when this function is called, partially
3738 // aggregated values reside in the first lane of every active warp.
3739 Argument *ReduceListArg = WcFunc->getArg(0);
3740 // NumWarps: number of warps active in the parallel region. This could
3741 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3742 Argument *NumWarpsArg = WcFunc->getArg(1);
3743
3744 // This array is used as a medium to transfer, one reduce element at a time,
3745 // the data from the first lane of every warp to lanes in the first warp
3746 // in order to perform the final step of a reduction in a parallel region
3747 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3748 // for reduced latency, as well as to have a distinct copy for concurrently
3749 // executing target regions. The array is declared with common linkage so
3750 // as to be shared across compilation units.
3751 StringRef TransferMediumName =
3752 "__openmp_nvptx_data_transfer_temporary_storage";
3753 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3754 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3755 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3756 if (!TransferMedium) {
3757 TransferMedium = new GlobalVariable(
3758 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3759 UndefValue::get(ArrayTy), TransferMediumName,
3760 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3761 /*AddressSpace=*/3);
3762 }
3763
3764 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3765 Value *GPUThreadID = getGPUThreadID();
3766 // nvptx_lane_id = nvptx_id % warpsize
3767 Value *LaneID = getNVPTXLaneID();
3768 // nvptx_warp_id = nvptx_id / warpsize
3769 Value *WarpID = getNVPTXWarpID();
3770
3771 InsertPointTy AllocaIP = Builder.GetInsertBlock()->getFirstInsertionPt();
3772 Type *Arg0Type = ReduceListArg->getType();
3773 Type *Arg1Type = NumWarpsArg->getType();
3774 Builder.restoreIP(AllocaIP);
3775 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3776 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3777 AllocaInst *NumWarpsAlloca =
3778 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3779 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3780 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3781 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3782 NumWarpsAlloca, Builder.getPtrTy(0),
3783 NumWarpsAlloca->getName() + ".ascast");
3784 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3785 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3786 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3787 InsertPointTy CodeGenIP =
3788 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3789 Builder.restoreIP(CodeGenIP);
3790
3791 Value *ReduceList =
3792 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3793
3794 for (auto En : enumerate(ReductionInfos)) {
3795 //
3796 // Warp master copies reduce element to transfer medium in __shared__
3797 // memory.
3798 //
3799 const ReductionInfo &RI = En.value();
3800 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3801 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3802 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3803 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3804 Type *CType = Builder.getIntNTy(TySize * 8);
3805
3806 unsigned NumIters = RealTySize / TySize;
3807 if (NumIters == 0)
3808 continue;
3809 Value *Cnt = nullptr;
3810 Value *CntAddr = nullptr;
3811 BasicBlock *PrecondBB = nullptr;
3812 BasicBlock *ExitBB = nullptr;
3813 if (NumIters > 1) {
3814 CodeGenIP = Builder.saveIP();
3815 Builder.restoreIP(AllocaIP);
3816 CntAddr =
3817 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3818
3819 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3820 CntAddr->getName() + ".ascast");
3821 Builder.restoreIP(CodeGenIP);
3822 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3823 CntAddr,
3824 /*Volatile=*/false);
3825 PrecondBB = BasicBlock::Create(Ctx, "precond");
3826 ExitBB = BasicBlock::Create(Ctx, "exit");
3827 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3828 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3829 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3830 /*Volatile=*/false);
3831 Value *Cmp = Builder.CreateICmpULT(
3832 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3833 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3834 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3835 }
3836
3837 // kmpc_barrier.
3838 InsertPointOrErrorTy BarrierIP1 =
3840 omp::Directive::OMPD_unknown,
3841 /* ForceSimpleCall */ false,
3842 /* CheckCancelFlag */ true);
3843 if (!BarrierIP1)
3844 return BarrierIP1.takeError();
3845 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3846 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3847 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3848
3849 // if (lane_id == 0)
3850 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3851 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3852 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3853
3854 // Reduce element = LocalReduceList[i]
3855 auto *RedListArrayTy =
3856 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3857 Type *IndexTy = Builder.getIndexTy(
3858 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3859 Value *ElemPtrPtr =
3860 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3861 {ConstantInt::get(IndexTy, 0),
3862 ConstantInt::get(IndexTy, En.index())});
3863 // elemptr = ((CopyType*)(elemptrptr)) + I
3864 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3865
3866 if (IsByRefElem && RI.DataPtrPtrGen) {
3867 InsertPointOrErrorTy GenRes =
3868 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3869
3870 if (!GenRes)
3871 return GenRes.takeError();
3872
3873 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3874 }
3875
3876 if (NumIters > 1)
3877 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3878
3879 // Get pointer to location in transfer medium.
3880 // MediumPtr = &medium[warp_id]
3881 Value *MediumPtr = Builder.CreateInBoundsGEP(
3882 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3883 // elem = *elemptr
3884 //*MediumPtr = elem
3885 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3886 // Store the source element value to the dest element address.
3887 Builder.CreateStore(Elem, MediumPtr,
3888 /*IsVolatile*/ true);
3889 Builder.CreateBr(MergeBB);
3890
3891 // else
3892 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3893 Builder.CreateBr(MergeBB);
3894
3895 // endif
3896 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3897 InsertPointOrErrorTy BarrierIP2 =
3899 omp::Directive::OMPD_unknown,
3900 /* ForceSimpleCall */ false,
3901 /* CheckCancelFlag */ true);
3902 if (!BarrierIP2)
3903 return BarrierIP2.takeError();
3904
3905 // Warp 0 copies reduce element from transfer medium
3906 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3907 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3908 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3909
3910 Value *NumWarpsVal =
3911 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3912 // Up to 32 threads in warp 0 are active.
3913 Value *IsActiveThread =
3914 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3915 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3916
3917 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3918
3919 // SecMediumPtr = &medium[tid]
3920 // SrcMediumVal = *SrcMediumPtr
3921 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3922 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3923 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3924 Value *TargetElemPtrPtr =
3925 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3926 {ConstantInt::get(IndexTy, 0),
3927 ConstantInt::get(IndexTy, En.index())});
3928 Value *TargetElemPtrVal =
3929 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3930 Value *TargetElemPtr = TargetElemPtrVal;
3931
3932 if (IsByRefElem && RI.DataPtrPtrGen) {
3933 InsertPointOrErrorTy GenRes =
3934 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3935
3936 if (!GenRes)
3937 return GenRes.takeError();
3938
3939 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3940 }
3941
3942 if (NumIters > 1)
3943 TargetElemPtr =
3944 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3945
3946 // *TargetElemPtr = SrcMediumVal;
3947 Value *SrcMediumValue =
3948 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3949 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3950 Builder.CreateBr(W0MergeBB);
3951
3952 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3953 Builder.CreateBr(W0MergeBB);
3954
3955 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3956
3957 if (NumIters > 1) {
3958 Cnt = Builder.CreateNSWAdd(
3959 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3960 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3961
3962 auto *CurFn = Builder.GetInsertBlock()->getParent();
3963 emitBranch(PrecondBB);
3964 emitBlock(ExitBB, CurFn);
3965 }
3966 RealTySize %= TySize;
3967 }
3968 }
3969
3970 Builder.CreateRetVoid();
3971
3972 return WcFunc;
3973}
3974
3975Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3976 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3977 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3978 LLVMContext &Ctx = M.getContext();
3979 IRBuilder<>::InsertPointGuard IPG(Builder);
3980 FunctionType *FuncTy =
3981 FunctionType::get(Builder.getVoidTy(),
3982 {Builder.getPtrTy(), Builder.getInt16Ty(),
3983 Builder.getInt16Ty(), Builder.getInt16Ty()},
3984 /* IsVarArg */ false);
3985 Function *SarFunc =
3987 "_omp_reduction_shuffle_and_reduce_func", &M);
3988 SarFunc->setCallingConv(Config.getRuntimeCC());
3989 SarFunc->setAttributes(FuncAttrs);
3990 SarFunc->addParamAttr(0, Attribute::NoUndef);
3991 SarFunc->addParamAttr(1, Attribute::NoUndef);
3992 SarFunc->addParamAttr(2, Attribute::NoUndef);
3993 SarFunc->addParamAttr(3, Attribute::NoUndef);
3994 SarFunc->addParamAttr(1, Attribute::SExt);
3995 SarFunc->addParamAttr(2, Attribute::SExt);
3996 SarFunc->addParamAttr(3, Attribute::SExt);
3997 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3998 Builder.SetInsertPoint(EntryBB);
3999 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4000
4001 // Thread local Reduce list used to host the values of data to be reduced.
4002 Argument *ReduceListArg = SarFunc->getArg(0);
4003 // Current lane id; could be logical.
4004 Argument *LaneIDArg = SarFunc->getArg(1);
4005 // Offset of the remote source lane relative to the current lane.
4006 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
4007 // Algorithm version. This is expected to be known at compile time.
4008 Argument *AlgoVerArg = SarFunc->getArg(3);
4009
4010 Type *ReduceListArgType = ReduceListArg->getType();
4011 Type *LaneIDArgType = LaneIDArg->getType();
4012 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
4013 Value *ReduceListAlloca = Builder.CreateAlloca(
4014 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
4015 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4016 LaneIDArg->getName() + ".addr");
4017 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
4018 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
4019 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4020 AlgoVerArg->getName() + ".addr");
4021 ArrayType *RedListArrayTy =
4022 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4023
4024 // Create a local thread-private variable to host the Reduce list
4025 // from a remote lane.
4026 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
4027 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
4028
4029 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4030 ReduceListAlloca, ReduceListArgType,
4031 ReduceListAlloca->getName() + ".ascast");
4032 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4033 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4034 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4035 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4036 RemoteLaneOffsetAlloca->getName() + ".ascast");
4037 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4038 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4039 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4040 RemoteReductionListAlloca, Builder.getPtrTy(),
4041 RemoteReductionListAlloca->getName() + ".ascast");
4042
4043 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4044 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4045 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4046 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4047
4048 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4049 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4050 Value *RemoteLaneOffset =
4051 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4052 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4053
4054 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4055
4056 // This loop iterates through the list of reduce elements and copies,
4057 // element by element, from a remote lane in the warp to RemoteReduceList,
4058 // hosted on the thread's stack.
4059 Error EmitRedLsCpRes = emitReductionListCopy(
4060 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4061 ReduceList, RemoteListAddrCast, IsByRef,
4062 {RemoteLaneOffset, nullptr, nullptr});
4063
4064 if (EmitRedLsCpRes)
4065 return EmitRedLsCpRes;
4066
4067 // The actions to be performed on the Remote Reduce list is dependent
4068 // on the algorithm version.
4069 //
4070 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4071 // LaneId % 2 == 0 && Offset > 0):
4072 // do the reduction value aggregation
4073 //
4074 // The thread local variable Reduce list is mutated in place to host the
4075 // reduced data, which is the aggregated value produced from local and
4076 // remote lanes.
4077 //
4078 // Note that AlgoVer is expected to be a constant integer known at compile
4079 // time.
4080 // When AlgoVer==0, the first conjunction evaluates to true, making
4081 // the entire predicate true during compile time.
4082 // When AlgoVer==1, the second conjunction has only the second part to be
4083 // evaluated during runtime. Other conjunctions evaluates to false
4084 // during compile time.
4085 // When AlgoVer==2, the third conjunction has only the second part to be
4086 // evaluated during runtime. Other conjunctions evaluates to false
4087 // during compile time.
4088 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4089 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4090 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4091 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4092 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4093 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4094 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4095 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4096 Value *RemoteOffsetComp =
4097 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4098 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4099 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4100 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4101
4102 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4103 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4104 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4105
4106 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4107 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4108 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4109 ReduceList, Builder.getPtrTy());
4110 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4111 RemoteListAddrCast, Builder.getPtrTy());
4112 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4113 ->addFnAttr(Attribute::NoUnwind);
4114 Builder.CreateBr(MergeBB);
4115
4116 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4117 Builder.CreateBr(MergeBB);
4118
4119 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4120
4121 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4122 // Reduce list.
4123 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4124 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4125 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4126
4127 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4128 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4129 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4130 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4131
4132 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4133
4134 EmitRedLsCpRes = emitReductionListCopy(
4135 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4136 RemoteListAddrCast, ReduceList, IsByRef);
4137
4138 if (EmitRedLsCpRes)
4139 return EmitRedLsCpRes;
4140
4141 Builder.CreateBr(CpyMergeBB);
4142
4143 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4144 Builder.CreateBr(CpyMergeBB);
4145
4146 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4147
4148 Builder.CreateRetVoid();
4149
4150 return SarFunc;
4151}
4152
4154OpenMPIRBuilder::generateReductionDescriptor(
4155 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4156 Type *DescriptorType,
4157 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4158 DataPtrPtrGen) {
4159
4160 // Copy the source descriptor to preserve all metadata (rank, extents,
4161 // strides, etc.)
4162 Value *DescriptorSize =
4163 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4164 Builder.CreateMemCpy(
4165 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4166 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4167 DescriptorSize);
4168
4169 // Update the base pointer field to point to the local shuffled data
4170 Value *DataPtrField;
4171 InsertPointOrErrorTy GenResult =
4172 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4173
4174 if (!GenResult)
4175 return GenResult.takeError();
4176
4177 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4178 DataPtr, Builder.getPtrTy(), ".ascast"),
4179 DataPtrField);
4180
4181 return Builder.saveIP();
4182}
4183
4184Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4185 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4186 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4187 InsertPointTy OldIP = Builder.saveIP();
4188 Builder.restoreIP(AllocaIP);
4189
4190 AllocaInst *DescriptorAlloca =
4191 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4192 DescriptorAlloca->setAlignment(
4193 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4194 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4195 DescriptorAlloca, DescriptorPtrTy,
4196 DescriptorAlloca->getName() + ".ascast");
4197
4198 Builder.restoreIP(OldIP);
4199
4200 InsertPointOrErrorTy GenResult =
4201 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4202 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4203 if (!GenResult)
4204 return GenResult.takeError();
4205
4206 return DescriptorAddr;
4207}
4208
4209Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4210 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4211 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4212 IRBuilder<>::InsertPointGuard IPG(Builder);
4213 LLVMContext &Ctx = M.getContext();
4214 FunctionType *FuncTy = FunctionType::get(
4215 Builder.getVoidTy(),
4216 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4217 /* IsVarArg */ false);
4218 Function *LtGCFunc =
4220 "_omp_reduction_list_to_global_copy_func", &M);
4221 LtGCFunc->setAttributes(FuncAttrs);
4222 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4223 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4224 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4225
4226 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4227 Builder.SetInsertPoint(EntryBlock);
4228 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4229
4230 // Buffer: global reduction buffer.
4231 Argument *BufferArg = LtGCFunc->getArg(0);
4232 // Idx: index of the buffer.
4233 Argument *IdxArg = LtGCFunc->getArg(1);
4234 // ReduceList: thread local Reduce list.
4235 Argument *ReduceListArg = LtGCFunc->getArg(2);
4236
4237 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4238 BufferArg->getName() + ".addr");
4239 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4240 IdxArg->getName() + ".addr");
4241 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4242 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4243 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4244 BufferArgAlloca, Builder.getPtrTy(),
4245 BufferArgAlloca->getName() + ".ascast");
4246 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4247 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4248 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4249 ReduceListArgAlloca, Builder.getPtrTy(),
4250 ReduceListArgAlloca->getName() + ".ascast");
4251
4252 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4253 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4254 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4255
4256 Value *LocalReduceList =
4257 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4258 Value *BufferArgVal =
4259 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4260 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4261 Type *IndexTy = Builder.getIndexTy(
4262 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4263 for (auto En : enumerate(ReductionInfos)) {
4264 const ReductionInfo &RI = En.value();
4265 auto *RedListArrayTy =
4266 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4267 // Reduce element = LocalReduceList[i]
4268 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4269 RedListArrayTy, LocalReduceList,
4270 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4271 // elemptr = ((CopyType*)(elemptrptr)) + I
4272 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4273
4274 // Global = Buffer.VD[Idx];
4275 Value *BufferVD =
4276 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4277 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4278 ReductionsBufferTy, BufferVD, 0, En.index());
4279
4280 switch (RI.EvaluationKind) {
4281 case EvalKind::Scalar: {
4282 Value *TargetElement;
4283
4284 if (IsByRef.empty() || !IsByRef[En.index()]) {
4285 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4286 } else {
4287 if (RI.DataPtrPtrGen) {
4288 InsertPointOrErrorTy GenResult =
4289 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4290
4291 if (!GenResult)
4292 return GenResult.takeError();
4293
4294 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4295 }
4296 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4297 }
4298
4299 Builder.CreateStore(TargetElement, GlobVal);
4300 break;
4301 }
4302 case EvalKind::Complex: {
4303 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4304 RI.ElementType, ElemPtr, 0, 0, ".realp");
4305 Value *SrcReal = Builder.CreateLoad(
4306 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4307 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4308 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4309 Value *SrcImg = Builder.CreateLoad(
4310 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4311
4312 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4313 RI.ElementType, GlobVal, 0, 0, ".realp");
4314 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4315 RI.ElementType, GlobVal, 0, 1, ".imagp");
4316 Builder.CreateStore(SrcReal, DestRealPtr);
4317 Builder.CreateStore(SrcImg, DestImgPtr);
4318 break;
4319 }
4320 case EvalKind::Aggregate: {
4321 Value *SizeVal =
4322 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4323 Builder.CreateMemCpy(
4324 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4325 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4326 break;
4327 }
4328 }
4329 }
4330
4331 Builder.CreateRetVoid();
4332 return LtGCFunc;
4333}
4334
4335Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4336 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4337 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4338 IRBuilder<>::InsertPointGuard IPG(Builder);
4339 LLVMContext &Ctx = M.getContext();
4340 FunctionType *FuncTy = FunctionType::get(
4341 Builder.getVoidTy(),
4342 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4343 /* IsVarArg */ false);
4344 Function *LtGRFunc =
4346 "_omp_reduction_list_to_global_reduce_func", &M);
4347 LtGRFunc->setAttributes(FuncAttrs);
4348 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4349 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4350 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4351
4352 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4353 Builder.SetInsertPoint(EntryBlock);
4354 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4355
4356 // Buffer: global reduction buffer.
4357 Argument *BufferArg = LtGRFunc->getArg(0);
4358 // Idx: index of the buffer.
4359 Argument *IdxArg = LtGRFunc->getArg(1);
4360 // ReduceList: thread local Reduce list.
4361 Argument *ReduceListArg = LtGRFunc->getArg(2);
4362
4363 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4364 BufferArg->getName() + ".addr");
4365 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4366 IdxArg->getName() + ".addr");
4367 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4368 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4369 auto *RedListArrayTy =
4370 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4371
4372 // 1. Build a list of reduction variables.
4373 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4374 Value *LocalReduceList =
4375 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4376
4377 InsertPointTy AllocaIP(EntryBlock->begin());
4378
4379 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4380 BufferArgAlloca, Builder.getPtrTy(),
4381 BufferArgAlloca->getName() + ".ascast");
4382 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4383 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4384 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4385 ReduceListArgAlloca, Builder.getPtrTy(),
4386 ReduceListArgAlloca->getName() + ".ascast");
4387 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4388 LocalReduceList, Builder.getPtrTy(),
4389 LocalReduceList->getName() + ".ascast");
4390
4391 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4392 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4393 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4394
4395 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4396 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4397 Type *IndexTy = Builder.getIndexTy(
4398 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4399 for (auto En : enumerate(ReductionInfos)) {
4400 const ReductionInfo &RI = En.value();
4401
4402 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4403 RedListArrayTy, LocalReduceListAddrCast,
4404 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4405 Value *BufferVD =
4406 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4407 // Global = Buffer.VD[Idx];
4408 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4409 ReductionsBufferTy, BufferVD, 0, En.index());
4410
4411 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4412 // Get source descriptor from the reduce list argument
4413 Value *ReduceList =
4414 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4415 Value *SrcElementPtrPtr =
4416 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4417 {ConstantInt::get(IndexTy, 0),
4418 ConstantInt::get(IndexTy, En.index())});
4419 Value *SrcDescriptorAddr =
4420 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4421
4422 // Copy descriptor from source and update base_ptr to global buffer data
4423 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4424 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4425 if (!ByRefAlloc)
4426 return ByRefAlloc.takeError();
4427
4428 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4429 } else {
4430 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4431 }
4432 }
4433
4434 // Call reduce_function(GlobalReduceList, ReduceList)
4435 Value *ReduceList =
4436 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4437 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4438 ->addFnAttr(Attribute::NoUnwind);
4439 Builder.CreateRetVoid();
4440 return LtGRFunc;
4441}
4442
4443Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4444 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4445 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4446 IRBuilder<>::InsertPointGuard IPG(Builder);
4447 LLVMContext &Ctx = M.getContext();
4448 FunctionType *FuncTy = FunctionType::get(
4449 Builder.getVoidTy(),
4450 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4451 /* IsVarArg */ false);
4452 Function *GtLCFunc =
4454 "_omp_reduction_global_to_list_copy_func", &M);
4455 GtLCFunc->setAttributes(FuncAttrs);
4456 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4457 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4458 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4459
4460 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4461 Builder.SetInsertPoint(EntryBlock);
4462 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4463
4464 // Buffer: global reduction buffer.
4465 Argument *BufferArg = GtLCFunc->getArg(0);
4466 // Idx: index of the buffer.
4467 Argument *IdxArg = GtLCFunc->getArg(1);
4468 // ReduceList: thread local Reduce list.
4469 Argument *ReduceListArg = GtLCFunc->getArg(2);
4470
4471 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4472 BufferArg->getName() + ".addr");
4473 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4474 IdxArg->getName() + ".addr");
4475 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4476 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4477 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4478 BufferArgAlloca, Builder.getPtrTy(),
4479 BufferArgAlloca->getName() + ".ascast");
4480 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4481 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4482 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4483 ReduceListArgAlloca, Builder.getPtrTy(),
4484 ReduceListArgAlloca->getName() + ".ascast");
4485 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4486 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4487 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4488
4489 Value *LocalReduceList =
4490 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4491 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4492 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4493 Type *IndexTy = Builder.getIndexTy(
4494 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4495 for (auto En : enumerate(ReductionInfos)) {
4496 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4497 auto *RedListArrayTy =
4498 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4499 // Reduce element = LocalReduceList[i]
4500 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4501 RedListArrayTy, LocalReduceList,
4502 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4503 // elemptr = ((CopyType*)(elemptrptr)) + I
4504 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4505 // Global = Buffer.VD[Idx];
4506 Value *BufferVD =
4507 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4508 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4509 ReductionsBufferTy, BufferVD, 0, En.index());
4510
4511 switch (RI.EvaluationKind) {
4512 case EvalKind::Scalar: {
4513 Type *ElemType = RI.ElementType;
4514
4515 if (!IsByRef.empty() && IsByRef[En.index()]) {
4516 ElemType = RI.ByRefElementType;
4517 if (RI.DataPtrPtrGen) {
4518 InsertPointOrErrorTy GenResult =
4519 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4520
4521 if (!GenResult)
4522 return GenResult.takeError();
4523
4524 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4525 }
4526 }
4527
4528 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4529 Builder.CreateStore(TargetElement, ElemPtr);
4530 break;
4531 }
4532 case EvalKind::Complex: {
4533 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4534 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4535 Value *SrcReal = Builder.CreateLoad(
4536 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4537 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4538 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4539 Value *SrcImg = Builder.CreateLoad(
4540 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4541
4542 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4543 RI.ElementType, ElemPtr, 0, 0, ".realp");
4544 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4545 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4546 Builder.CreateStore(SrcReal, DestRealPtr);
4547 Builder.CreateStore(SrcImg, DestImgPtr);
4548 break;
4549 }
4550 case EvalKind::Aggregate: {
4551 Value *SizeVal =
4552 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4553 Builder.CreateMemCpy(
4554 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4555 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4556 SizeVal, false);
4557 break;
4558 }
4559 }
4560 }
4561
4562 Builder.CreateRetVoid();
4563 return GtLCFunc;
4564}
4565
4566Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4567 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4568 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4569 IRBuilder<>::InsertPointGuard IPG(Builder);
4570 LLVMContext &Ctx = M.getContext();
4571 auto *FuncTy = FunctionType::get(
4572 Builder.getVoidTy(),
4573 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4574 /* IsVarArg */ false);
4575 Function *GtLRFunc =
4577 "_omp_reduction_global_to_list_reduce_func", &M);
4578 GtLRFunc->setAttributes(FuncAttrs);
4579 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4580 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4581 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4582
4583 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4584 Builder.SetInsertPoint(EntryBlock);
4585 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4586
4587 // Buffer: global reduction buffer.
4588 Argument *BufferArg = GtLRFunc->getArg(0);
4589 // Idx: index of the buffer.
4590 Argument *IdxArg = GtLRFunc->getArg(1);
4591 // ReduceList: thread local Reduce list.
4592 Argument *ReduceListArg = GtLRFunc->getArg(2);
4593
4594 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4595 BufferArg->getName() + ".addr");
4596 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4597 IdxArg->getName() + ".addr");
4598 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4599 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4600 ArrayType *RedListArrayTy =
4601 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4602
4603 // 1. Build a list of reduction variables.
4604 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4605 Value *LocalReduceList =
4606 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4607
4608 InsertPointTy AllocaIP(EntryBlock->begin());
4609
4610 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4611 BufferArgAlloca, Builder.getPtrTy(),
4612 BufferArgAlloca->getName() + ".ascast");
4613 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4614 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4615 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4616 ReduceListArgAlloca, Builder.getPtrTy(),
4617 ReduceListArgAlloca->getName() + ".ascast");
4618 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4619 LocalReduceList, Builder.getPtrTy(),
4620 LocalReduceList->getName() + ".ascast");
4621
4622 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4623 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4624 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4625
4626 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4627 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4628 Type *IndexTy = Builder.getIndexTy(
4629 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4630 for (auto En : enumerate(ReductionInfos)) {
4631 const ReductionInfo &RI = En.value();
4632
4633 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4634 RedListArrayTy, ReductionList,
4635 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4636 // Global = Buffer.VD[Idx];
4637 Value *BufferVD =
4638 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4639 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4640 ReductionsBufferTy, BufferVD, 0, En.index());
4641
4642 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4643 // Get source descriptor from the reduce list
4644 Value *ReduceListVal =
4645 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4646 Value *SrcElementPtrPtr =
4647 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4648 {ConstantInt::get(IndexTy, 0),
4649 ConstantInt::get(IndexTy, En.index())});
4650 Value *SrcDescriptorAddr =
4651 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4652
4653 // Copy descriptor from source and update base_ptr to global buffer data
4654 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4655 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4656 if (!ByRefAlloc)
4657 return ByRefAlloc.takeError();
4658
4659 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4660 } else {
4661 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4662 }
4663 }
4664
4665 // Call reduce_function(ReduceList, GlobalReduceList)
4666 Value *ReduceList =
4667 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4668 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4669 ->addFnAttr(Attribute::NoUnwind);
4670 Builder.CreateRetVoid();
4671 return GtLRFunc;
4672}
4673
4674std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4675 std::string Suffix =
4676 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4677 return (Name + Suffix).str();
4678}
4679
4680Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4681 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4683 AttributeList FuncAttrs) {
4684 IRBuilder<>::InsertPointGuard IPG(Builder);
4685 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4686 {Builder.getPtrTy(), Builder.getPtrTy()},
4687 /* IsVarArg */ false);
4688 std::string Name = getReductionFuncName(ReducerName);
4689 Function *ReductionFunc =
4691 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4692 ReductionFunc->setAttributes(FuncAttrs);
4693 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4694 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4695 BasicBlock *EntryBB =
4696 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4697 Builder.SetInsertPoint(EntryBB);
4698 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4699
4700 // Need to alloca memory here and deal with the pointers before getting
4701 // LHS/RHS pointers out
4702 Value *LHSArrayPtr = nullptr;
4703 Value *RHSArrayPtr = nullptr;
4704 Argument *Arg0 = ReductionFunc->getArg(0);
4705 Argument *Arg1 = ReductionFunc->getArg(1);
4706 Type *Arg0Type = Arg0->getType();
4707 Type *Arg1Type = Arg1->getType();
4708
4709 Value *LHSAlloca =
4710 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4711 Value *RHSAlloca =
4712 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4713 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4714 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4715 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4716 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4717 Builder.CreateStore(Arg0, LHSAddrCast);
4718 Builder.CreateStore(Arg1, RHSAddrCast);
4719 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4720 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4721
4722 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4723 Type *IndexTy = Builder.getIndexTy(
4724 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4725 SmallVector<Value *> LHSPtrs, RHSPtrs;
4726 for (auto En : enumerate(ReductionInfos)) {
4727 const ReductionInfo &RI = En.value();
4728 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4729 RedArrayTy, RHSArrayPtr,
4730 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4731 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4732 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4733 RHSI8Ptr, RI.PrivateVariable->getType(),
4734 RHSI8Ptr->getName() + ".ascast");
4735
4736 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4737 RedArrayTy, LHSArrayPtr,
4738 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4739 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4740 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4741 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4742
4744 LHSPtrs.emplace_back(LHSPtr);
4745 RHSPtrs.emplace_back(RHSPtr);
4746 } else {
4747 Value *LHS = LHSPtr;
4748 Value *RHS = RHSPtr;
4749
4750 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4751 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4752 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4753 }
4754
4755 Value *Reduced;
4756 InsertPointOrErrorTy AfterIP =
4757 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4758 if (!AfterIP)
4759 return AfterIP.takeError();
4760 if (!Builder.GetInsertBlock())
4761 return ReductionFunc;
4762
4763 Builder.restoreIP(*AfterIP);
4764
4765 if (!IsByRef.empty() && !IsByRef[En.index()])
4766 Builder.CreateStore(Reduced, LHSPtr);
4767 }
4768 }
4769
4771 for (auto En : enumerate(ReductionInfos)) {
4772 unsigned Index = En.index();
4773 const ReductionInfo &RI = En.value();
4774 Value *LHSFixupPtr, *RHSFixupPtr;
4775 Builder.restoreIP(RI.ReductionGenClang(
4776 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4777
4778 // Fix the CallBack code genereated to use the correct Values for the LHS
4779 // and RHS
4780 LHSFixupPtr->replaceUsesWithIf(
4781 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4782 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4783 ReductionFunc;
4784 });
4785 RHSFixupPtr->replaceUsesWithIf(
4786 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4787 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4788 ReductionFunc;
4789 });
4790 }
4791
4792 Builder.CreateRetVoid();
4793 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4794 // to the entry block (this is dones for higher opt levels by later passes in
4795 // the pipeline). This has caused issues because non-entry `alloca`s force the
4796 // function to use dynamic stack allocations and we might run out of scratch
4797 // memory.
4798 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4799
4800 return ReductionFunc;
4801}
4802
4803static void
4805 bool IsGPU) {
4806 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4807 (void)RI;
4808 assert(RI.Variable && "expected non-null variable");
4809 assert(RI.PrivateVariable && "expected non-null private variable");
4810 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4811 "expected non-null reduction generator callback");
4812 if (!IsGPU) {
4813 assert(
4814 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4815 "expected variables and their private equivalents to have the same "
4816 "type");
4817 }
4818 assert(RI.Variable->getType()->isPointerTy() &&
4819 "expected variables to be pointers");
4820 }
4821}
4822
4823// The atomic cross-team reduction fast path applies when every reduction in the
4824// set can be represented by an atomicrmw. Clang only populates it for scalar
4825// reductions with a supported atomic operator.
4828 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4829 return static_cast<bool>(RI.AtomicReductionGen);
4830 });
4831}
4832
4834 const LocationDescription &Loc, InsertPointTy AllocaIP,
4835 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4836 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4837 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4838 Value *SrcLocInfo) {
4839 if (!updateToLocation(Loc))
4840 return InsertPointTy();
4841 Builder.restoreIP(CodeGenIP);
4842 Builder.SetCurrentDebugLocation(Loc.DL);
4843 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4844 LLVMContext &Ctx = M.getContext();
4845
4846 // Source location for the ident struct
4847 if (!SrcLocInfo) {
4848 uint32_t SrcLocStrSize;
4849 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4850 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4851 }
4852
4853 if (ReductionInfos.size() == 0)
4854 return Builder.saveIP();
4855
4856 BasicBlock *ContinuationBlock = nullptr;
4858 // Copied code from createReductions
4859 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
4860 ContinuationBlock = InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
4861 InsertBlock->getTerminator()->eraseFromParent();
4862 Builder.SetInsertPoint(InsertBlock->end());
4863 }
4864
4865 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4866 AttributeList FuncAttrs;
4867 AttrBuilder AttrBldr(Ctx);
4868 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4869 AttrBldr.addAttribute(Attr);
4870 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4871 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4872
4873 Expected<Function *> ReductionResult = createReductionFunction(
4874 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4875 ReductionGenCBKind, FuncAttrs);
4876 if (!ReductionResult)
4877 return ReductionResult.takeError();
4878 Function *ReductionFunc = *ReductionResult;
4879
4880 // Set the grid value in the config needed for lowering later on
4881 if (GridValue.has_value())
4882 Config.setGridValue(GridValue.value());
4883 else
4884 Config.setGridValue(getGridValue(T, ReductionFunc));
4885
4886 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4887 // RedList, shuffle_reduce_func, interwarp_copy_func);
4888 // or
4889 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4890 Value *Res;
4891
4892 // 1. Build a list of reduction variables.
4893 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4894 auto Size = ReductionInfos.size();
4895 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4896 Type *FuncPtrTy =
4897 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4898 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4899 Value *ReductionList;
4900 {
4902 Builder.restoreIP(AllocaIP);
4903 Value *ReductionListAlloca =
4904 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4905 ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4906 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4907 }
4908 Type *IndexTy = Builder.getIndexTy(
4909 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4910 for (auto En : enumerate(ReductionInfos)) {
4911 const ReductionInfo &RI = En.value();
4912 Value *ElemPtr = Builder.CreateInBoundsGEP(
4913 RedArrayTy, ReductionList,
4914 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4915
4916 Value *PrivateVar = RI.PrivateVariable;
4917 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4918 if (IsByRefElem)
4919 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4920
4921 Value *CastElem =
4922 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4923 Builder.CreateStore(CastElem, ElemPtr);
4924 }
4925 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4926 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4927
4928 if (!SarFunc)
4929 return SarFunc.takeError();
4930
4931 Expected<Function *> CopyResult =
4932 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4933 if (!CopyResult)
4934 return CopyResult.takeError();
4935 Function *WcFunc = *CopyResult;
4936
4937 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4938
4939 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4940 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4941 // not currently use it. It is computed here conservatively as max(element
4942 // sizes) * N rather than the exact sum, which over-calculates the size for
4943 // mixed reduction types but is harmless given the argument is unused.
4944 // TODO: Consider dropping this computation if the runtime API is ever revised
4945 // to remove the unused parameter.
4946 unsigned MaxDataSize = 0;
4947 SmallVector<Type *> ReductionTypeArgs;
4948 for (auto En : enumerate(ReductionInfos)) {
4949 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4950 // the actual data size stored in the global reduction buffer, consistent
4951 // with the ReductionsBufferTy struct used for GEP offsets below.
4952 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4953 ? En.value().ByRefElementType
4954 : En.value().ElementType;
4955 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4956 if (Size > MaxDataSize)
4957 MaxDataSize = Size;
4958 ReductionTypeArgs.emplace_back(RedTypeArg);
4959 }
4960 Value *ReductionDataSize =
4961 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4962
4963 // Helper function to copy thread-local data back to the original reduction
4964 // list.
4965 Function *CopyScratchToListFunc = nullptr;
4966 // Thread-local storage for the reduction variables.
4967 Value *ScratchForCopyBack = nullptr;
4968 // RL pointer to which the final value from the per-thread scratch should be
4969 // copied back. (Basically RL, appropriately casted if necessary.)
4970 Value *RLForCopyBack = RL;
4971
4972 bool IsAtomicReduction =
4973 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4974
4975 if (!IsTeamsReduction) {
4976 Value *SarFuncCast =
4977 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4978 Value *WcFuncCast =
4979 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4980 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4981 WcFuncCast};
4983 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4984 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4985 } else if (IsAtomicReduction) {
4986 // Atomic cross-team reduction fast path: determine the team's main thread
4987 // that is later to fold its value atomically into the mapped variable.
4988 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4989 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4990 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4991 } else {
4992 StructType *ReductionsBufferTy = StructType::create(
4993 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4994
4995 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4996 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4997 if (!LtGCFunc)
4998 return LtGCFunc.takeError();
4999
5000 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
5001 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
5002 if (!GtLCFunc)
5003 return GtLCFunc.takeError();
5004
5005 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
5006 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
5007 if (!GtLRFunc)
5008 return GtLRFunc.takeError();
5009
5010 // The runtime's cross-team final aggregate uses the storage pointed at by
5011 // its reduce-list argument as per-thread scratch. When the surrounding
5012 // kernel is already in SPMD execution mode, clang emitted each reduction
5013 // private as a per-thread `alloca addrspace(5)`, so the original red_list
5014 // (RL) is already per-thread and nothing else is needed.
5015 //
5016 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
5017 // Generic-mode globalization put the reduction private into team-shared
5018 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
5019 // point all threads of the last team would race on the shared LDS slot.
5020 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
5021 // value in, and hand the per-thread RL to the runtime instead. The writer
5022 // thread copies the final value from that per-thread scratch back to RL
5023 // before running the existing combine path below.
5024
5025 // Thread-local RL (might need localization below before being passed to the
5026 // runtime).
5027 Value *RuntimeRL = RL;
5028
5029 if (!IsSPMD) {
5030 Value *PerThreadScratch;
5031
5032 {
5034 Builder.restoreIP(AllocaIP);
5035 // Allocate thread-local buffer for the reduction variables.
5036 Value *PerThreadScratchAlloca =
5037 Builder.CreateAlloca(ReductionsBufferTy, /*ArraySize=*/nullptr,
5038 ".omp.reduction.scratch");
5039 PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5040 PerThreadScratchAlloca, PtrTy,
5041 PerThreadScratchAlloca->getName() + ".ascast");
5042 // Allocate thread-local buffer for the pointers to the reduction
5043 // variables.
5044 Value *PerThreadRedListAlloca =
5045 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5046 ".omp.reduction.per_thread_red_list");
5047 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5048 PerThreadRedListAlloca, PtrTy,
5049 PerThreadRedListAlloca->getName() + ".ascast");
5050 }
5051
5052 // Iterate over the reduction variables and copy the team-local value to
5053 // the thread-local buffer.
5054 for (auto En : enumerate(ReductionInfos)) {
5055 const ReductionInfo &RI = En.value();
5056 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5057
5058 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5059 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5060 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5061 0, En.index());
5062
5063 Value *RuntimeListEntry = FieldPtr;
5064 if (IsByRefElem && RI.DataPtrPtrGen) {
5065 Value *SrcDescriptor =
5066 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5067 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5068 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5069 if (!Descriptor)
5070 return Descriptor.takeError();
5071 RuntimeListEntry = *Descriptor;
5072 }
5073 Builder.CreateStore(RuntimeListEntry, Slot);
5074 }
5075 // The copy helpers were emitted with default-AS (AS 0) pointer params
5076 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5077 // but PerThreadScratch and RL live in the target's default AS, which
5078 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5079 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5080 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5081 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5082 PerThreadScratch, CopyArg0Ty);
5083 RLForCopyBack =
5084 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5085 // Use index 0 because there is no array of target values to index into,
5086 // there is only one thread-local memory slot.
5087 // restoreIP above left a stale/empty debug location; this inlinable call
5088 // to a debug-info-bearing helper needs one or the verifier rejects the
5089 // module ("!dbg attachment points at wrong subprogram") after inlining.
5090 Builder.SetCurrentDebugLocation(Loc.DL);
5091 Builder.CreateCall(
5092 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5093 CopyScratchToListFunc = *GtLCFunc;
5094 }
5095
5096 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5097 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5098
5099 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5100 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5101 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5102 }
5103
5104 // 5. Build if (res == 1)
5105 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5106 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5107 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5108 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5109
5110 // 6. Build then branch: where we have reduced values in the master
5111 // thread in each team.
5112 // __kmpc_end_reduce{_nowait}(<gtid>);
5113 // break;
5114 emitBlock(ThenBB, CurFunc);
5115
5116 // Copy the writer thread's per-thread scratch result back into the original
5117 // red-list storage before the existing combine path reads RI.PrivateVariable.
5118 // Set a debug location: this inlinable call to a debug-info-bearing helper
5119 // needs one or the verifier rejects the module after inlining.
5120 if (ScratchForCopyBack) {
5121 Builder.SetCurrentDebugLocation(Loc.DL);
5122 Builder.CreateCall(
5123 CopyScratchToListFunc,
5124 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5125 }
5126
5127 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5128 for (auto En : enumerate(ReductionInfos)) {
5129 const ReductionInfo &RI = En.value();
5130
5131 // Atomic cross-team fast path: each team's main thread folds its
5132 // team-reduced value directly into the mapped reduction variable with a
5133 // single atomicrmw.
5134 if (IsAtomicReduction) {
5136 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5137 if (!AfterIP)
5138 return AfterIP.takeError();
5139 Builder.restoreIP(*AfterIP);
5140 continue;
5141 }
5142
5144 Value *RedValue = RI.Variable;
5145
5146 Value *RHS =
5147 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5148
5150 Value *LHSPtr, *RHSPtr;
5151 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5152 &LHSPtr, &RHSPtr, CurFunc));
5153
5154 // Fix the CallBack code genereated to use the correct Values for the LHS
5155 // and RHS. Cast to match types before replacing (necessary to handle
5156 // different address spaces).
5157 if (LHSPtr->getType() != RedValue->getType())
5158 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5159 RedValue, LHSPtr->getType());
5160 if (RHSPtr->getType() != RHS->getType())
5161 RHS =
5162 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5163
5164 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5165 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5166 ReductionFunc;
5167 });
5168 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5169 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5170 ReductionFunc;
5171 });
5172 } else {
5173 if (IsByRef.empty() || !IsByRef[En.index()]) {
5174 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5175 "red.value." + Twine(En.index()));
5176 }
5177 Value *PrivateRedValue = Builder.CreateLoad(
5178 ValueType, RHS, "red.private.value" + Twine(En.index()));
5179 Value *Reduced;
5180 InsertPointOrErrorTy AfterIP =
5181 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5182 if (!AfterIP)
5183 return AfterIP.takeError();
5184 Builder.restoreIP(*AfterIP);
5185
5186 if (!IsByRef.empty() && !IsByRef[En.index()])
5187 Builder.CreateStore(Reduced, RI.Variable);
5188 }
5189 }
5190 emitBlock(ExitBB, CurFunc);
5191 if (ContinuationBlock) {
5192 Builder.CreateBr(ContinuationBlock);
5193 Builder.SetInsertPoint(ContinuationBlock);
5194 }
5195 Config.setEmitLLVMUsed();
5196
5197 return Builder.saveIP();
5198}
5199
5201 Type *VoidTy = Type::getVoidTy(M.getContext());
5202 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5203 auto *FuncTy =
5204 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5206 ".omp.reduction.func", &M);
5207}
5208
5210 Function *ReductionFunc,
5212 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5213 IRBuilder<>::InsertPointGuard IPG(Builder);
5214 Module *Module = ReductionFunc->getParent();
5215 BasicBlock *ReductionFuncBlock =
5216 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5217 Builder.SetInsertPoint(ReductionFuncBlock);
5218 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5219 Value *LHSArrayPtr = nullptr;
5220 Value *RHSArrayPtr = nullptr;
5221 if (IsGPU) {
5222 // Need to alloca memory here and deal with the pointers before getting
5223 // LHS/RHS pointers out
5224 //
5225 Argument *Arg0 = ReductionFunc->getArg(0);
5226 Argument *Arg1 = ReductionFunc->getArg(1);
5227 Type *Arg0Type = Arg0->getType();
5228 Type *Arg1Type = Arg1->getType();
5229
5230 Value *LHSAlloca =
5231 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5232 Value *RHSAlloca =
5233 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5234 Value *LHSAddrCast =
5235 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5236 Value *RHSAddrCast =
5237 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5238 Builder.CreateStore(Arg0, LHSAddrCast);
5239 Builder.CreateStore(Arg1, RHSAddrCast);
5240 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5241 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5242 } else {
5243 LHSArrayPtr = ReductionFunc->getArg(0);
5244 RHSArrayPtr = ReductionFunc->getArg(1);
5245 }
5246
5247 unsigned NumReductions = ReductionInfos.size();
5248 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5249
5250 for (auto En : enumerate(ReductionInfos)) {
5251 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5252 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5253 RedArrayTy, LHSArrayPtr, 0, En.index());
5254 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5255 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5256 LHSI8Ptr, RI.Variable->getType());
5257 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5258 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5259 RedArrayTy, RHSArrayPtr, 0, En.index());
5260 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5261 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5262 RHSI8Ptr, RI.PrivateVariable->getType());
5263 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5264 Value *Reduced;
5266 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5267 if (!AfterIP)
5268 return AfterIP.takeError();
5269
5270 Builder.restoreIP(*AfterIP);
5271 // TODO: Consider flagging an error.
5272 if (!Builder.GetInsertBlock())
5273 return Error::success();
5274
5275 // store is inside of the reduction region when using by-ref
5276 if (!IsByRef[En.index()])
5277 Builder.CreateStore(Reduced, LHSPtr);
5278 }
5279 Builder.CreateRetVoid();
5280 return Error::success();
5281}
5282
5284 const LocationDescription &Loc, InsertPointTy AllocaIP,
5285 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5286 bool IsNoWait, bool IsTeamsReduction) {
5287 assert(ReductionInfos.size() == IsByRef.size());
5288 if (Config.isGPU())
5289 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5290 IsByRef, IsNoWait, IsTeamsReduction);
5291
5292 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5293
5294 if (!updateToLocation(Loc))
5295 return InsertPointTy();
5296
5297 if (ReductionInfos.size() == 0)
5298 return Builder.saveIP();
5299
5300 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
5301 BasicBlock *ContinuationBlock =
5302 InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
5303 InsertBlock->getTerminator()->eraseFromParent();
5304
5305 // Create and populate array of type-erased pointers to private reduction
5306 // values.
5307 unsigned NumReductions = ReductionInfos.size();
5308 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5309 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getTerminator());
5310 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5311
5312 Builder.SetInsertPoint(InsertBlock->end());
5313 // Emitting the alloca moved the insertion point into the alloca block and
5314 // can clear the debug loc. Restore back to Loc.DL.
5315 Builder.SetCurrentDebugLocation(Loc.DL);
5316
5317 for (auto En : enumerate(ReductionInfos)) {
5318 unsigned Index = En.index();
5319 const ReductionInfo &RI = En.value();
5320 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5321 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5322 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5323 }
5324
5325 // Emit a call to the runtime function that orchestrates the reduction.
5326 // Declare the reduction function in the process.
5327 Type *IndexTy = Builder.getIndexTy(
5328 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5329 Function *Func = Builder.GetInsertBlock()->getParent();
5330 Module *Module = Func->getParent();
5331 uint32_t SrcLocStrSize;
5332 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5333 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5334 return RI.AtomicReductionGen;
5335 });
5336 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5337 CanGenerateAtomic
5338 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5339 : IdentFlag(0));
5340 Value *ThreadId = getOrCreateThreadID(Ident);
5341 Constant *NumVariables = Builder.getInt32(NumReductions);
5342 const DataLayout &DL = Module->getDataLayout();
5343 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5344 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5345 Function *ReductionFunc = getFreshReductionFunc(*Module);
5346 Value *Lock = getOMPCriticalRegionLock(".reduction");
5348 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5349 : RuntimeFunction::OMPRTL___kmpc_reduce);
5350 CallInst *ReduceCall =
5351 createRuntimeFunctionCall(ReduceFunc,
5352 {Ident, ThreadId, NumVariables, RedArraySize,
5353 RedArray, ReductionFunc, Lock},
5354 "reduce");
5355
5356 // Create final reduction entry blocks for the atomic and non-atomic case.
5357 // Emit IR that dispatches control flow to one of the blocks based on the
5358 // reduction supporting the atomic mode.
5359 BasicBlock *NonAtomicRedBlock =
5360 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5361 BasicBlock *AtomicRedBlock =
5362 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5363 SwitchInst *Switch =
5364 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5365 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5366 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5367
5368 // Populate the non-atomic reduction using the elementwise reduction function.
5369 // This loads the elements from the global and private variables and reduces
5370 // them before storing back the result to the global variable.
5371 Builder.SetInsertPoint(NonAtomicRedBlock);
5372 for (auto En : enumerate(ReductionInfos)) {
5373 const ReductionInfo &RI = En.value();
5375 // We have one less load for by-ref case because that load is now inside of
5376 // the reduction region
5377 Value *RedValue = RI.Variable;
5378 if (!IsByRef[En.index()]) {
5379 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5380 "red.value." + Twine(En.index()));
5381 }
5382 Value *PrivateRedValue =
5383 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5384 "red.private.value." + Twine(En.index()));
5385 Value *Reduced;
5386 InsertPointOrErrorTy AfterIP =
5387 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5388 if (!AfterIP)
5389 return AfterIP.takeError();
5390 Builder.restoreIP(*AfterIP);
5391
5392 if (!Builder.GetInsertBlock())
5393 return InsertPointTy();
5394 // for by-ref case, the load is inside of the reduction region
5395 if (!IsByRef[En.index()])
5396 Builder.CreateStore(Reduced, RI.Variable);
5397 }
5398 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5399 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5400 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5401 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5402 Builder.CreateBr(ContinuationBlock);
5403
5404 // Populate the atomic reduction using the atomic elementwise reduction
5405 // function. There are no loads/stores here because they will be happening
5406 // inside the atomic elementwise reduction.
5407 Builder.SetInsertPoint(AtomicRedBlock);
5408 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5409 for (const ReductionInfo &RI : ReductionInfos) {
5411 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5412 if (!AfterIP)
5413 return AfterIP.takeError();
5414 Builder.restoreIP(*AfterIP);
5415 if (!Builder.GetInsertBlock())
5416 return InsertPointTy();
5417 }
5418 Builder.CreateBr(ContinuationBlock);
5419 } else {
5420 Builder.CreateUnreachable();
5421 }
5422
5423 // Populate the outlined reduction function using the elementwise reduction
5424 // function. Partial values are extracted from the type-erased array of
5425 // pointers to private variables.
5426 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5427 IsByRef, /*isGPU=*/false);
5428 if (Err)
5429 return Err;
5430
5431 if (!Builder.GetInsertBlock())
5432 return InsertPointTy();
5433
5434 Builder.SetInsertPoint(ContinuationBlock);
5435 return Builder.saveIP();
5436}
5437
5440 BodyGenCallbackTy BodyGenCB,
5441 FinalizeCallbackTy FiniCB) {
5442 if (!updateToLocation(Loc))
5443 return Loc.IP;
5444
5445 Directive OMPD = Directive::OMPD_master;
5446 uint32_t SrcLocStrSize;
5447 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5448 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5449 Value *ThreadId = getOrCreateThreadID(Ident);
5450 Value *Args[] = {Ident, ThreadId};
5451
5452 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5453 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5454
5455 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5456 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5457
5458 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5459 /*Conditional*/ true, /*hasFinalize*/ true);
5460}
5461
5464 BodyGenCallbackTy BodyGenCB,
5465 FinalizeCallbackTy FiniCB, Value *Filter) {
5467 if (!updateToLocation(Loc))
5468 return Loc.IP;
5469
5470 Directive OMPD = Directive::OMPD_masked;
5471 uint32_t SrcLocStrSize;
5472 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5473 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5474 Value *ThreadId = getOrCreateThreadID(Ident);
5475 Value *Args[] = {Ident, ThreadId, Filter};
5476 Value *ArgsEnd[] = {Ident, ThreadId};
5477
5478 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5479 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5480
5481 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5482 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5483
5484 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5485 /*Conditional*/ true, /*hasFinalize*/ true);
5486}
5487
5489 llvm::FunctionCallee Callee,
5491 const llvm::Twine &Name) {
5492 llvm::CallInst *Call = Builder.CreateCall(
5493 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5494 Call->setDoesNotThrow();
5495 return Call;
5496}
5497
5498// Expects input basic block is dominated by BeforeScanBB.
5499// Once Scan directive is encountered, the code after scan directive should be
5500// dominated by AfterScanBB. Scan directive splits the code sequence to
5501// scan and input phase. Based on whether inclusive or exclusive
5502// clause is used in the scan directive and whether input loop or scan loop
5503// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5504// input loop and second is the scan loop. The code generated handles only
5505// inclusive scans now.
5507 const LocationDescription &Loc, InsertPointTy AllocaIP,
5508 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5509 bool IsInclusive, ScanInfo *ScanRedInfo) {
5510 if (ScanRedInfo->OMPFirstScanLoop) {
5511 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5512 ScanVarsType, ScanRedInfo);
5513 if (Err)
5514 return Err;
5515 }
5516 if (!updateToLocation(Loc))
5517 return Loc.IP;
5518
5519 llvm::Value *IV = ScanRedInfo->IV;
5520
5521 if (ScanRedInfo->OMPFirstScanLoop) {
5522 // Emit buffer[i] = red; at the end of the input phase.
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 *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5528 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5529
5530 Builder.CreateStore(Src, Val);
5531 }
5532 }
5533 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5534 emitBlock(ScanRedInfo->OMPScanDispatch,
5535 Builder.GetInsertBlock()->getParent());
5536
5537 if (!ScanRedInfo->OMPFirstScanLoop) {
5538 IV = ScanRedInfo->IV;
5539 // Emit red = buffer[i]; at the entrance to the scan phase.
5540 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5541 for (size_t i = 0; i < ScanVars.size(); i++) {
5542 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5543 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5544 Type *DestTy = ScanVarsType[i];
5545 Value *SrcPtr =
5546 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5547 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5548 Builder.CreateStore(Src, ScanVars[i]);
5549 }
5550 }
5551
5552 // TODO: Update it to CreateBr and remove dead blocks
5553 llvm::Value *CmpI = Builder.getInt1(true);
5554 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5555 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5556 ScanRedInfo->OMPAfterScanBlock);
5557 } else {
5558 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5559 ScanRedInfo->OMPBeforeScanBlock);
5560 }
5561 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5562 Builder.GetInsertBlock()->getParent());
5563 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5564 return Builder.saveIP();
5565}
5566
5567Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5568 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5569 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5570
5571 Builder.restoreIP(AllocaIP);
5572 // Create the shared pointer at alloca IP.
5573 for (size_t i = 0; i < ScanVars.size(); i++) {
5574 llvm::Value *BuffPtr =
5575 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5576 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5577 }
5578
5579 // Allocate temporary buffer by master thread
5580 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5581 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5582 Builder.restoreIP(CodeGenIP);
5583 Value *AllocSpan =
5584 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5585 for (size_t i = 0; i < ScanVars.size(); i++) {
5586 Type *IntPtrTy = Builder.getInt32Ty();
5587 Value *Allocsize = Builder.CreateTypeSize(
5588 IntPtrTy, M.getDataLayout().getTypeAllocSize(ScanVarsType[i]));
5589 Value *Buff =
5590 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5591 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5592 }
5593 return Error::success();
5594 };
5595 // TODO: Perform finalization actions for variables. This has to be
5596 // called for variables which have destructors/finalizers.
5597 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5598
5599 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5600 llvm::Value *FilterVal = Builder.getInt32(0);
5602 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5603
5604 if (!AfterIP)
5605 return AfterIP.takeError();
5606 Builder.restoreIP(*AfterIP);
5607 BasicBlock *InputBB = Builder.GetInsertBlock();
5608 if (InputBB->hasTerminator())
5609 Builder.SetInsertPoint(InputBB->getTerminator());
5610 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5611 if (!AfterIP)
5612 return AfterIP.takeError();
5613 Builder.restoreIP(*AfterIP);
5614
5615 return Error::success();
5616}
5617
5618Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5619 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5620 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5621 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5622 Builder.restoreIP(CodeGenIP);
5623 for (ReductionInfo RedInfo : ReductionInfos) {
5624 Value *PrivateVar = RedInfo.PrivateVariable;
5625 Value *OrigVar = RedInfo.Variable;
5626 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5627 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5628
5629 Type *SrcTy = RedInfo.ElementType;
5630 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5631 "arrayOffset");
5632 Value *Src = Builder.CreateLoad(SrcTy, Val);
5633
5634 Builder.CreateStore(Src, OrigVar);
5635 Builder.CreateFree(Buff);
5636 }
5637 return Error::success();
5638 };
5639 // TODO: Perform finalization actions for variables. This has to be
5640 // called for variables which have destructors/finalizers.
5641 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5642
5643 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5644 Builder.SetInsertPoint(TI);
5645 else
5646 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5647
5648 llvm::Value *FilterVal = Builder.getInt32(0);
5650 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5651
5652 if (!AfterIP)
5653 return AfterIP.takeError();
5654 Builder.restoreIP(*AfterIP);
5655 BasicBlock *InputBB = Builder.GetInsertBlock();
5656 if (InputBB->hasTerminator())
5657 Builder.SetInsertPoint(InputBB->getTerminator());
5658 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5659 if (!AfterIP)
5660 return AfterIP.takeError();
5661 Builder.restoreIP(*AfterIP);
5662 return Error::success();
5663}
5664
5666 const LocationDescription &Loc,
5668 ScanInfo *ScanRedInfo) {
5669
5670 if (!updateToLocation(Loc))
5671 return Loc.IP;
5672 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5673 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5674 Builder.restoreIP(CodeGenIP);
5675 Function *CurFn = Builder.GetInsertBlock()->getParent();
5676 // for (int k = 0; k <= ceil(log2(n)); ++k)
5677 llvm::BasicBlock *LoopBB =
5678 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5679 llvm::BasicBlock *ExitBB =
5680 splitBB(Builder, false, "omp.outer.log.scan.exit");
5682 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::log2,
5683 Builder.getDoubleTy());
5684 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5685 llvm::Value *Arg =
5686 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5687 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5689 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::ceil,
5690 Builder.getDoubleTy());
5691 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5692 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5693 llvm::Value *NMin1 = Builder.CreateNUWSub(
5694 ScanRedInfo->Span,
5695 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5696 Builder.SetInsertPoint(InputBB);
5697 Builder.CreateBr(LoopBB);
5698 emitBlock(LoopBB, CurFn);
5699 Builder.SetInsertPoint(LoopBB);
5700
5701 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5702 // size pow2k = 1;
5703 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5704 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5705 InputBB);
5706 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5707 InputBB);
5708 // for (size i = n - 1; i >= 2 ^ k; --i)
5709 // tmp[i] op= tmp[i-pow2k];
5710 llvm::BasicBlock *InnerLoopBB =
5711 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5712 llvm::BasicBlock *InnerExitBB =
5713 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5714 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5715 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5716 emitBlock(InnerLoopBB, CurFn);
5717 Builder.SetInsertPoint(InnerLoopBB);
5718 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5719 IVal->addIncoming(NMin1, LoopBB);
5720 for (ReductionInfo RedInfo : ReductionInfos) {
5721 Value *ReductionVal = RedInfo.PrivateVariable;
5722 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5723 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5724 Type *DestTy = RedInfo.ElementType;
5725 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5726 Value *LHSPtr =
5727 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5728 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5729 Value *RHSPtr =
5730 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5731 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5732 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5733 llvm::Value *Result;
5734 InsertPointOrErrorTy AfterIP =
5735 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5736 if (!AfterIP)
5737 return AfterIP.takeError();
5738 Builder.CreateStore(Result, LHSPtr);
5739 }
5740 llvm::Value *NextIVal = Builder.CreateNUWSub(
5741 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5742 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5743 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5744 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5745 emitBlock(InnerExitBB, CurFn);
5746 llvm::Value *Next = Builder.CreateNUWAdd(
5747 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5748 Counter->addIncoming(Next, Builder.GetInsertBlock());
5749 // pow2k <<= 1;
5750 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5751 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5752 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5753 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5754 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5755 return Error::success();
5756 };
5757
5758 // TODO: Perform finalization actions for variables. This has to be
5759 // called for variables which have destructors/finalizers.
5760 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5761
5762 llvm::Value *FilterVal = Builder.getInt32(0);
5764 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5765
5766 if (!AfterIP)
5767 return AfterIP.takeError();
5768 Builder.restoreIP(*AfterIP);
5769 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5770
5771 if (!AfterIP)
5772 return AfterIP.takeError();
5773 Builder.restoreIP(*AfterIP);
5774 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5775 if (Err)
5776 return Err;
5777
5778 return AfterIP;
5779}
5780
5781Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5782 llvm::function_ref<Error()> InputLoopGen,
5783 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5784 ScanInfo *ScanRedInfo) {
5785
5786 {
5787 // Emit loop with input phase:
5788 // for (i: 0..<num_iters>) {
5789 // <input phase>;
5790 // buffer[i] = red;
5791 // }
5792 ScanRedInfo->OMPFirstScanLoop = true;
5793 Error Err = InputLoopGen();
5794 if (Err)
5795 return Err;
5796 }
5797 {
5798 // Emit loop with scan phase:
5799 // for (i: 0..<num_iters>) {
5800 // red = buffer[i];
5801 // <scan phase>;
5802 // }
5803 ScanRedInfo->OMPFirstScanLoop = false;
5804 Error Err = ScanLoopGen(Builder);
5805 if (Err)
5806 return Err;
5807 }
5808 return Error::success();
5809}
5810
5811void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5812 Function *Fun = Builder.GetInsertBlock()->getParent();
5813 ScanRedInfo->OMPScanDispatch =
5814 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5815 ScanRedInfo->OMPAfterScanBlock =
5816 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5817 ScanRedInfo->OMPBeforeScanBlock =
5818 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5819 ScanRedInfo->OMPScanLoopExit =
5820 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5821}
5823 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5824 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5825 Module *M = F->getParent();
5826 LLVMContext &Ctx = M->getContext();
5827 Type *IndVarTy = TripCount->getType();
5828
5829 // Create the basic block structure.
5830 BasicBlock *Preheader =
5831 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5832 BasicBlock *Header =
5833 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5834 BasicBlock *Cond =
5835 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5836 BasicBlock *Body =
5837 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5838 BasicBlock *Latch =
5839 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5840 BasicBlock *Exit =
5841 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5842 BasicBlock *After =
5843 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5844
5845 // Use specified DebugLoc for new instructions.
5846 Builder.SetCurrentDebugLocation(DL);
5847
5848 Builder.SetInsertPoint(Preheader);
5849 Builder.CreateBr(Header);
5850
5851 Builder.SetInsertPoint(Header);
5852 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5853 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5854 Builder.CreateBr(Cond);
5855
5856 Builder.SetInsertPoint(Cond);
5857 Value *Cmp =
5858 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5859 Builder.CreateCondBr(Cmp, Body, Exit);
5860
5861 Builder.SetInsertPoint(Body);
5862 Builder.CreateBr(Latch);
5863
5864 Builder.SetInsertPoint(Latch);
5865 // Decide whether the induction variable increment can carry nsw.
5866 //
5867 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5868 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5869 // for valid programs 0 <= count <= INT_MAX always holds.
5870 //
5871 // Collapsed loops: the trip count is a product that can overflow i32 even for
5872 // a conforming program, so nsw is kept only when the product is a constant
5873 // that provably fits, dropped otherwise.
5874 bool HasNSW = Config.hasNoSignedWrap();
5875 if (HasNSW) {
5876 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5877 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5879 if (CI->getValue().ugt(SignedMax))
5880 HasNSW = false;
5881 } else if (IsCollapsed) {
5882 HasNSW = false;
5883 }
5884 }
5885 Value *Next =
5886 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5887 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5888 Builder.CreateBr(Header);
5889 IndVarPHI->addIncoming(Next, Latch);
5890
5891 Builder.SetInsertPoint(Exit);
5892 Builder.CreateBr(After);
5893
5894 // Remember and return the canonical control flow.
5895 LoopInfos.emplace_front();
5896 CanonicalLoopInfo *CL = &LoopInfos.front();
5897
5898 CL->Header = Header;
5899 CL->Cond = Cond;
5900 CL->Latch = Latch;
5901 CL->Exit = Exit;
5902
5903#ifndef NDEBUG
5904 CL->assertOK();
5905#endif
5906 return CL;
5907}
5908
5911 LoopBodyGenCallbackTy BodyGenCB,
5912 Value *TripCount, const Twine &Name) {
5913 BasicBlock *BB = Loc.IP.getNodeParent();
5914 BasicBlock *NextBB = BB->getNextNode();
5915
5916 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5917 NextBB, NextBB, Name);
5918 BasicBlock *After = CL->getAfter();
5919
5920 // If location is not set, don't connect the loop.
5921 if (updateToLocation(Loc)) {
5922 // Split the loop at the insertion point: Branch to the preheader and move
5923 // every following instruction to after the loop (the After BB). Also, the
5924 // new successor is the loop's after block.
5925 spliceBB(Builder, After, /*CreateBranch=*/false);
5926 Builder.CreateBr(CL->getPreheader());
5927 }
5928
5929 // Emit the body content. We do it after connecting the loop to the CFG to
5930 // avoid that the callback encounters degenerate BBs.
5931 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5932 return Err;
5933
5934#ifndef NDEBUG
5935 CL->assertOK();
5936#endif
5937 return CL;
5938}
5939
5941 ScanInfos.emplace_front();
5942 ScanInfo *Result = &ScanInfos.front();
5943 return Result;
5944}
5945
5949 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5950 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5951 LocationDescription ComputeLoc =
5952 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5953 updateToLocation(ComputeLoc);
5954
5956
5958 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5959 ScanRedInfo->Span = TripCount;
5960 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5961 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5962
5963 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5964 Builder.restoreIP(CodeGenIP);
5965 ScanRedInfo->IV = IV;
5966 createScanBBs(ScanRedInfo);
5967 BasicBlock *InputBlock = Builder.GetInsertBlock();
5968 Instruction *Terminator = InputBlock->getTerminator();
5969 assert(Terminator->getNumSuccessors() == 1);
5970 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5971 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5972 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5973 Builder.GetInsertBlock()->getParent());
5974 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5975 emitBlock(ScanRedInfo->OMPScanLoopExit,
5976 Builder.GetInsertBlock()->getParent());
5977 Builder.CreateBr(ContinueBlock);
5978 Builder.SetInsertPoint(
5979 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5980 return BodyGenCB(Builder.saveIP(), IV);
5981 };
5982
5983 const auto &&InputLoopGen = [&]() -> Error {
5985 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5986 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5987 if (!LoopInfo)
5988 return LoopInfo.takeError();
5989 Result.push_back(*LoopInfo);
5990 Builder.restoreIP((*LoopInfo)->getAfterIP());
5991 return Error::success();
5992 };
5993 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5995 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5996 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5997 if (!LoopInfo)
5998 return LoopInfo.takeError();
5999 Result.push_back(*LoopInfo);
6000 Builder.restoreIP((*LoopInfo)->getAfterIP());
6001 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
6002 return Error::success();
6003 };
6004 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
6005 if (Err)
6006 return Err;
6007 return Result;
6008}
6009
6011 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
6012 bool IsSigned, bool InclusiveStop, const Twine &Name) {
6013
6014 // Consider the following difficulties (assuming 8-bit signed integers):
6015 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
6016 // DO I = 1, 100, 50
6017 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
6018 // DO I = 100, 0, -128
6019
6020 // Start, Stop and Step must be of the same integer type.
6021 auto *IndVarTy = cast<IntegerType>(Start->getType());
6022 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
6023 assert(IndVarTy == Step->getType() && "Step type mismatch");
6024
6026
6027 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
6028 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
6029
6030 // Like Step, but always positive.
6031 Value *Incr = Step;
6032
6033 // Distance between Start and Stop; always positive.
6034 Value *Span;
6035
6036 // Condition whether there are no iterations are executed at all, e.g. because
6037 // UB < LB.
6038 Value *ZeroCmp;
6039
6040 if (IsSigned) {
6041 // Ensure that increment is positive. If not, negate and invert LB and UB.
6042 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6043 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6044 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6045 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6046 Span = Builder.CreateSub(UB, LB, "", false, true);
6047 ZeroCmp = Builder.CreateICmp(
6048 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6049 } else {
6050 Span = Builder.CreateSub(Stop, Start, "", true);
6051 ZeroCmp = Builder.CreateICmp(
6052 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6053 }
6054
6055 Value *CountIfLooping;
6056 if (InclusiveStop) {
6057 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6058 } else {
6059 // Avoid incrementing past stop since it could overflow.
6060 Value *CountIfTwo = Builder.CreateAdd(
6061 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6062 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6063 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6064 }
6065
6066 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6067 "omp_" + Name + ".tripcount");
6068}
6069
6072 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6073 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6074 ScanInfo *ScanRedInfo) {
6075 LocationDescription ComputeLoc =
6076 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6077
6079 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6080
6081 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6082 Builder.restoreIP(CodeGenIP);
6083 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6084 /*HasNSW=*/Config.hasNoSignedWrap());
6085 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6086 /*HasNSW=*/Config.hasNoSignedWrap());
6087 if (InScan)
6088 ScanRedInfo->IV = IndVar;
6089 return BodyGenCB(Builder.saveIP(), IndVar);
6090 };
6091 LocationDescription LoopLoc =
6092 ComputeIP.isValid()
6093 ? Loc
6094 : LocationDescription(Builder.saveIP(),
6095 Builder.getCurrentDebugLocation());
6096 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6097}
6098
6099// Returns an LLVM function to call for initializing loop bounds using OpenMP
6100// static scheduling for composite `distribute parallel for` depending on
6101// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6102// integers as unsigned similarly to CanonicalLoopInfo.
6103static FunctionCallee
6105 OpenMPIRBuilder &OMPBuilder) {
6106 unsigned Bitwidth = Ty->getIntegerBitWidth();
6107 if (Bitwidth == 32)
6108 return OMPBuilder.getOrCreateRuntimeFunction(
6109 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6110 if (Bitwidth == 64)
6111 return OMPBuilder.getOrCreateRuntimeFunction(
6112 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6113 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6114}
6115
6116// Returns an LLVM function to call for initializing loop bounds using OpenMP
6117// static scheduling depending on `type`. Only i32 and i64 are supported by the
6118// runtime. Always interpret integers as unsigned similarly to
6119// CanonicalLoopInfo.
6121 OpenMPIRBuilder &OMPBuilder) {
6122 unsigned Bitwidth = Ty->getIntegerBitWidth();
6123 if (Bitwidth == 32)
6124 return OMPBuilder.getOrCreateRuntimeFunction(
6125 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6126 if (Bitwidth == 64)
6127 return OMPBuilder.getOrCreateRuntimeFunction(
6128 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6129 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6130}
6131
6132OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6133 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6134 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6135 OMPScheduleType DistScheduleSchedType) {
6136 assert(CLI->isValid() && "Requires a valid canonical loop");
6137 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6138 "Require dedicated allocate IP");
6139
6140 // Set up the source location value for OpenMP runtime.
6141 Builder.restoreIP(CLI->getPreheaderIP());
6142 Builder.SetCurrentDebugLocation(DL);
6143
6144 uint32_t SrcLocStrSize;
6145 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6147 switch (LoopType) {
6148 case WorksharingLoopType::ForStaticLoop:
6149 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6150 break;
6151 case WorksharingLoopType::DistributeStaticLoop:
6152 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6153 break;
6154 case WorksharingLoopType::DistributeForStaticLoop:
6155 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6156 break;
6157 }
6158 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6159
6160 // Declare useful OpenMP runtime functions.
6161 Value *IV = CLI->getIndVar();
6162 Type *IVTy = IV->getType();
6163 FunctionCallee StaticInit =
6164 LoopType == WorksharingLoopType::DistributeForStaticLoop
6165 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6166 : getKmpcForStaticInitForType(IVTy, M, *this);
6167 FunctionCallee StaticFini =
6168 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6169
6170 // Allocate space for computed loop bounds as expected by the "init" function.
6171 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getFirstNonPHIOrAlloca());
6172
6173 Type *I32Type = Type::getInt32Ty(M.getContext());
6174 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6175 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6176 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6177 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6178 CLI->setLastIter(PLastIter);
6179
6180 // At the end of the preheader, prepare for calling the "init" function by
6181 // storing the current loop bounds into the allocated space. A canonical loop
6182 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6183 // and produces an inclusive upper bound.
6184 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6185 Constant *Zero = ConstantInt::get(IVTy, 0);
6186 Constant *One = ConstantInt::get(IVTy, 1);
6187 Builder.CreateStore(Zero, PLowerBound);
6188 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6189 Builder.CreateStore(UpperBound, PUpperBound);
6190 Builder.CreateStore(One, PStride);
6191
6192 Value *ThreadNum =
6193 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6194
6195 OMPScheduleType SchedType =
6196 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6197 ? OMPScheduleType::OrderedDistribute
6199 Constant *SchedulingType =
6200 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6201
6202 // Call the "init" function and update the trip count of the loop with the
6203 // value it produced.
6204 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6205 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6206 this](Value *SchedulingType, auto &Builder) {
6207 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6208 PLowerBound, PUpperBound});
6209 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6210 Value *PDistUpperBound =
6211 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6212 Args.push_back(PDistUpperBound);
6213 }
6214 Args.append({PStride, One, Zero});
6215 createRuntimeFunctionCall(StaticInit, Args);
6216 };
6217 BuildInitCall(SchedulingType, Builder);
6218 if (HasDistSchedule &&
6219 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6220 Constant *DistScheduleSchedType = ConstantInt::get(
6221 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6222 // We want to emit a second init function call for the dist_schedule clause
6223 // to the Distribute construct. This should only be done however if a
6224 // Workshare Loop is nested within a Distribute Construct
6225 BuildInitCall(DistScheduleSchedType, Builder);
6226 }
6227 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6228 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6229 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6230 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6231 CLI->setTripCount(TripCount);
6232
6233 // Update all uses of the induction variable except the one in the condition
6234 // block that compares it with the actual upper bound, and the increment in
6235 // the latch block.
6236
6237 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6238 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6239 Builder.SetCurrentDebugLocation(DL);
6240 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6241 /*HasNSW=*/Config.hasNoSignedWrap());
6242 });
6243
6244 // In the "exit" block, call the "fini" function.
6245 Builder.SetInsertPoint(CLI->getExit()->getTerminator()->getIterator());
6246 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6247
6248 // Add the barrier if requested.
6249 if (NeedsBarrier) {
6250 InsertPointOrErrorTy BarrierIP =
6252 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6253 /* CheckCancelFlag */ false);
6254 if (!BarrierIP)
6255 return BarrierIP.takeError();
6256 }
6257
6258 InsertPointTy AfterIP = CLI->getAfterIP();
6259 CLI->invalidate();
6260
6261 return AfterIP;
6262}
6263
6264static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6265 LoopInfo &LI);
6266static void addLoopMetadata(CanonicalLoopInfo *Loop,
6268
6270 LLVMContext &Ctx, Loop *Loop,
6272 SmallVector<Metadata *> &LoopMDList) {
6273 SmallSet<BasicBlock *, 8> Reachable;
6274
6275 // Get the basic blocks from the loop in which memref instructions
6276 // can be found.
6277 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6278 // preferably without running any passes.
6279 for (BasicBlock *Block : Loop->getBlocks()) {
6280 if (Block == CLI->getCond() || Block == CLI->getHeader())
6281 continue;
6282 Reachable.insert(Block);
6283 }
6284
6285 // Add access group metadata to memory-access instructions.
6287 for (BasicBlock *BB : Reachable)
6289 // TODO: If the loop has existing parallel access metadata, have
6290 // to combine two lists.
6291 LoopMDList.push_back(MDNode::get(
6292 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6293}
6294
6296OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6297 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6298 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6299 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6300 assert(CLI->isValid() && "Requires a valid canonical loop");
6301 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6302
6303 LLVMContext &Ctx = CLI->getFunction()->getContext();
6304 Value *IV = CLI->getIndVar();
6305 Value *OrigTripCount = CLI->getTripCount();
6306 Type *IVTy = IV->getType();
6307 assert(IVTy->getIntegerBitWidth() <= 64 &&
6308 "Max supported tripcount bitwidth is 64 bits");
6309 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6310 : Type::getInt64Ty(Ctx);
6311 Type *I32Type = Type::getInt32Ty(M.getContext());
6312 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6313 Constant *One = ConstantInt::get(InternalIVTy, 1);
6314
6315 Function *F = CLI->getFunction();
6316 // Blocks must have terminators.
6317 // FIXME: Don't run analyses on incomplete/invalid IR.
6318 SmallVector<Instruction *> UIs;
6319 for (BasicBlock &BB : *F)
6320 if (!BB.hasTerminator())
6321 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6323 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6324 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6325 LoopAnalysis LIA;
6326 LoopInfo &&LI = LIA.run(*F, FAM);
6327 for (Instruction *I : UIs)
6328 I->eraseFromParent();
6329 Loop *L = LI.getLoopFor(CLI->getHeader());
6330 SmallVector<Metadata *> LoopMDList;
6331 if (ChunkSize || DistScheduleChunkSize)
6332 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6333 addLoopMetadata(CLI, LoopMDList);
6334
6335 // Declare useful OpenMP runtime functions.
6336 FunctionCallee StaticInit =
6337 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6338 FunctionCallee StaticFini =
6339 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6340
6341 // Allocate space for computed loop bounds as expected by the "init" function.
6342 Builder.restoreIP(AllocaIP);
6343 Builder.SetCurrentDebugLocation(DL);
6344 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6345 Value *PLowerBound =
6346 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6347 Value *PUpperBound =
6348 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6349 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6350 CLI->setLastIter(PLastIter);
6351
6352 // Set up the source location value for the OpenMP runtime.
6353 Builder.restoreIP(CLI->getPreheaderIP());
6354 Builder.SetCurrentDebugLocation(DL);
6355
6356 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6357 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6358 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6359 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6360 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6361 "distschedulechunksize");
6362 Value *CastedTripCount =
6363 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6364
6365 Constant *SchedulingType =
6366 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6367 Constant *DistSchedulingType =
6368 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6369 Builder.CreateStore(Zero, PLowerBound);
6370 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6371 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6372 Value *UpperBound =
6373 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6374 Builder.CreateStore(UpperBound, PUpperBound);
6375 Builder.CreateStore(One, PStride);
6376
6377 // Call the "init" function and update the trip count of the loop with the
6378 // value it produced.
6379 uint32_t SrcLocStrSize;
6380 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6381 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6382 if (DistScheduleSchedType != OMPScheduleType::None) {
6383 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6384 }
6385 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6386 Value *ThreadNum =
6387 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6388 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6389 PUpperBound, PStride, One,
6390 this](Value *SchedulingType, Value *ChunkSize,
6391 auto &Builder) {
6393 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6394 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6395 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6396 /*pstride=*/PStride, /*incr=*/One,
6397 /*chunk=*/ChunkSize});
6398 };
6399 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6400 if (DistScheduleSchedType != OMPScheduleType::None &&
6401 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6402 SchedType != OMPScheduleType::OrderedDistribute) {
6403 // We want to emit a second init function call for the dist_schedule clause
6404 // to the Distribute construct. This should only be done however if a
6405 // Workshare Loop is nested within a Distribute Construct
6406 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6407 }
6408
6409 // Load values written by the "init" function.
6410 Value *FirstChunkStart =
6411 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6412 Value *FirstChunkStop =
6413 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6414 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6415 Value *ChunkRange =
6416 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6417 Value *NextChunkStride =
6418 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6419
6420 // Create outer "dispatch" loop for enumerating the chunks.
6421 BasicBlock *DispatchEnter = splitBB(Builder, true);
6422 Value *DispatchCounter;
6423
6424 // It is safe to assume this didn't return an error because the callback
6425 // passed into createCanonicalLoop is the only possible error source, and it
6426 // always returns success.
6427 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6428 {Builder.saveIP(), DL},
6429 [&](InsertPointTy BodyIP, Value *Counter) {
6430 DispatchCounter = Counter;
6431 return Error::success();
6432 },
6433 FirstChunkStart, CastedTripCount, NextChunkStride,
6434 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6435 "dispatch"));
6436
6437 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6438 // not have to preserve the canonical invariant.
6439 BasicBlock *DispatchBody = DispatchCLI->getBody();
6440 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6441 BasicBlock *DispatchExit = DispatchCLI->getExit();
6442 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6443 DispatchCLI->invalidate();
6444
6445 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6446 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6447 redirectTo(CLI->getExit(), DispatchLatch, DL);
6448 redirectTo(DispatchBody, DispatchEnter, DL);
6449
6450 // Prepare the prolog of the chunk loop.
6451 Builder.restoreIP(CLI->getPreheaderIP());
6452 Builder.SetCurrentDebugLocation(DL);
6453
6454 // Compute the number of iterations of the chunk loop.
6455 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6456 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6457 Value *IsLastChunk =
6458 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6459 Value *CountUntilOrigTripCount =
6460 Builder.CreateSub(CastedTripCount, DispatchCounter);
6461 Value *ChunkTripCount = Builder.CreateSelect(
6462 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6463 Value *BackcastedChunkTC =
6464 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6465 CLI->setTripCount(BackcastedChunkTC);
6466
6467 // Update all uses of the induction variable except the one in the condition
6468 // block that compares it with the actual upper bound, and the increment in
6469 // the latch block.
6470 Value *BackcastedDispatchCounter =
6471 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6472 CLI->mapIndVar([&](Instruction *) -> Value * {
6473 Builder.restoreIP(CLI->getBodyIP());
6474 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6475 });
6476
6477 // In the "exit" block, call the "fini" function.
6478 Builder.SetInsertPoint(DispatchExit->getFirstInsertionPt());
6479 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6480
6481 // Add the barrier if requested.
6482 if (NeedsBarrier) {
6483 InsertPointOrErrorTy AfterIP =
6484 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6485 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6486 if (!AfterIP)
6487 return AfterIP.takeError();
6488 }
6489
6490#ifndef NDEBUG
6491 // Even though we currently do not support applying additional methods to it,
6492 // the chunk loop should remain a canonical loop.
6493 CLI->assertOK();
6494#endif
6495
6496 return DispatchAfter->getFirstInsertionPt();
6497}
6498
6499// Returns an LLVM function to call for executing an OpenMP static worksharing
6500// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6501// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6502static FunctionCallee
6504 WorksharingLoopType LoopType) {
6505 unsigned Bitwidth = Ty->getIntegerBitWidth();
6506 Module &M = OMPBuilder->M;
6507 switch (LoopType) {
6508 case WorksharingLoopType::ForStaticLoop:
6509 if (Bitwidth == 32)
6510 return OMPBuilder->getOrCreateRuntimeFunction(
6511 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6512 if (Bitwidth == 64)
6513 return OMPBuilder->getOrCreateRuntimeFunction(
6514 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6515 break;
6516 case WorksharingLoopType::DistributeStaticLoop:
6517 if (Bitwidth == 32)
6518 return OMPBuilder->getOrCreateRuntimeFunction(
6519 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6520 if (Bitwidth == 64)
6521 return OMPBuilder->getOrCreateRuntimeFunction(
6522 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6523 break;
6524 case WorksharingLoopType::DistributeForStaticLoop:
6525 if (Bitwidth == 32)
6526 return OMPBuilder->getOrCreateRuntimeFunction(
6527 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6528 if (Bitwidth == 64)
6529 return OMPBuilder->getOrCreateRuntimeFunction(
6530 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6531 break;
6532 }
6533 if (Bitwidth != 32 && Bitwidth != 64) {
6534 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6535 }
6536 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6537}
6538
6539// Inserts a call to proper OpenMP Device RTL function which handles
6540// loop worksharing.
6542 WorksharingLoopType LoopType,
6543 BasicBlock *InsertBlock, Value *Ident,
6544 Value *LoopBodyArg, Value *TripCount,
6545 Function &LoopBodyFn, bool NoLoop) {
6546 Type *TripCountTy = TripCount->getType();
6547 Module &M = OMPBuilder->M;
6548 IRBuilder<> &Builder = OMPBuilder->Builder;
6549 FunctionCallee RTLFn =
6550 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6551 SmallVector<Value *, 8> RealArgs;
6552 RealArgs.push_back(Ident);
6553 RealArgs.push_back(&LoopBodyFn);
6554 RealArgs.push_back(LoopBodyArg);
6555 RealArgs.push_back(TripCount);
6556 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6557 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6558 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6559 Builder.restoreIP(std::prev(InsertBlock->end()));
6560 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6561 return;
6562 }
6563 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6564 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6565 Builder.restoreIP(std::prev(InsertBlock->end()));
6566 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6567
6568 RealArgs.push_back(
6569 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6570 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6571 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6572 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6573 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6574 } else {
6575 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6576 }
6577
6578 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6579}
6580
6582 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6583 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6584 WorksharingLoopType LoopType, bool NoLoop) {
6585 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6586 BasicBlock *Preheader = CLI->getPreheader();
6587 Value *TripCount = CLI->getTripCount();
6588
6589 // After loop body outling, the loop body contains only set up
6590 // of loop body argument structure and the call to the outlined
6591 // loop body function. Firstly, we need to move setup of loop body args
6592 // into loop preheader.
6593 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6594 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6595
6596 // The next step is to remove the whole loop. We do not it need anymore.
6597 // That's why make an unconditional branch from loop preheader to loop
6598 // exit block
6599 Builder.restoreIP(Preheader->end());
6600 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6601 Preheader->getTerminator()->eraseFromParent();
6602 Builder.CreateBr(CLI->getExit());
6603
6604 // Delete dead loop blocks
6605 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6606 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6607 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6608 CleanUpInfo.EntryBB = CLI->getHeader();
6609 CleanUpInfo.ExitBB = CLI->getExit();
6610 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6611 DeleteDeadBlocks(BlocksToBeRemoved);
6612
6613 // Find the instruction which corresponds to loop body argument structure
6614 // and remove the call to loop body function instruction.
6615 Value *LoopBodyArg;
6616 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6617 assert(OutlinedFnUser &&
6618 "Expected unique undroppable user of outlined function");
6619 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6620 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6621 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6622 "Expected outlined function call to be located in loop preheader");
6623 // Check in case no argument structure has been passed.
6624 if (OutlinedFnCallInstruction->arg_size() > 1)
6625 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6626 else
6627 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6628 OutlinedFnCallInstruction->eraseFromParent();
6629
6630 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6631 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6632
6633 for (auto &ToBeDeletedItem : ToBeDeleted)
6634 ToBeDeletedItem->eraseFromParent();
6635 CLI->invalidate();
6636}
6637
6638OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6639 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6640 WorksharingLoopType LoopType, bool NeedsBarrier, bool NoLoop,
6641 bool NeedsLastIter) {
6642 uint32_t SrcLocStrSize;
6643 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6644
6645 // Mirrors host runtime reporting of last iteration by in-body computation.
6646 if (NeedsLastIter) {
6647 Type *I32Type = Type::getInt32Ty(M.getContext());
6648 Builder.restoreIP(AllocaIP);
6649 AllocaInst *PLastIter =
6650 Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6651 CLI->setLastIter(PLastIter);
6652
6653 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6654 Builder.CreateStore(ConstantInt::get(I32Type, 0), PLastIter);
6655
6656 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6657 Value *TripCount = CLI->getTripCount();
6658 Value *LastIter =
6659 Builder.CreateSub(TripCount, ConstantInt::get(TripCount->getType(), 1));
6660 Value *IsLast =
6661 Builder.CreateICmpEQ(CLI->getIndVar(), LastIter, "omp.is_last_iter");
6662 Builder.CreateStore(Builder.CreateZExt(IsLast, I32Type), PLastIter);
6663 }
6664
6666 switch (LoopType) {
6667 case WorksharingLoopType::ForStaticLoop:
6668 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6669 break;
6670 case WorksharingLoopType::DistributeStaticLoop:
6671 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6672 break;
6673 case WorksharingLoopType::DistributeForStaticLoop:
6674 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6675 break;
6676 }
6677 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6678
6679 auto OI = std::make_unique<OutlineInfo>();
6680 OI->OuterAllocBB = CLI->getPreheader();
6681 Function *OuterFn = CLI->getPreheader()->getParent();
6682
6683 // Instructions which need to be deleted at the end of code generation
6684 SmallVector<Instruction *, 4> ToBeDeleted;
6685
6686 OI->OuterAllocBB = AllocaIP.getNodeParent();
6687
6688 // Mark the body loop as region which needs to be extracted
6689 OI->EntryBB = CLI->getBody();
6690 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6691 "omp.prelatch");
6692
6693 // Prepare loop body for extraction
6694 Builder.restoreIP(CLI->getPreheader()->begin());
6695
6696 // Insert new loop counter variable which will be used only in loop
6697 // body.
6698 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6699 Instruction *NewLoopCntLoad =
6700 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6701 // New loop counter instructions are redundant in the loop preheader when
6702 // code generation for workshare loop is finshed. That's why mark them as
6703 // ready for deletion.
6704 ToBeDeleted.push_back(NewLoopCntLoad);
6705 ToBeDeleted.push_back(NewLoopCnt);
6706
6707 // Analyse loop body region. Find all input variables which are used inside
6708 // loop body region.
6709 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6711 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6712
6713 CodeExtractorAnalysisCache CEAC(*OuterFn);
6714 CodeExtractor Extractor(Blocks,
6715 /* DominatorTree */ nullptr,
6716 /* AggregateArgs */ true,
6717 /* BlockFrequencyInfo */ nullptr,
6718 /* BranchProbabilityInfo */ nullptr,
6719 /* AssumptionCache */ nullptr,
6720 /* AllowVarArgs */ true,
6721 /* AllowAlloca */ true,
6722 /* AllocationBlock */ CLI->getPreheader(),
6723 /* DeallocationBlocks */ {},
6724 /* Suffix */ ".omp_wsloop",
6725 /* AggrArgsIn0AddrSpace */ true);
6726
6727 BasicBlock *CommonExit = nullptr;
6728 SetVector<Value *> SinkingCands, HoistingCands;
6729
6730 // Find allocas outside the loop body region which are used inside loop
6731 // body
6732 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6733
6734 // We need to model loop body region as the function f(cnt, loop_arg).
6735 // That's why we replace loop induction variable by the new counter
6736 // which will be one of loop body function argument
6738 CLI->getIndVar()->user_end());
6739 for (auto Use : Users) {
6740 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6741 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6742 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6743 }
6744 }
6745 }
6746 // Make sure that loop counter variable is not merged into loop body
6747 // function argument structure and it is passed as separate variable
6748 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6749
6750 // PostOutline CB is invoked when loop body function is outlined and
6751 // loop body is replaced by call to outlined function. We need to add
6752 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6753 // function will handle loop control logic.
6754 //
6755 OI->PostOutlineCB = [=, ToBeDeletedVec =
6756 std::move(ToBeDeleted)](Function &OutlinedFn) {
6757 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6758 LoopType, NoLoop);
6759 };
6760 addOutlineInfo(std::move(OI));
6761
6762 // Keep the barrier outside the outlined loop body so that every thread
6763 // encounters it, including threads that execute no iterations.
6764 if (NeedsBarrier) {
6765 Builder.SetInsertPoint(CLI->getExit()->getTerminator());
6766 // Standalone distribute loops never request a barrier. For both regular
6767 // worksharing loops and combined distribute/for loops, the barrier is
6768 // associated with the worksharing loop, hence OMPD_for.
6769 InsertPointOrErrorTy BarrierIP =
6770 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6771 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6772 if (!BarrierIP)
6773 return BarrierIP.takeError();
6774 }
6775 return CLI->getAfterIP();
6776}
6777
6780 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6781 bool HasSimdModifier, bool HasMonotonicModifier,
6782 bool HasNonmonotonicModifier, bool HasOrderedClause,
6783 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6784 Value *DistScheduleChunkSize, bool NeedsLastIter) {
6785 if (Config.isTargetDevice())
6786 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6787 NoLoop, NeedsLastIter);
6788 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6789 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6790 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6791
6792 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6793 OMPScheduleType::ModifierOrdered;
6794 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6795 if (HasDistSchedule) {
6796 DistScheduleSchedType = DistScheduleChunkSize
6797 ? OMPScheduleType::OrderedDistributeChunked
6798 : OMPScheduleType::OrderedDistribute;
6799 }
6800 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6801 case OMPScheduleType::BaseStatic:
6802 case OMPScheduleType::BaseDistribute:
6803 assert((!ChunkSize || !DistScheduleChunkSize) &&
6804 "No chunk size with static-chunked schedule");
6805 if (IsOrdered && !HasDistSchedule)
6806 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6807 NeedsBarrier, ChunkSize);
6808 // FIXME: Monotonicity ignored?
6809 if (DistScheduleChunkSize)
6810 return applyStaticChunkedWorkshareLoop(
6811 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6812 DistScheduleChunkSize, DistScheduleSchedType);
6813 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6814 HasDistSchedule);
6815
6816 case OMPScheduleType::BaseStaticChunked:
6817 case OMPScheduleType::BaseDistributeChunked:
6818 if (IsOrdered && !HasDistSchedule)
6819 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6820 NeedsBarrier, ChunkSize);
6821 // FIXME: Monotonicity ignored?
6822 return applyStaticChunkedWorkshareLoop(
6823 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6824 DistScheduleChunkSize, DistScheduleSchedType);
6825
6826 case OMPScheduleType::BaseRuntime:
6827 case OMPScheduleType::BaseAuto:
6828 case OMPScheduleType::BaseGreedy:
6829 case OMPScheduleType::BaseBalanced:
6830 case OMPScheduleType::BaseSteal:
6831 case OMPScheduleType::BaseRuntimeSimd:
6832 assert(!ChunkSize &&
6833 "schedule type does not support user-defined chunk sizes");
6834 [[fallthrough]];
6835 case OMPScheduleType::BaseGuidedSimd:
6836 case OMPScheduleType::BaseDynamicChunked:
6837 case OMPScheduleType::BaseGuidedChunked:
6838 case OMPScheduleType::BaseGuidedIterativeChunked:
6839 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6840 case OMPScheduleType::BaseStaticBalancedChunked:
6841 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6842 NeedsBarrier, ChunkSize);
6843
6844 default:
6845 llvm_unreachable("Unknown/unimplemented schedule kind");
6846 }
6847}
6848
6849/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6850/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6851/// the runtime. Always interpret integers as unsigned similarly to
6852/// CanonicalLoopInfo.
6853static FunctionCallee
6855 unsigned Bitwidth = Ty->getIntegerBitWidth();
6856 if (Bitwidth == 32)
6857 return OMPBuilder.getOrCreateRuntimeFunction(
6858 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6859 if (Bitwidth == 64)
6860 return OMPBuilder.getOrCreateRuntimeFunction(
6861 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6862 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6863}
6864
6865/// Returns an LLVM function to call for updating the next loop using OpenMP
6866/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6867/// the runtime. Always interpret integers as unsigned similarly to
6868/// CanonicalLoopInfo.
6869static FunctionCallee
6871 unsigned Bitwidth = Ty->getIntegerBitWidth();
6872 if (Bitwidth == 32)
6873 return OMPBuilder.getOrCreateRuntimeFunction(
6874 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6875 if (Bitwidth == 64)
6876 return OMPBuilder.getOrCreateRuntimeFunction(
6877 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6878 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6879}
6880
6881/// Returns an LLVM function to call for finalizing the dynamic loop using
6882/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6883/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6884static FunctionCallee
6886 unsigned Bitwidth = Ty->getIntegerBitWidth();
6887 if (Bitwidth == 32)
6888 return OMPBuilder.getOrCreateRuntimeFunction(
6889 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6890 if (Bitwidth == 64)
6891 return OMPBuilder.getOrCreateRuntimeFunction(
6892 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6893 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6894}
6895
6897OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6898 InsertPointTy AllocaIP,
6899 OMPScheduleType SchedType,
6900 bool NeedsBarrier, Value *Chunk) {
6901 assert(CLI->isValid() && "Requires a valid canonical loop");
6902 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6903 "Require dedicated allocate IP");
6905 "Require valid schedule type");
6906
6907 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6908 OMPScheduleType::ModifierOrdered;
6909
6910 // Set up the source location value for OpenMP runtime.
6911 Builder.SetCurrentDebugLocation(DL);
6912
6913 uint32_t SrcLocStrSize;
6914 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6915 Value *SrcLoc =
6916 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6917
6918 // Declare useful OpenMP runtime functions.
6919 Value *IV = CLI->getIndVar();
6920 Type *IVTy = IV->getType();
6921 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6922 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6923
6924 // Allocate space for computed loop bounds as expected by the "init" function.
6925 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getFirstNonPHIOrAlloca());
6926 Type *I32Type = Type::getInt32Ty(M.getContext());
6927 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6928 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6929 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6930 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6931 CLI->setLastIter(PLastIter);
6932
6933 // At the end of the preheader, prepare for calling the "init" function by
6934 // storing the current loop bounds into the allocated space. A canonical loop
6935 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6936 // and produces an inclusive upper bound.
6937 BasicBlock *PreHeader = CLI->getPreheader();
6938 Builder.SetInsertPoint(PreHeader->getTerminator());
6939 Constant *One = ConstantInt::get(IVTy, 1);
6940 Builder.CreateStore(One, PLowerBound);
6941 Value *UpperBound = CLI->getTripCount();
6942 Builder.CreateStore(UpperBound, PUpperBound);
6943 Builder.CreateStore(One, PStride);
6944
6945 BasicBlock *Header = CLI->getHeader();
6946 BasicBlock *Exit = CLI->getExit();
6947 BasicBlock *Cond = CLI->getCond();
6948 BasicBlock *Latch = CLI->getLatch();
6949 InsertPointTy AfterIP = CLI->getAfterIP();
6950
6951 // The CLI will be "broken" in the code below, as the loop is no longer
6952 // a valid canonical loop.
6953
6954 if (!Chunk)
6955 Chunk = One;
6956
6957 Value *ThreadNum =
6958 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6959
6960 Constant *SchedulingType =
6961 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6962
6963 // Call the "init" function.
6964 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6965 /* LowerBound */ One, UpperBound,
6966 /* step */ One, Chunk});
6967
6968 // An outer loop around the existing one.
6969 BasicBlock *OuterCond = BasicBlock::Create(
6970 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6971 PreHeader->getParent());
6972 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6973 Builder.SetInsertPoint(OuterCond->getFirstInsertionPt());
6975 DynamicNext,
6976 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6977 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6978 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6979 Value *LowerBound =
6980 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6981 Builder.CreateCondBr(MoreWork, Header, Exit);
6982
6983 // Change PHI-node in loop header to use outer cond rather than preheader,
6984 // and set IV to the LowerBound.
6985 Instruction *Phi = &Header->front();
6986 auto *PI = cast<PHINode>(Phi);
6987 PI->setIncomingBlock(0, OuterCond);
6988 PI->setIncomingValue(0, LowerBound);
6989
6990 // Then set the pre-header to jump to the OuterCond
6991 Instruction *Term = PreHeader->getTerminator();
6992 auto *Br = cast<UncondBrInst>(Term);
6993 Br->setSuccessor(OuterCond);
6994
6995 // Modify the inner condition:
6996 // * Use the UpperBound returned from the DynamicNext call.
6997 // * jump to the loop outer loop when done with one of the inner loops.
6998 Builder.SetInsertPoint(Cond->getFirstInsertionPt());
6999 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
7000 Instruction *Comp = &*Builder.GetInsertPoint();
7001 auto *CI = cast<CmpInst>(Comp);
7002 CI->setOperand(1, UpperBound);
7003 // Redirect the inner exit to branch to outer condition.
7004 Instruction *Branch = &Cond->back();
7005 auto *BI = cast<CondBrInst>(Branch);
7006 assert(BI->getSuccessor(1) == Exit);
7007 BI->setSuccessor(1, OuterCond);
7008
7009 // Call the "fini" function if "ordered" is present in wsloop directive.
7010 if (Ordered) {
7011 Builder.SetInsertPoint(&Latch->back());
7012 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
7013 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
7014 }
7015
7016 // Add the barrier if requested.
7017 if (NeedsBarrier) {
7018 Builder.SetInsertPoint(&Exit->back());
7019 InsertPointOrErrorTy BarrierIP =
7021 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
7022 /* CheckCancelFlag */ false);
7023 if (!BarrierIP)
7024 return BarrierIP.takeError();
7025 }
7026
7027 CLI->invalidate();
7028 return AfterIP;
7029}
7030
7031/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
7032/// after this \p OldTarget will be orphaned.
7034 BasicBlock *NewTarget, DebugLoc DL) {
7035 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
7036 redirectTo(Pred, NewTarget, DL);
7037}
7038
7040 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
7041 // We add a block to BBsToKeep iff we have proven it has an external use.
7043
7044 while (true) {
7045 bool Changed = false;
7046
7047 for (BasicBlock *BB : BBs) {
7048 if (BBsToKeep.contains(BB))
7049 continue;
7050
7051 for (Use &U : BB->uses()) {
7052 auto *UseInst = dyn_cast<Instruction>(U.getUser());
7053 if (!UseInst)
7054 continue;
7055 BasicBlock *UseBB = UseInst->getParent();
7056 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
7057 BBsToKeep.insert(BB);
7058 Changed = true;
7059 break;
7060 }
7061 }
7062 }
7063
7064 if (!Changed)
7065 break;
7066 }
7067
7069 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7070 DeleteDeadBlocks(BBsToDelete);
7071}
7072
7073CanonicalLoopInfo *
7075 InsertPointTy ComputeIP) {
7076 assert(Loops.size() >= 1 && "At least one loop required");
7077 size_t NumLoops = Loops.size();
7078
7079 // Nothing to do if there is already just one loop.
7080 if (NumLoops == 1)
7081 return Loops.front();
7082
7083 CanonicalLoopInfo *Outermost = Loops.front();
7084 CanonicalLoopInfo *Innermost = Loops.back();
7085 BasicBlock *OrigPreheader = Outermost->getPreheader();
7086 BasicBlock *OrigAfter = Outermost->getAfter();
7087 Function *F = OrigPreheader->getParent();
7088
7089 // Loop control blocks that may become orphaned later.
7090 SmallVector<BasicBlock *, 12> OldControlBBs;
7091 OldControlBBs.reserve(6 * Loops.size());
7093 Loop->collectControlBlocks(OldControlBBs);
7094
7095 // Setup the IRBuilder for inserting the trip count computation.
7096 Builder.SetCurrentDebugLocation(DL);
7097 if (ComputeIP.isValid())
7098 Builder.restoreIP(ComputeIP);
7099 else
7100 Builder.restoreIP(Outermost->getPreheaderIP());
7101
7102 // Derive the collapsed' loop trip count.
7103 // TODO: Find common/largest indvar type.
7104 Value *CollapsedTripCount = nullptr;
7105 for (CanonicalLoopInfo *L : Loops) {
7106 assert(L->isValid() &&
7107 "All loops to collapse must be valid canonical loops");
7108 Value *OrigTripCount = L->getTripCount();
7109 if (!CollapsedTripCount) {
7110 CollapsedTripCount = OrigTripCount;
7111 continue;
7112 }
7113
7114 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7115 CollapsedTripCount =
7116 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7117 }
7118
7119 // Create the collapsed loop control flow.
7120 CanonicalLoopInfo *Result =
7121 createLoopSkeleton(DL, CollapsedTripCount, F,
7122 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7123 /*IsCollapsed=*/true);
7124
7125 // Build the collapsed loop body code.
7126 // Start with deriving the input loop induction variables from the collapsed
7127 // one, using a divmod scheme. To preserve the original loops' order, the
7128 // innermost loop use the least significant bits.
7129 Builder.restoreIP(Result->getBodyIP());
7130
7131 Value *Leftover = Result->getIndVar();
7132 SmallVector<Value *> NewIndVars;
7133 NewIndVars.resize(NumLoops);
7134 for (int i = NumLoops - 1; i >= 1; --i) {
7135 Value *OrigTripCount = Loops[i]->getTripCount();
7136
7137 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7138 NewIndVars[i] = NewIndVar;
7139
7140 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7141 }
7142 // Outermost loop gets all the remaining bits.
7143 NewIndVars[0] = Leftover;
7144
7145 // Construct the loop body control flow.
7146 // We progressively construct the branch structure following in direction of
7147 // the control flow, from the leading in-between code, the loop nest body, the
7148 // trailing in-between code, and rejoining the collapsed loop's latch.
7149 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7150 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7151 // its predecessors as sources.
7152 BasicBlock *ContinueBlock = Result->getBody();
7153 BasicBlock *ContinuePred = nullptr;
7154 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7155 BasicBlock *NextSrc) {
7156 if (ContinueBlock)
7157 redirectTo(ContinueBlock, Dest, DL);
7158 else
7159 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7160
7161 ContinueBlock = nullptr;
7162 ContinuePred = NextSrc;
7163 };
7164
7165 // The code before the nested loop of each level.
7166 // Because we are sinking it into the nest, it will be executed more often
7167 // that the original loop. More sophisticated schemes could keep track of what
7168 // the in-between code is and instantiate it only once per thread.
7169 for (size_t i = 0; i < NumLoops - 1; ++i)
7170 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7171
7172 // Connect the loop nest body.
7173 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7174
7175 // The code after the nested loop at each level.
7176 for (size_t i = NumLoops - 1; i > 0; --i)
7177 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7178
7179 // Connect the finished loop to the collapsed loop latch.
7180 ContinueWith(Result->getLatch(), nullptr);
7181
7182 // Replace the input loops with the new collapsed loop.
7183 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7184 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7185
7186 // Replace the input loop indvars with the derived ones.
7187 for (size_t i = 0; i < NumLoops; ++i)
7188 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7189
7190 // Remove unused parts of the input loops.
7191 removeUnusedBlocksFromParent(OldControlBBs);
7192
7193 for (CanonicalLoopInfo *L : Loops)
7194 L->invalidate();
7195
7196#ifndef NDEBUG
7197 Result->assertOK();
7198#endif
7199 return Result;
7200}
7201
7202std::vector<CanonicalLoopInfo *>
7204 ArrayRef<Value *> TileSizes) {
7205 assert(TileSizes.size() == Loops.size() &&
7206 "Must pass as many tile sizes as there are loops");
7207 int NumLoops = Loops.size();
7208 assert(NumLoops >= 1 && "At least one loop to tile required");
7209
7210 CanonicalLoopInfo *OutermostLoop = Loops.front();
7211 CanonicalLoopInfo *InnermostLoop = Loops.back();
7212 Function *F = OutermostLoop->getBody()->getParent();
7213 BasicBlock *InnerEnter = InnermostLoop->getBody();
7214 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7215
7216 // Loop control blocks that may become orphaned later.
7217 SmallVector<BasicBlock *, 12> OldControlBBs;
7218 OldControlBBs.reserve(6 * Loops.size());
7220 Loop->collectControlBlocks(OldControlBBs);
7221
7222 // Collect original trip counts and induction variable to be accessible by
7223 // index. Also, the structure of the original loops is not preserved during
7224 // the construction of the tiled loops, so do it before we scavenge the BBs of
7225 // any original CanonicalLoopInfo.
7226 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7227 for (CanonicalLoopInfo *L : Loops) {
7228 assert(L->isValid() && "All input loops must be valid canonical loops");
7229 OrigTripCounts.push_back(L->getTripCount());
7230 OrigIndVars.push_back(L->getIndVar());
7231 }
7232
7233 // Collect the code between loop headers. These may contain SSA definitions
7234 // that are used in the loop nest body. To be usable with in the innermost
7235 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7236 // these instructions may be executed more often than before the tiling.
7237 // TODO: It would be sufficient to only sink them into body of the
7238 // corresponding tile loop.
7240 for (int i = 0; i < NumLoops - 1; ++i) {
7241 CanonicalLoopInfo *Surrounding = Loops[i];
7242 CanonicalLoopInfo *Nested = Loops[i + 1];
7243
7244 BasicBlock *EnterBB = Surrounding->getBody();
7245 BasicBlock *ExitBB = Nested->getHeader();
7246 InbetweenCode.emplace_back(EnterBB, ExitBB);
7247 }
7248
7249 // Compute the trip counts of the floor loops.
7250 Builder.SetCurrentDebugLocation(DL);
7251 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7252 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7253 for (int i = 0; i < NumLoops; ++i) {
7254 Value *TileSize = TileSizes[i];
7255 Value *OrigTripCount = OrigTripCounts[i];
7256 Type *IVType = OrigTripCount->getType();
7257
7258 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7259 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7260
7261 // 0 if tripcount divides the tilesize, 1 otherwise.
7262 // 1 means we need an additional iteration for a partial tile.
7263 //
7264 // Unfortunately we cannot just use the roundup-formula
7265 // (tripcount + tilesize - 1)/tilesize
7266 // because the summation might overflow. We do not want introduce undefined
7267 // behavior when the untiled loop nest did not.
7268 Value *FloorTripOverflow =
7269 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7270
7271 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7272 Value *FloorTripCount =
7273 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7274 "omp_floor" + Twine(i) + ".tripcount", true);
7275
7276 // Remember some values for later use.
7277 FloorCompleteCount.push_back(FloorCompleteTripCount);
7278 FloorCount.push_back(FloorTripCount);
7279 FloorRems.push_back(FloorTripRem);
7280 }
7281
7282 // Generate the new loop nest, from the outermost to the innermost.
7283 std::vector<CanonicalLoopInfo *> Result;
7284 Result.reserve(NumLoops * 2);
7285
7286 // The basic block of the surrounding loop that enters the nest generated
7287 // loop.
7288 BasicBlock *Enter = OutermostLoop->getPreheader();
7289
7290 // The basic block of the surrounding loop where the inner code should
7291 // continue.
7292 BasicBlock *Continue = OutermostLoop->getAfter();
7293
7294 // Where the next loop basic block should be inserted.
7295 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7296
7297 auto EmbeddNewLoop =
7298 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7299 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7300 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7301 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7302 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7303 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7304
7305 // Setup the position where the next embedded loop connects to this loop.
7306 Enter = EmbeddedLoop->getBody();
7307 Continue = EmbeddedLoop->getLatch();
7308 OutroInsertBefore = EmbeddedLoop->getLatch();
7309 return EmbeddedLoop;
7310 };
7311
7312 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7313 const Twine &NameBase) {
7314 for (auto P : enumerate(TripCounts)) {
7315 CanonicalLoopInfo *EmbeddedLoop =
7316 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7317 Result.push_back(EmbeddedLoop);
7318 }
7319 };
7320
7321 EmbeddNewLoops(FloorCount, "floor");
7322
7323 // Within the innermost floor loop, emit the code that computes the tile
7324 // sizes.
7325 Builder.SetInsertPoint(Enter->getTerminator());
7326 SmallVector<Value *, 4> TileCounts;
7327 for (int i = 0; i < NumLoops; ++i) {
7328 CanonicalLoopInfo *FloorLoop = Result[i];
7329 Value *TileSize = TileSizes[i];
7330
7331 Value *FloorIsEpilogue =
7332 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7333 Value *TileTripCount =
7334 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7335
7336 TileCounts.push_back(TileTripCount);
7337 }
7338
7339 // Create the tile loops.
7340 EmbeddNewLoops(TileCounts, "tile");
7341
7342 // Insert the inbetween code into the body.
7343 BasicBlock *BodyEnter = Enter;
7344 BasicBlock *BodyEntered = nullptr;
7345 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7346 BasicBlock *EnterBB = P.first;
7347 BasicBlock *ExitBB = P.second;
7348
7349 if (BodyEnter)
7350 redirectTo(BodyEnter, EnterBB, DL);
7351 else
7352 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7353
7354 BodyEnter = nullptr;
7355 BodyEntered = ExitBB;
7356 }
7357
7358 // Append the original loop nest body into the generated loop nest body.
7359 if (BodyEnter)
7360 redirectTo(BodyEnter, InnerEnter, DL);
7361 else
7362 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7364
7365 // Replace the original induction variable with an induction variable computed
7366 // from the tile and floor induction variables.
7367 Builder.restoreIP(Result.back()->getBodyIP());
7368 for (int i = 0; i < NumLoops; ++i) {
7369 CanonicalLoopInfo *FloorLoop = Result[i];
7370 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7371 Value *OrigIndVar = OrigIndVars[i];
7372 Value *Size = TileSizes[i];
7373
7374 Value *Scale =
7375 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7376 Value *Shift =
7377 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7378 OrigIndVar->replaceAllUsesWith(Shift);
7379 }
7380
7381 // Remove unused parts of the original loops.
7382 removeUnusedBlocksFromParent(OldControlBBs);
7383
7384 for (CanonicalLoopInfo *L : Loops)
7385 L->invalidate();
7386
7387#ifndef NDEBUG
7388 for (CanonicalLoopInfo *GenL : Result)
7389 GenL->assertOK();
7390#endif
7391 return Result;
7392}
7393
7394/// Attach metadata \p Properties to the basic block described by \p BB. If the
7395/// basic block already has metadata, the basic block properties are appended.
7398 // Nothing to do if no property to attach.
7399 if (Properties.empty())
7400 return;
7401
7402 LLVMContext &Ctx = BB->getContext();
7403 SmallVector<Metadata *> NewProperties;
7404 NewProperties.push_back(nullptr);
7405
7406 // If the basic block already has metadata, prepend it to the new metadata.
7407 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7408 if (Existing)
7409 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7410
7411 append_range(NewProperties, Properties);
7412 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7413 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7414
7415 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7416}
7417
7418/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7419/// loop already has metadata, the loop properties are appended.
7422 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7423
7424 // Attach metadata to the loop's latch
7425 BasicBlock *Latch = Loop->getLatch();
7426 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7428}
7429
7430/// Attach llvm.access.group metadata to the memref instructions of \p Block
7432 LoopInfo &LI) {
7433 for (Instruction &I : *Block) {
7434 if (I.mayReadOrWriteMemory()) {
7435 // TODO: This instruction may already have access group from
7436 // other pragmas e.g. #pragma clang loop vectorize. Append
7437 // so that the existing metadata is not overwritten.
7438 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7439 }
7440 }
7441}
7442
7443CanonicalLoopInfo *
7445 CanonicalLoopInfo *firstLoop = Loops.front();
7446 CanonicalLoopInfo *lastLoop = Loops.back();
7447 Function *F = firstLoop->getPreheader()->getParent();
7448
7449 // Loop control blocks that will become orphaned later
7450 SmallVector<BasicBlock *> oldControlBBs;
7452 Loop->collectControlBlocks(oldControlBBs);
7453
7454 // Collect original trip counts
7455 SmallVector<Value *> origTripCounts;
7456 for (CanonicalLoopInfo *L : Loops) {
7457 assert(L->isValid() && "All input loops must be valid canonical loops");
7458 origTripCounts.push_back(L->getTripCount());
7459 }
7460
7461 Builder.SetCurrentDebugLocation(DL);
7462
7463 // Compute max trip count.
7464 // The fused loop will be from 0 to max(origTripCounts)
7465 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7466 F, firstLoop->getHeader());
7467 Builder.SetInsertPoint(TCBlock);
7468 Value *fusedTripCount = nullptr;
7469 for (CanonicalLoopInfo *L : Loops) {
7470 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7471 Value *origTripCount = L->getTripCount();
7472 if (!fusedTripCount) {
7473 fusedTripCount = origTripCount;
7474 continue;
7475 }
7476 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7477 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7478 ".omp.fuse.tc");
7479 }
7480
7481 // Generate new loop
7482 CanonicalLoopInfo *fused =
7483 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7484 lastLoop->getLatch(), "fused");
7485
7486 // Replace original loops with the fused loop
7487 // Preheader and After are not considered inside the CLI.
7488 // These are used to compute the individual TCs of the loops
7489 // so they have to be put before the resulting fused loop.
7490 // Moving them up for readability.
7491 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7492 Loops[i]->getPreheader()->moveBefore(TCBlock);
7493 Loops[i]->getAfter()->moveBefore(TCBlock);
7494 }
7495 lastLoop->getPreheader()->moveBefore(TCBlock);
7496
7497 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7498 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7499 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7500 }
7501 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7502 redirectTo(TCBlock, fused->getPreheader(), DL);
7503 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7504
7505 // Build the fused body
7506 // Create new Blocks with conditions that jump to the original loop bodies
7508 SmallVector<Value *> condValues;
7509 for (size_t i = 0; i < Loops.size(); ++i) {
7510 BasicBlock *condBlock = BasicBlock::Create(
7511 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7512 Builder.SetInsertPoint(condBlock);
7513 Value *condValue =
7514 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7515 condBBs.push_back(condBlock);
7516 condValues.push_back(condValue);
7517 }
7518 // Join the condition blocks with the bodies of the original loops
7519 redirectTo(fused->getBody(), condBBs[0], DL);
7520 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7521 Builder.SetInsertPoint(condBBs[i]);
7522 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7523 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7524 // Replace the IV with the fused IV
7525 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7526 }
7527 // Last body jumps to the created end body block
7528 Builder.SetInsertPoint(condBBs.back());
7529 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7530 fused->getLatch());
7531 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7532 // Replace the IV with the fused IV
7533 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7534
7535 // The loop latch must have only one predecessor. Currently it is branched to
7536 // from both the last condition block and the last loop body
7537 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7538 "omp.fused.pre_latch");
7539
7540 // Remove unused parts
7541 removeUnusedBlocksFromParent(oldControlBBs);
7542
7543 // Invalidate old CLIs
7544 for (CanonicalLoopInfo *L : Loops)
7545 L->invalidate();
7546
7547#ifndef NDEBUG
7548 fused->assertOK();
7549#endif
7550 return fused;
7551}
7552
7554 LLVMContext &Ctx = Builder.getContext();
7556 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7557 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7558}
7559
7561 LLVMContext &Ctx = Builder.getContext();
7563 Loop, {
7564 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7565 });
7566}
7567
7568void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7569 Value *IfCond, ValueToValueMapTy &VMap,
7570 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7571 const Twine &NamePrefix) {
7572 Function *F = CanonicalLoop->getFunction();
7573
7574 // We can't do
7575 // if (cond) {
7576 // simd_loop;
7577 // } else {
7578 // non_simd_loop;
7579 // }
7580 // because then the CanonicalLoopInfo would only point to one of the loops:
7581 // leading to other constructs operating on the same loop to malfunction.
7582 // Instead generate
7583 // while (...) {
7584 // if (cond) {
7585 // simd_body;
7586 // } else {
7587 // not_simd_body;
7588 // }
7589 // }
7590 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7591 // body at -O3
7592
7593 // Define where if branch should be inserted
7594 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7595
7596 // Create additional blocks for the if statement
7597 BasicBlock *Cond = SplitBeforeIt->getParent();
7598 llvm::LLVMContext &C = Cond->getContext();
7600 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7602 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7603
7604 // Create if condition branch.
7605 Builder.SetInsertPoint(SplitBeforeIt);
7606 Instruction *BrInstr =
7607 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7608 InsertPointTy IP(++BrInstr->getIterator());
7609 // Then block contains branch to omp loop body which needs to be vectorized
7610 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7611 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7612
7613 Builder.SetInsertPoint(ElseBlock);
7614
7615 // Clone loop for the else branch
7617
7618 SmallVector<BasicBlock *, 8> ExistingBlocks;
7619 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7620 ExistingBlocks.push_back(ThenBlock);
7621 ExistingBlocks.append(L->block_begin(), L->block_end());
7622 // Cond is the block that has the if clause condition
7623 // LoopCond is omp_loop.cond
7624 // LoopHeader is omp_loop.header
7625 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7626 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7627 assert(LoopCond && LoopHeader && "Invalid loop structure");
7628 for (BasicBlock *Block : ExistingBlocks) {
7629 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7630 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7631 continue;
7632 }
7633 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7634
7635 // fix name not to be omp.if.then
7636 if (Block == ThenBlock)
7637 NewBB->setName(NamePrefix + ".if.else");
7638
7639 NewBB->moveBefore(CanonicalLoop->getExit());
7640 VMap[Block] = NewBB;
7641 NewBlocks.push_back(NewBB);
7642 }
7643 remapInstructionsInBlocks(NewBlocks, VMap);
7644 Builder.CreateBr(NewBlocks.front());
7645
7646 // The loop latch must have only one predecessor. Currently it is branched to
7647 // from both the 'then' and 'else' branches.
7648 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7649 NamePrefix + ".pre_latch");
7650
7651 // Ensure that the then block is added to the loop so we add the attributes in
7652 // the next step
7653 L->addBasicBlockToLoop(ThenBlock, LI);
7654}
7655
7656unsigned
7658 const StringMap<bool> &Features) {
7659 if (TargetTriple.isX86()) {
7660 if (Features.lookup("avx512f"))
7661 return 512;
7662 else if (Features.lookup("avx"))
7663 return 256;
7664 return 128;
7665 }
7666 if (TargetTriple.isPPC())
7667 return 128;
7668 if (TargetTriple.isWasm())
7669 return 128;
7670 if (TargetTriple.isSystemZ())
7671 return 64;
7672 return 0;
7673}
7674
7676 MapVector<Value *, Value *> AlignedVars,
7677 Value *IfCond, OrderKind Order,
7678 ConstantInt *Simdlen, ConstantInt *Safelen) {
7679 LLVMContext &Ctx = Builder.getContext();
7680
7681 Function *F = CanonicalLoop->getFunction();
7682
7683 // Blocks must have terminators.
7684 // FIXME: Don't run analyses on incomplete/invalid IR.
7686 for (BasicBlock &BB : *F)
7687 if (!BB.hasTerminator())
7688 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7689
7690 // TODO: We should not rely on pass manager. Currently we use pass manager
7691 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7692 // object. We should have a method which returns all blocks between
7693 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7695 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7696 FAM.registerPass([]() { return LoopAnalysis(); });
7697 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7698
7699 LoopAnalysis LIA;
7700 LoopInfo &&LI = LIA.run(*F, FAM);
7701
7702 for (Instruction *I : UIs)
7703 I->eraseFromParent();
7704
7705 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7706 if (AlignedVars.size()) {
7707 InsertPointTy IP = Builder.saveIP();
7708 for (auto &AlignedItem : AlignedVars) {
7709 Value *AlignedPtr = AlignedItem.first;
7710 Value *Alignment = AlignedItem.second;
7711 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7712 Builder.SetInsertPoint(loadInst->getNextNode());
7713 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7714 Alignment);
7715 }
7716 Builder.restoreIP(IP);
7717 }
7718
7719 if (IfCond) {
7720 ValueToValueMapTy VMap;
7721 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7722 }
7723
7725
7726 // Get the basic blocks from the loop in which memref instructions
7727 // can be found.
7728 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7729 // preferably without running any passes.
7730 for (BasicBlock *Block : L->getBlocks()) {
7731 if (Block == CanonicalLoop->getCond() ||
7732 Block == CanonicalLoop->getHeader())
7733 continue;
7734 Reachable.insert(Block);
7735 }
7736
7737 SmallVector<Metadata *> LoopMDList;
7738
7739 // In presence of finite 'safelen', it may be unsafe to mark all
7740 // the memory instructions parallel, because loop-carried
7741 // dependences of 'safelen' iterations are possible.
7742 // If clause order(concurrent) is specified then the memory instructions
7743 // are marked parallel even if 'safelen' is finite.
7744 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7745 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7746
7747 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7748 // versions so we can't add the loop attributes in that case.
7749 if (IfCond) {
7750 // we can still add llvm.loop.parallel_access
7751 addLoopMetadata(CanonicalLoop, LoopMDList);
7752 return;
7753 }
7754
7755 // Use the above access group metadata to create loop level
7756 // metadata, which should be distinct for each loop.
7757 LoopMDList.push_back(
7758 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7759
7760 if (Simdlen || Safelen) {
7761 // If both simdlen and safelen clauses are specified, the value of the
7762 // simdlen parameter must be less than or equal to the value of the safelen
7763 // parameter. Therefore, use safelen only in the absence of simdlen.
7764 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7765 LoopMDList.push_back(
7766 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7767 ConstantAsMetadata::get(VectorizeWidth)}));
7768 }
7769
7770 addLoopMetadata(CanonicalLoop, LoopMDList);
7771}
7772
7773/// Create the TargetMachine object to query the backend for optimization
7774/// preferences.
7775///
7776/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7777/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7778/// needed for the LLVM pass pipline. We use some default options to avoid
7779/// having to pass too many settings from the frontend that probably do not
7780/// matter.
7781///
7782/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7783/// method. If we are going to use TargetMachine for more purposes, especially
7784/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7785/// might become be worth requiring front-ends to pass on their TargetMachine,
7786/// or at least cache it between methods. Note that while fontends such as Clang
7787/// have just a single main TargetMachine per translation unit, "target-cpu" and
7788/// "target-features" that determine the TargetMachine are per-function and can
7789/// be overrided using __attribute__((target("OPTIONS"))).
7790static std::unique_ptr<TargetMachine>
7792 Module *M = F->getParent();
7793
7794 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7795 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7796 const llvm::Triple &Triple = M->getTargetTriple();
7797
7798 std::string Error;
7800 if (!TheTarget)
7801 return {};
7802
7804 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7805 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7806 /*CodeModel=*/std::nullopt, OptLevel));
7807}
7808
7809/// Heuristically determine the best-performant unroll factor for \p CLI. This
7810/// depends on the target processor. We are re-using the same heuristics as the
7811/// LoopUnrollPass.
7813 Function *F = CLI->getFunction();
7814
7815 // Assume the user requests the most aggressive unrolling, even if the rest of
7816 // the code is optimized using a lower setting.
7818 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7819
7820 // Blocks must have terminators.
7821 // FIXME: Don't run analyses on incomplete/invalid IR.
7823 for (BasicBlock &BB : *F)
7824 if (!BB.hasTerminator())
7825 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7826
7828 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7829 FAM.registerPass([]() { return AssumptionAnalysis(); });
7830 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7831 FAM.registerPass([]() { return LoopAnalysis(); });
7832 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7833 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7834 TargetIRAnalysis TIRA;
7835 if (TM)
7836 TIRA = TargetIRAnalysis(
7837 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7838 FAM.registerPass([&]() { return TIRA; });
7839
7840 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7842 ScalarEvolution &&SE = SEA.run(*F, FAM);
7844 DominatorTree &&DT = DTA.run(*F, FAM);
7845 LoopAnalysis LIA;
7846 LoopInfo &&LI = LIA.run(*F, FAM);
7848 AssumptionCache &&AC = ACT.run(*F, FAM);
7850
7851 for (Instruction *I : UIs)
7852 I->eraseFromParent();
7853
7854 Loop *L = LI.getLoopFor(CLI->getHeader());
7855 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7856
7858 L, SE, TTI,
7859 /*BlockFrequencyInfo=*/nullptr,
7860 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7861 /*UserThreshold=*/std::nullopt,
7862 /*UserAllowPartial=*/true,
7863 /*UserAllowRuntime=*/true,
7864 /*UserUpperBound=*/std::nullopt,
7865 /*UserFullUnrollMaxCount=*/std::nullopt);
7866
7867 UP.Force = true;
7868
7869 // Account for additional optimizations taking place before the LoopUnrollPass
7870 // would unroll the loop.
7873
7874 // Use normal unroll factors even if the rest of the code is optimized for
7875 // size.
7878
7879 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7880 << " Threshold=" << UP.Threshold << "\n"
7881 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7882 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7883 << " PartialOptSizeThreshold="
7884 << UP.PartialOptSizeThreshold << "\n");
7885
7886 // Disable peeling.
7889 /*UserAllowPeeling=*/false,
7890 /*UserAllowProfileBasedPeeling=*/false,
7891 /*UnrollingSpecficValues=*/false);
7892
7894 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7895
7896 // Assume that reads and writes to stack variables can be eliminated by
7897 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7898 // size.
7899 for (BasicBlock *BB : L->blocks()) {
7900 for (Instruction &I : *BB) {
7901 Value *Ptr;
7902 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7903 Ptr = Load->getPointerOperand();
7904 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7905 Ptr = Store->getPointerOperand();
7906 } else
7907 continue;
7908
7909 Ptr = Ptr->stripPointerCasts();
7910
7911 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7912 if (Alloca->getParent() == &F->getEntryBlock())
7913 EphValues.insert(&I);
7914 }
7915 }
7916 }
7917
7918 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7919
7920 // Loop is not unrollable if the loop contains certain instructions.
7921 if (!UCE.canUnroll()) {
7922 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7923 return 1;
7924 }
7925
7926 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7927 << "\n");
7928
7929 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7930 // be able to use it.
7931 int TripCount = 0;
7932 int MaxTripCount = 0;
7933 bool MaxOrZero = false;
7934 unsigned TripMultiple = 0;
7935
7936 unsigned Factor =
7937 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7938 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7939 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7940
7941 // This function returns 1 to signal to not unroll a loop.
7942 if (Factor == 0)
7943 return 1;
7944 return Factor;
7945}
7946
7948 int32_t Factor,
7949 CanonicalLoopInfo **UnrolledCLI) {
7950 assert(Factor >= 0 && "Unroll factor must not be negative");
7951
7952 Function *F = Loop->getFunction();
7953 LLVMContext &Ctx = F->getContext();
7954
7955 // If the unrolled loop is not used for another loop-associated directive, it
7956 // is sufficient to add metadata for the LoopUnrollPass.
7957 if (!UnrolledCLI) {
7958 SmallVector<Metadata *, 2> LoopMetadata;
7959 LoopMetadata.push_back(
7960 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7961
7962 if (Factor >= 1) {
7964 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7965 LoopMetadata.push_back(MDNode::get(
7966 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7967 }
7968
7969 addLoopMetadata(Loop, LoopMetadata);
7970 return;
7971 }
7972
7973 // Heuristically determine the unroll factor.
7974 if (Factor == 0)
7976
7977 // No change required with unroll factor 1.
7978 if (Factor == 1) {
7979 *UnrolledCLI = Loop;
7980 return;
7981 }
7982
7983 assert(Factor >= 2 &&
7984 "unrolling only makes sense with a factor of 2 or larger");
7985
7986 Type *IndVarTy = Loop->getIndVarType();
7987
7988 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7989 // unroll the inner loop.
7990 Value *FactorVal =
7991 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7992 /*isSigned=*/false));
7993 std::vector<CanonicalLoopInfo *> LoopNest =
7994 tileLoops(DL, {Loop}, {FactorVal});
7995 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7996 *UnrolledCLI = LoopNest[0];
7997 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7998
7999 // LoopUnrollPass can only fully unroll loops with constant trip count.
8000 // Unroll by the unroll factor with a fallback epilog for the remainder
8001 // iterations if necessary.
8003 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
8005 InnerLoop,
8006 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
8008 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
8009
8010#ifndef NDEBUG
8011 (*UnrolledCLI)->assertOK();
8012#endif
8013}
8014
8017 llvm::Value *BufSize, llvm::Value *CpyBuf,
8018 llvm::Value *CpyFn, llvm::Value *DidIt) {
8019 if (!updateToLocation(Loc))
8020 return Loc.IP;
8021
8022 uint32_t SrcLocStrSize;
8023 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8024 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8025 Value *ThreadId = getOrCreateThreadID(Ident);
8026
8027 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
8028
8029 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
8030
8031 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
8032 createRuntimeFunctionCall(Fn, Args);
8033
8034 return Builder.saveIP();
8035}
8036
8038 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8039 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
8041
8042 if (!updateToLocation(Loc))
8043 return Loc.IP;
8044
8045 // If needed allocate and initialize `DidIt` with 0.
8046 // DidIt: flag variable: 1=single thread; 0=not single thread.
8047 llvm::Value *DidIt = nullptr;
8048 if (!CPVars.empty()) {
8049 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
8050 Builder.CreateStore(Builder.getInt32(0), DidIt);
8051 }
8052
8053 Directive OMPD = Directive::OMPD_single;
8054 uint32_t SrcLocStrSize;
8055 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8056 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8057 Value *ThreadId = getOrCreateThreadID(Ident);
8058 Value *Args[] = {Ident, ThreadId};
8059
8060 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8061 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8062
8063 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8064 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8065
8066 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8067 if (Error Err = FiniCB(IP))
8068 return Err;
8069
8070 // The thread that executes the single region must set `DidIt` to 1.
8071 // This is used by __kmpc_copyprivate, to know if the caller is the
8072 // single thread or not.
8073 if (DidIt)
8074 Builder.CreateStore(Builder.getInt32(1), DidIt);
8075
8076 return Error::success();
8077 };
8078
8079 // generates the following:
8080 // if (__kmpc_single()) {
8081 // .... single region ...
8082 // __kmpc_end_single
8083 // }
8084 // __kmpc_copyprivate
8085 // __kmpc_barrier
8086
8087 InsertPointOrErrorTy AfterIP =
8088 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8089 /*Conditional*/ true,
8090 /*hasFinalize*/ true);
8091 if (!AfterIP)
8092 return AfterIP.takeError();
8093
8094 if (DidIt) {
8095 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8096 // NOTE BufSize is currently unused, so just pass 0.
8098 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8099 CPFuncs[I], DidIt);
8100 // NOTE __kmpc_copyprivate already inserts a barrier
8101 } else if (!IsNowait) {
8102 InsertPointOrErrorTy AfterIP =
8104 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8105 /* CheckCancelFlag */ false);
8106 if (!AfterIP)
8107 return AfterIP.takeError();
8108 }
8109 return Builder.saveIP();
8110}
8111
8114 BodyGenCallbackTy BodyGenCB,
8115 FinalizeCallbackTy FiniCB, bool IsNowait) {
8116
8117 if (!updateToLocation(Loc))
8118 return Loc.IP;
8119
8120 // All threads execute the scope body — no conditional entry.
8121 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8122 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8123 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8124 /*IsCancellable=*/false);
8125 if (!AfterIP)
8126 return AfterIP.takeError();
8127
8128 Builder.restoreIP(*AfterIP);
8129 if (!IsNowait) {
8130 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8131 omp::Directive::OMPD_unknown,
8132 /*ForceSimpleCall=*/false,
8133 /*CheckCancelFlag=*/false);
8134 if (!AfterIP)
8135 return AfterIP.takeError();
8136 }
8137 return Builder.saveIP();
8138}
8139
8141 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8142 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8143
8144 if (!updateToLocation(Loc))
8145 return Loc.IP;
8146
8147 Directive OMPD = Directive::OMPD_critical;
8148 uint32_t SrcLocStrSize;
8149 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8150 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8151 Value *ThreadId = getOrCreateThreadID(Ident);
8152 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8153 Value *Args[] = {Ident, ThreadId, LockVar};
8154
8155 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8156 Function *RTFn = nullptr;
8157 if (HintInst) {
8158 // Add Hint to entry Args and create call
8159 EnterArgs.push_back(HintInst);
8160 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8161 } else {
8162 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8163 }
8164 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8165
8166 Function *ExitRTLFn =
8167 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8168 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8169
8170 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8171 /*Conditional*/ false, /*hasFinalize*/ true);
8172}
8173
8176 InsertPointTy AllocaIP, unsigned NumLoops,
8177 ArrayRef<llvm::Value *> StoreValues,
8178 const Twine &Name, bool IsDependSource) {
8179 assert(
8180 llvm::all_of(StoreValues,
8181 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8182 "OpenMP runtime requires depend vec with i64 type");
8183
8184 if (!updateToLocation(Loc))
8185 return Loc.IP;
8186
8187 // Allocate space for vector and generate alloc instruction.
8188 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8189 Builder.restoreIP(AllocaIP);
8190 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8191 ArgsBase->setAlignment(Align(8));
8193
8194 // Store the index value with offset in depend vector.
8195 for (unsigned I = 0; I < NumLoops; ++I) {
8196 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8197 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8198 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8199 STInst->setAlignment(Align(8));
8200 }
8201
8202 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8203 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8204
8205 uint32_t SrcLocStrSize;
8206 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8207 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8208 Value *ThreadId = getOrCreateThreadID(Ident);
8209 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8210
8211 Function *RTLFn = nullptr;
8212 if (IsDependSource)
8213 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8214 else
8215 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8216 createRuntimeFunctionCall(RTLFn, Args);
8217
8218 return Builder.saveIP();
8219}
8220
8222 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8223 FinalizeCallbackTy FiniCB, bool IsThreads) {
8224 if (!updateToLocation(Loc))
8225 return Loc.IP;
8226
8227 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8228 Instruction *EntryCall = nullptr;
8229 Instruction *ExitCall = nullptr;
8230
8231 if (IsThreads) {
8232 uint32_t SrcLocStrSize;
8233 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8234 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8235 Value *ThreadId = getOrCreateThreadID(Ident);
8236 Value *Args[] = {Ident, ThreadId};
8237
8238 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8239 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8240
8241 Function *ExitRTLFn =
8242 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8243 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8244 }
8245
8246 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8247 /*Conditional*/ false, /*hasFinalize*/ true);
8248}
8249
8250OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8251 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8252 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8253 bool HasFinalize, bool IsCancellable) {
8254
8255 if (HasFinalize)
8256 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8257
8258 // Create inlined region's entry and body blocks, in preparation
8259 // for conditional creation
8260 BasicBlock *EntryBB = Builder.GetInsertBlock();
8261 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8263 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8264 SplitPos->setDebugLoc(Builder.getCurrentDebugLocation());
8265 }
8266 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8267 BasicBlock *FiniBB =
8268 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8269
8270 Builder.SetInsertPoint(EntryBB->getTerminator());
8271 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8272
8273 // generate body
8274 if (Error Err =
8275 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8276 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8277 return Err;
8278
8279 // emit exit call and do any needed finalization.
8280 auto FinIP = FiniBB->getFirstInsertionPt();
8281 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8282 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8283 "Unexpected control flow graph state!!");
8284 InsertPointOrErrorTy AfterIP =
8285 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8286 if (!AfterIP)
8287 return AfterIP.takeError();
8288
8289 // If we are skipping the region of a non conditional, remove the exit
8290 // block, and clear the builder's insertion point.
8291 assert(SplitPos->getParent() == ExitBB &&
8292 "Unexpected Insertion point location!");
8293 auto merged = MergeBlockIntoPredecessor(ExitBB);
8294 BasicBlock *ExitPredBB = SplitPos->getParent();
8295 auto InsertBB = merged ? ExitPredBB : ExitBB;
8297 SplitPos->eraseFromParent();
8298 Builder.SetInsertPoint(InsertBB);
8299
8300 return Builder.saveIP();
8301}
8302
8303OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8304 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8305 // if nothing to do, Return current insertion point.
8306 if (!Conditional || !EntryCall)
8307 return Builder.saveIP();
8308
8309 BasicBlock *EntryBB = Builder.GetInsertBlock();
8310 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8311 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8312 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8313
8314 // Emit thenBB and set the Builder's insertion point there for
8315 // body generation next. Place the block after the current block.
8316 Function *CurFn = EntryBB->getParent();
8317 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8318
8319 // Move Entry branch to end of ThenBB, and replace with conditional
8320 // branch (If-stmt)
8321 Instruction *EntryBBTI = EntryBB->getTerminator();
8322 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8323 EntryBBTI->removeFromParent();
8324 Builder.SetInsertPoint(UI);
8325 Builder.Insert(EntryBBTI);
8326 UI->eraseFromParent();
8327 Builder.SetInsertPoint(ThenBB->getTerminator());
8328
8329 // return an insertion point to ExitBB.
8330 return ExitBB->getFirstInsertionPt();
8331}
8332
8333OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8334 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8335 bool HasFinalize) {
8336
8337 Builder.restoreIP(FinIP);
8338
8339 // If there is finalization to do, emit it before the exit call
8340 if (HasFinalize) {
8341 assert(!FinalizationStack.empty() &&
8342 "Unexpected finalization stack state!");
8343
8344 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8345 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8346
8347 BasicBlock *FinBB = FinIP.getNodeParent();
8348 if (Error Err = Fi.mergeFiniBB(Builder, FinBB))
8349 return std::move(Err);
8350
8351 // Exit condition: insertion point is before the terminator of the new Fini
8352 // block
8353 Builder.SetInsertPoint(FinBB->getTerminator());
8354 }
8355
8356 if (!ExitCall)
8357 return Builder.saveIP();
8358
8359 // place the Exitcall as last instruction before Finalization block terminator
8360 ExitCall->removeFromParent();
8361 Builder.Insert(ExitCall);
8362
8363 return ExitCall->getIterator();
8364}
8365
8367 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8368 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8369 if (!IP.isValid())
8370 return IP;
8371
8373
8374 // creates the following CFG structure
8375 // OMP_Entry : (MasterAddr != PrivateAddr)?
8376 // F T
8377 // | \
8378 // | copin.not.master
8379 // | /
8380 // v /
8381 // copyin.not.master.end
8382 // |
8383 // v
8384 // OMP.Entry.Next
8385
8386 BasicBlock *OMP_Entry = IP.getNodeParent();
8387 Function *CurFn = OMP_Entry->getParent();
8388 BasicBlock *CopyBegin =
8389 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8390 BasicBlock *CopyEnd = nullptr;
8391
8392 // If entry block is terminated, split to preserve the branch to following
8393 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8395 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8396 "copyin.not.master.end");
8397 OMP_Entry->getTerminator()->eraseFromParent();
8398 } else {
8399 CopyEnd =
8400 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8401 }
8402
8403 Builder.SetInsertPoint(OMP_Entry);
8404 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8405 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8406 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8407 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8408
8409 Builder.SetInsertPoint(CopyBegin);
8410 if (BranchtoEnd)
8411 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8412
8413 return Builder.saveIP();
8414}
8415
8417 Value *Size, Value *Allocator,
8418 std::string Name) {
8420 if (!updateToLocation(Loc))
8421 return nullptr;
8422
8423 uint32_t SrcLocStrSize;
8424 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8425 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8426 Value *ThreadId = getOrCreateThreadID(Ident);
8427 Value *Args[] = {ThreadId, Size, Allocator};
8428
8429 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8430
8431 return createRuntimeFunctionCall(Fn, Args, Name);
8432}
8433
8435 Value *Align, Value *Size,
8436 Value *Allocator,
8437 std::string Name) {
8439 if (!updateToLocation(Loc))
8440 return nullptr;
8441
8442 uint32_t SrcLocStrSize;
8443 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8444 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8445 Value *ThreadId = getOrCreateThreadID(Ident);
8446 Value *Args[] = {ThreadId, Align, Size, Allocator};
8447
8448 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8449
8450 return Builder.CreateCall(Fn, Args, Name);
8451}
8452
8454 Value *Addr, Value *Allocator,
8455 std::string Name) {
8457 if (!updateToLocation(Loc))
8458 return nullptr;
8459
8460 uint32_t SrcLocStrSize;
8461 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8462 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8463 Value *ThreadId = getOrCreateThreadID(Ident);
8464 Value *Args[] = {ThreadId, Addr, Allocator};
8465 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8466 return createRuntimeFunctionCall(Fn, Args, Name);
8467}
8468
8470 Value *Size,
8471 const Twine &Name) {
8474
8475 Value *Args[] = {Size};
8476 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8477 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8479 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8480 return Call;
8481}
8482
8484 Type *VarType,
8485 const Twine &Name) {
8486 return createOMPAllocShared(
8487 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8488}
8489
8491 Value *Addr, Value *Size,
8492 const Twine &Name) {
8495
8496 Value *Args[] = {Addr, Size};
8497 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8498 return Builder.CreateCall(Fn, Args, Name);
8499}
8500
8502 Value *Addr, Type *VarType,
8503 const Twine &Name) {
8504 return createOMPFreeShared(
8505 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8506 Name);
8507}
8508
8510 const LocationDescription &Loc, Value *InteropVar,
8512 Value *DependenceAddress, bool HaveNowaitClause) {
8515
8516 uint32_t SrcLocStrSize;
8517 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8518 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8519 Value *ThreadId = getOrCreateThreadID(Ident);
8520 if (Device == nullptr)
8522 else if (Device->getType() != Int32)
8523 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8524 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8525 if (NumDependences == nullptr) {
8526 NumDependences = ConstantInt::get(Int32, 0);
8527 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8528 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8529 }
8530 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8531 Value *Args[] = {
8532 Ident, ThreadId, InteropVar, InteropTypeVal,
8533 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8534
8535 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8536
8537 return createRuntimeFunctionCall(Fn, Args);
8538}
8539
8541 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8542 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8545
8546 uint32_t SrcLocStrSize;
8547 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8548 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8549 Value *ThreadId = getOrCreateThreadID(Ident);
8550 if (Device == nullptr)
8552 else if (Device->getType() != Int32)
8553 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8554 if (NumDependences == nullptr) {
8555 NumDependences = ConstantInt::get(Int32, 0);
8556 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8557 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8558 }
8559 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8560 Value *Args[] = {
8561 Ident, ThreadId, InteropVar, Device,
8562 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8563
8564 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8565
8566 return createRuntimeFunctionCall(Fn, Args);
8567}
8568
8570 Value *InteropVar, Value *Device,
8571 Value *NumDependences,
8572 Value *DependenceAddress,
8573 bool HaveNowaitClause) {
8576 uint32_t SrcLocStrSize;
8577 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8578 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8579 Value *ThreadId = getOrCreateThreadID(Ident);
8580 if (Device == nullptr)
8582 else if (Device->getType() != Int32)
8583 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8584 if (NumDependences == nullptr) {
8585 NumDependences = ConstantInt::get(Int32, 0);
8586 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8587 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8588 }
8589 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8590 Value *Args[] = {
8591 Ident, ThreadId, InteropVar, Device,
8592 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8593
8594 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8595
8596 return createRuntimeFunctionCall(Fn, Args);
8597}
8598
8601 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8604
8605 uint32_t SrcLocStrSize;
8606 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8607 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8608 Value *ThreadId = getOrCreateThreadID(Ident);
8609 Constant *ThreadPrivateCache =
8610 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8611 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8612
8613 Function *Fn =
8614 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8615
8616 return createRuntimeFunctionCall(Fn, Args);
8617}
8618
8620 const LocationDescription &Loc,
8622 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8623 "expected num_threads and num_teams to be specified");
8624
8625 if (!updateToLocation(Loc))
8626 return nullptr;
8627
8628 uint32_t SrcLocStrSize;
8629 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8630 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8631 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8632 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8633 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8634 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8635 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8636 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8637
8638 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8639 Function *Kernel = DebugKernelWrapper;
8640
8641 // We need to strip the debug prefix to get the correct kernel name.
8642 StringRef KernelName = Kernel->getName();
8643 const std::string DebugPrefix = "_debug__";
8644 if (KernelName.ends_with(DebugPrefix)) {
8645 KernelName = KernelName.drop_back(DebugPrefix.length());
8646 Kernel = M.getFunction(KernelName);
8647 assert(Kernel && "Expected the real kernel to exist");
8648 }
8649
8650 // Manifest the launch configuration in the metadata matching the kernel
8651 // environment.
8652 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8653 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8654 Attrs.MaxTeams.front());
8655
8656 // Don't derive or write thread bounds for Bare kernels.
8657 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8658 if (Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_BARE) {
8659 // If MaxThreads is not set and needs adjustment, select the maximum
8660 // between the default workgroup size and the MinThreads value. This is
8661 // only meaningful for targets with a known grid value (i.e. GPUs); for
8662 // other targets (e.g. host kernels) leave it unset so the runtime falls
8663 // back to its own device-specific default.
8664 if (MaxThreadsVal < 0 && UseDefaultMaxThreads && hasGridValue(T))
8665 MaxThreadsVal =
8666 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8667 Attrs.MinThreads.front());
8668
8669 // Generic mode runs the main thread on a warp of its own, past
8670 // thread_limit. Reserve the widest warp any target has. Not on SPIR-V,
8671 // causes problems with Level Zero.
8672 if (MaxThreadsVal > 0 &&
8673 Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC && hasGridValue(T) &&
8674 !T.isSPIRV())
8675 MaxThreadsVal = int32_t(
8676 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8677 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8678
8679 if (MaxThreadsVal > 0)
8680 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8681 MaxThreadsVal);
8682 }
8683
8684 Constant *MinThreads =
8685 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8686 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8687 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8688 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8689 Constant *ReductionDataSize =
8690 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8691
8692 const DataLayout &DL = M.getDataLayout();
8693
8694 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8695 Constant *DynamicEnvironmentInitializer =
8696 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8697 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8698 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8699 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8700 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8701 DL.getDefaultGlobalsAddressSpace());
8702 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8703
8704 Constant *DynamicEnvironment =
8705 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8706 ? DynamicEnvironmentGV
8707 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8708 DynamicEnvironmentPtr);
8709
8710 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8711 ConfigurationEnvironment, {
8712 UseGenericStateMachineVal,
8713 MayUseNestedParallelismVal,
8714 IsSPMDVal,
8715 MinThreads,
8716 MaxThreads,
8717 MinTeams,
8718 MaxTeams,
8719 ReductionDataSize,
8720 });
8721 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8722 KernelEnvironment, {
8723 ConfigurationEnvironmentInitializer,
8724 Ident,
8725 DynamicEnvironment,
8726 });
8727 std::string KernelEnvironmentName =
8728 (KernelName + "_kernel_environment").str();
8729 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8730 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8731 KernelEnvironmentInitializer, KernelEnvironmentName,
8732 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8733 DL.getDefaultGlobalsAddressSpace());
8734 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8735
8736 return KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8737 ? KernelEnvironmentGV
8738 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8739 KernelEnvironmentPtr);
8740}
8741
8743 const LocationDescription &Loc,
8745 Constant *KernelEnvironment = emitKernelEnvironment(Loc, Attrs);
8746 if (!KernelEnvironment)
8747 return Loc.IP;
8748
8749 if (!updateToLocation(Loc))
8750 return Loc.IP;
8751
8752 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8754 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8755
8756 Value *KernelLaunchEnvironment =
8757 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8758 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8759 KernelLaunchEnvironment =
8760 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8761 ? KernelLaunchEnvironment
8762 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8763 KernelLaunchEnvParamTy);
8764 CallInst *ThreadKind = createRuntimeFunctionCall(
8765 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8766
8767 Value *ExecUserCode = Builder.CreateICmpEQ(
8768 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8769 "exec_user_code");
8770
8771 // ThreadKind = __kmpc_target_init(...)
8772 // if (ThreadKind == -1)
8773 // user_code
8774 // else
8775 // return;
8776
8777 auto *UI = Builder.CreateUnreachable();
8778 BasicBlock *CheckBB = UI->getParent();
8779 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8780
8781 BasicBlock *WorkerExitBB = BasicBlock::Create(
8782 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8783 Builder.SetInsertPoint(WorkerExitBB);
8784 Builder.CreateRetVoid();
8785
8786 auto *CheckBBTI = CheckBB->getTerminator();
8787 Builder.SetInsertPoint(CheckBBTI);
8788 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8789
8790 CheckBBTI->eraseFromParent();
8791 UI->eraseFromParent();
8792
8793 // Continue in the "user_code" block, see diagram above and in
8794 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8795 return UserCodeEntryBB->getFirstInsertionPt();
8796}
8797
8799 int32_t TeamsReductionDataSize) {
8800 if (!updateToLocation(Loc))
8801 return;
8802
8804 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8805
8807
8808 if (!TeamsReductionDataSize)
8809 return;
8810
8811 Function *Kernel = Builder.GetInsertBlock()->getParent();
8812 // We need to strip the debug prefix to get the correct kernel name.
8813 StringRef KernelName = Kernel->getName();
8814 const std::string DebugPrefix = "_debug__";
8815 if (KernelName.ends_with(DebugPrefix))
8816 KernelName = KernelName.drop_back(DebugPrefix.length());
8817 auto *KernelEnvironmentGV =
8818 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8819 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8820 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8821 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8822 KernelEnvironmentInitializer,
8823 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8824 KernelEnvironmentGV->setInitializer(NewInitializer);
8825}
8826
8827static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8828 bool Min) {
8829 if (Kernel.hasFnAttribute(Name)) {
8830 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8831 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8832 }
8833 Kernel.addFnAttr(Name, llvm::utostr(Value));
8834}
8835
8836std::pair<int32_t, int32_t>
8838 int32_t ThreadLimit =
8839 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8840
8841 if (T.isAMDGPU()) {
8842 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8843 if (!Attr.isValid() || !Attr.isStringAttribute())
8844 return {0, ThreadLimit};
8845 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8846 int32_t LB, UB;
8847 if (!llvm::to_integer(UBStr, UB, 10))
8848 return {0, ThreadLimit};
8849 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8850 if (!llvm::to_integer(LBStr, LB, 10))
8851 return {0, UB};
8852 return {LB, UB};
8853 }
8854
8855 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8856 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8857 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8858 }
8859 return {0, ThreadLimit};
8860}
8861
8863 Function &Kernel, int32_t LB,
8864 int32_t UB) {
8865 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8866
8867 if (T.isAMDGPU()) {
8868 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8869 llvm::utostr(LB) + "," + llvm::utostr(UB));
8870 return;
8871 }
8872
8874}
8875
8876std::pair<int32_t, int32_t>
8878 // TODO: Read from backend annotations if available.
8879 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8880}
8881
8883 int32_t LB, int32_t UB) {
8884 if (UB > 0) {
8885 if (T.isNVPTX())
8887 if (T.isAMDGPU())
8888 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8889 }
8890
8891 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8892}
8893
8894void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8895 Function *OutlinedFn) {
8896 if (Config.isTargetDevice()) {
8898 // TODO: Determine if DSO local can be set to true.
8899 OutlinedFn->setDSOLocal(false);
8901 if (T.isAMDGCN())
8903 else if (T.isNVPTX())
8905 else if (T.isSPIRV())
8907 }
8908}
8909
8910Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8911 StringRef EntryFnIDName) {
8912 if (Config.isTargetDevice()) {
8913 assert(OutlinedFn && "The outlined function must exist if embedded");
8914 return OutlinedFn;
8915 }
8916
8917 return new GlobalVariable(
8918 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8919 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8920}
8921
8922Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8923 StringRef EntryFnName) {
8924 if (OutlinedFn)
8925 return OutlinedFn;
8926
8927 assert(!M.getGlobalVariable(EntryFnName, true) &&
8928 "Named kernel already exists?");
8929 return new GlobalVariable(
8930 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8931 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8932}
8933
8935 TargetRegionEntryInfo &EntryInfo,
8936 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8937 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8938
8939 SmallString<64> EntryFnName;
8940 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8941
8942 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8943 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8944 if (!CBResult)
8945 return CBResult.takeError();
8946 OutlinedFn = *CBResult;
8947 } else {
8948 OutlinedFn = nullptr;
8949 }
8950
8951 // If this target outline function is not an offload entry, we don't need to
8952 // register it. This may be in the case of a false if clause, or if there are
8953 // no OpenMP targets.
8954 if (!IsOffloadEntry)
8955 return Error::success();
8956
8957 std::string EntryFnIDName =
8958 Config.isTargetDevice()
8959 ? std::string(EntryFnName)
8960 : createPlatformSpecificName({EntryFnName, "region_id"});
8961
8962 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8963 EntryFnName, EntryFnIDName);
8964 return Error::success();
8965}
8966
8968 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8969 StringRef EntryFnName, StringRef EntryFnIDName) {
8970 if (OutlinedFn)
8971 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8972 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8973 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8974 OffloadInfoManager.registerTargetRegionEntryInfo(
8975 EntryInfo, EntryAddr, OutlinedFnID,
8977 return OutlinedFnID;
8978}
8979
8981 const LocationDescription &Loc, InsertPointTy AllocaIP,
8982 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8983 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8984 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8985 omp::RuntimeFunction *MapperFunc,
8987 BodyGenTy BodyGenType)>
8988 BodyGenCB,
8989 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8990 if (!updateToLocation(Loc))
8991 return InsertPointTy();
8992
8993 Builder.restoreIP(CodeGenIP);
8994
8995 bool IsStandAlone = !BodyGenCB;
8996 MapInfosTy *MapInfo;
8997 // Generate the code for the opening of the data environment. Capture all the
8998 // arguments of the runtime call by reference because they are used in the
8999 // closing of the region.
9000 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9001 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9002 MapInfo = &GenMapInfoCB(Builder.saveIP());
9003 if (Error Err = emitOffloadingArrays(
9004 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
9005 /*IsNonContiguous=*/true, DeviceAddrCB))
9006 return Err;
9007
9008 TargetDataRTArgs RTArgs;
9010
9011 // Emit the number of elements in the offloading arrays.
9012 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9013
9014 // Source location for the ident struct
9015 if (!SrcLocInfo) {
9016 uint32_t SrcLocStrSize;
9017 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9018 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9019 }
9020
9021 SmallVector<llvm::Value *, 13> OffloadingArgs = {
9022 SrcLocInfo, DeviceID,
9023 PointerNum, RTArgs.BasePointersArray,
9024 RTArgs.PointersArray, RTArgs.SizesArray,
9025 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9026 RTArgs.MappersArray};
9027
9028 if (IsStandAlone) {
9029 assert(MapperFunc && "MapperFunc missing for standalone target data");
9030
9031 auto TaskBodyCB = [&](Value *, Value *,
9033 if (Info.HasNoWait) {
9034 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
9038 }
9039
9041 OffloadingArgs);
9042
9043 if (Info.HasNoWait) {
9044 BasicBlock *OffloadContBlock =
9045 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
9046 Function *CurFn = Builder.GetInsertBlock()->getParent();
9047 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
9048 Builder.restoreIP(Builder.saveIP());
9049 }
9050 return Error::success();
9051 };
9052
9053 bool RequiresOuterTargetTask = Info.HasNoWait;
9054 if (!RequiresOuterTargetTask)
9055 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
9056 /*TargetTaskAllocaIP=*/{}));
9057 else
9058 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
9059 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
9060 } else {
9061 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
9062 omp::OMPRTL___tgt_target_data_begin_mapper);
9063
9064 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
9065
9066 for (auto DeviceMap : Info.DevicePtrInfoMap) {
9067 if (isa<AllocaInst>(DeviceMap.second.second)) {
9068 auto *LI =
9069 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
9070 Builder.CreateStore(LI, DeviceMap.second.second);
9071 }
9072 }
9073
9074 // If device pointer privatization is required, emit the body of the
9075 // region here. It will have to be duplicated: with and without
9076 // privatization.
9077 InsertPointOrErrorTy AfterIP =
9078 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9079 if (!AfterIP)
9080 return AfterIP.takeError();
9081 Builder.restoreIP(*AfterIP);
9082 }
9083 return Error::success();
9084 };
9085
9086 // If we need device pointer privatization, we need to emit the body of the
9087 // region with no privatization in the 'else' branch of the conditional.
9088 // Otherwise, we don't have to do anything.
9089 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9090 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9091 InsertPointOrErrorTy AfterIP =
9092 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9093 if (!AfterIP)
9094 return AfterIP.takeError();
9095 Builder.restoreIP(*AfterIP);
9096 return Error::success();
9097 };
9098
9099 // Generate code for the closing of the data region.
9100 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9101 ArrayRef<BasicBlock *> DeallocBlocks) {
9102 TargetDataRTArgs RTArgs;
9103 Info.EmitDebug = !MapInfo->Names.empty();
9104 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9105
9106 // Emit the number of elements in the offloading arrays.
9107 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9108
9109 // Source location for the ident struct
9110 if (!SrcLocInfo) {
9111 uint32_t SrcLocStrSize;
9112 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9113 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9114 }
9115
9116 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9117 PointerNum, RTArgs.BasePointersArray,
9118 RTArgs.PointersArray, RTArgs.SizesArray,
9119 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9120 RTArgs.MappersArray};
9121 Function *EndMapperFunc =
9122 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9123
9124 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9125 return Error::success();
9126 };
9127
9128 // We don't have to do anything to close the region if the if clause evaluates
9129 // to false.
9130 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9131 ArrayRef<BasicBlock *> DeallocBlocks) {
9132 return Error::success();
9133 };
9134
9135 Error Err = [&]() -> Error {
9136 if (BodyGenCB) {
9137 Error Err = [&]() {
9138 if (IfCond)
9139 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9140 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9141 }();
9142
9143 if (Err)
9144 return Err;
9145
9146 // If we don't require privatization of device pointers, we emit the body
9147 // in between the runtime calls. This avoids duplicating the body code.
9148 InsertPointOrErrorTy AfterIP =
9149 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9150 if (!AfterIP)
9151 return AfterIP.takeError();
9152 restoreIPandDebugLoc(Builder, *AfterIP);
9153
9154 if (IfCond)
9155 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9156 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9157 }
9158 if (IfCond)
9159 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9160 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9161 }();
9162
9163 if (Err)
9164 return Err;
9165
9166 return Builder.saveIP();
9167}
9168
9171 bool IsGPUDistribute) {
9172 assert((IVSize == 32 || IVSize == 64) &&
9173 "IV size is not compatible with the omp runtime");
9174 RuntimeFunction Name;
9175 if (IsGPUDistribute)
9176 Name = IVSize == 32
9177 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9178 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9179 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9180 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9181 else
9182 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9183 : omp::OMPRTL___kmpc_for_static_init_4u)
9184 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9185 : omp::OMPRTL___kmpc_for_static_init_8u);
9186
9187 return getOrCreateRuntimeFunction(M, Name);
9188}
9189
9191 bool IVSigned) {
9192 assert((IVSize == 32 || IVSize == 64) &&
9193 "IV size is not compatible with the omp runtime");
9194 RuntimeFunction Name = IVSize == 32
9195 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9196 : omp::OMPRTL___kmpc_dispatch_init_4u)
9197 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9198 : omp::OMPRTL___kmpc_dispatch_init_8u);
9199
9200 return getOrCreateRuntimeFunction(M, Name);
9201}
9202
9204 bool IVSigned) {
9205 assert((IVSize == 32 || IVSize == 64) &&
9206 "IV size is not compatible with the omp runtime");
9207 RuntimeFunction Name = IVSize == 32
9208 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9209 : omp::OMPRTL___kmpc_dispatch_next_4u)
9210 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9211 : omp::OMPRTL___kmpc_dispatch_next_8u);
9212
9213 return getOrCreateRuntimeFunction(M, Name);
9214}
9215
9217 bool IVSigned) {
9218 assert((IVSize == 32 || IVSize == 64) &&
9219 "IV size is not compatible with the omp runtime");
9220 RuntimeFunction Name = IVSize == 32
9221 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9222 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9223 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9224 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9225
9226 return getOrCreateRuntimeFunction(M, Name);
9227}
9228
9230 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9231}
9232
9234 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9235 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9236
9237 DISubprogram *NewSP = Func->getSubprogram();
9238 if (!NewSP)
9239 return;
9240
9242
9243 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9244 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9245 // Only use cached variable if the arg number matches. This is important
9246 // so that DIVariable created for privatized variables are not discarded.
9247 if (NewVar && (arg == NewVar->getArg()))
9248 return NewVar;
9249
9251 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9252 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9253 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9254 return NewVar;
9255 };
9256
9257 auto UpdateDebugRecord = [&](auto *DR) {
9258 DILocalVariable *OldVar = DR->getVariable();
9259 unsigned ArgNo = 0;
9260 for (auto Loc : DR->location_ops()) {
9261 auto Iter = ValueReplacementMap.find(Loc);
9262 if (Iter != ValueReplacementMap.end()) {
9263 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9264 ArgNo = std::get<1>(Iter->second) + 1;
9265 }
9266 }
9267 if (ArgNo != 0)
9268 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9269 };
9270
9272 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9273 if (DVR->getNumVariableLocationOps() != 1u) {
9274 DVR->setKillLocation();
9275 return;
9276 }
9277 Value *Loc = DVR->getVariableLocationOp(0u);
9278 BasicBlock *CurBB = DVR->getParent();
9279 BasicBlock *RequiredBB = nullptr;
9280
9281 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9282 RequiredBB = LocInst->getParent();
9283 else if (isa<llvm::Argument>(Loc))
9284 RequiredBB = &DVR->getFunction()->getEntryBlock();
9285
9286 if (RequiredBB && RequiredBB != CurBB) {
9287 assert(!RequiredBB->empty());
9288 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9289 RequiredBB->back().getIterator());
9290 DVRsToDelete.push_back(DVR);
9291 }
9292 };
9293
9294 // The location and scope of variable records still point to
9295 // the parent function of the target region. Update them.
9296 for (Instruction &I : instructions(Func)) {
9297 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9298 UpdateDebugRecord(&DVR);
9299 MoveDebugRecordToCorrectBlock(&DVR);
9300 }
9301 }
9302 for (auto *DVR : DVRsToDelete)
9303 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9304 // An extra argument is passed to the device. Create the debug data for it.
9305 if (OMPBuilder.Config.isTargetDevice()) {
9306 DICompileUnit *CU = NewSP->getUnit();
9307 Module *M = Func->getParent();
9308 DIBuilder DB(*M, true, CU);
9309 DIType *VoidPtrTy =
9310 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9311 unsigned ArgNo = Func->arg_size();
9312 DILocalVariable *Var = DB.createParameterVariable(
9313 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9314 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9315 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9316 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9317 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9318 &(*Func->begin()));
9319 }
9320}
9321
9323 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9324 return cast<Operator>(V)->getOperand(0);
9325 return V;
9326}
9327
9329 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9331 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9334 DebugLoc OutlinedFnLoc) {
9335 SmallVector<Type *> ParameterTypes;
9336 if (OMPBuilder.Config.isTargetDevice()) {
9337 // All parameters to target devices are passed as pointers
9338 // or i64. This assumes 64-bit address spaces/pointers.
9339 for (auto &Arg : Inputs)
9340 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9341 ? Arg->getType()
9342 : Type::getInt64Ty(Builder.getContext()));
9343 } else {
9344 for (auto &Arg : Inputs)
9345 ParameterTypes.push_back(Arg->getType());
9346 }
9347
9348 // The implicit dyn_ptr argument is always the last parameter on both host
9349 // and device so the argument counts match without runtime manipulation.
9350 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9351 ParameterTypes.push_back(PtrTy);
9352
9353 auto BB = Builder.GetInsertBlock();
9354 auto M = BB->getModule();
9355 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9356 /*isVarArg*/ false);
9357 auto Func =
9358 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9359
9360 // Forward target-cpu and target-features function attributes from the
9361 // original function to the new outlined function.
9362 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9363
9364 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9365 if (TargetCpuAttr.isStringAttribute())
9366 Func->addFnAttr(TargetCpuAttr);
9367
9368 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9369 if (TargetFeaturesAttr.isStringAttribute())
9370 Func->addFnAttr(TargetFeaturesAttr);
9371
9372 if (OMPBuilder.Config.isTargetDevice()) {
9373 Value *ExecMode =
9374 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9375 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9376 }
9377
9378 // Save insert point.
9379 IRBuilder<>::InsertPointGuard IPG(Builder);
9380 // We will generate the entries in the outlined function but the debug
9381 // location is still pointing to the parent function, which is the wrong
9382 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9383 // position scoped to the subprogram that will be attached to the outlined
9384 // function, so it is what everything emitted below needs.
9385 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9386
9387 // Generate the region into the function.
9388 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9389 Builder.SetInsertPoint(EntryBB);
9390
9391 // Insert target init call in the device compilation pass. On the host
9392 // (e.g. a non-GPU offload target), there is no runtime init/deinit
9393 // sequence, but the runtime still needs a '<kernel>_kernel_environment'
9394 // global to know how the kernel was configured, so emit it directly.
9395 if (OMPBuilder.Config.isTargetDevice())
9396 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9397 else
9398 OMPBuilder.emitKernelEnvironment(Builder, DefaultAttrs);
9399
9400 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9401
9402 // As we embed the user code in the middle of our target region after we
9403 // generate entry code, we must move what allocas we can into the entry
9404 // block to avoid possible breaking optimisations
9406
9407 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9408 BasicBlock *OutlinedBodyBB =
9409 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9411 CBFunc(Builder.saveIP(), OutlinedBodyBB->begin(), ExitBB);
9412 if (!AfterIP)
9413 return AfterIP.takeError();
9414 Builder.SetInsertPoint(ExitBB);
9415 // The body callback builds the body with its own IRBuilder and cannot reach
9416 // this one directly. But a body holding another OpenMP construct, a nested
9417 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9418 // this Builder pointing at the wrong debug location, or at none at all. The
9419 // epilogue below belongs to the target construct rather than to whatever the
9420 // body emitted last, so re-establish the location the prologue was emitted
9421 // with.
9422 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9423
9424 // Insert target deinit call in the device compilation pass.
9425 if (OMPBuilder.Config.isTargetDevice())
9426 OMPBuilder.createTargetDeinit(Builder);
9427
9428 // Insert return instruction.
9429 Builder.CreateRetVoid();
9430
9431 // New Alloca IP at entry point of created device function.
9432 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9433 auto AllocaIP = Builder.saveIP();
9434
9435 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrPseudoProbe());
9436
9437 // Do not include the artificial dyn_ptr argument.
9438 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9439
9441
9442 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9443 // Things like GEP's can come in the form of Constants. Constants and
9444 // ConstantExpr's do not have access to the knowledge of what they're
9445 // contained in, so we must dig a little to find an instruction so we
9446 // can tell if they're used inside of the function we're outlining. We
9447 // also replace the original constant expression with a new instruction
9448 // equivalent; an instruction as it allows easy modification in the
9449 // following loop, as we can now know the constant (instruction) is
9450 // owned by our target function and replaceUsesOfWith can now be invoked
9451 // on it (cannot do this with constants it seems). A brand new one also
9452 // allows us to be cautious as it is perhaps possible the old expression
9453 // was used inside of the function but exists and is used externally
9454 // (unlikely by the nature of a Constant, but still).
9455 // NOTE: We cannot remove dead constants that have been rewritten to
9456 // instructions at this stage, we run the risk of breaking later lowering
9457 // by doing so as we could still be in the process of lowering the module
9458 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9459 // constants we have created rewritten versions of.
9460 if (auto *Const = dyn_cast<Constant>(Input))
9461 convertUsersOfConstantsToInstructions(Const, Func, false);
9462
9463 // Collect users before iterating over them to avoid invalidating the
9464 // iteration in case a user uses Input more than once (e.g. a call
9465 // instruction).
9466 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9467 // Collect all the instructions
9469 if (auto *Instr = dyn_cast<Instruction>(User))
9470 if (Instr->getFunction() == Func)
9471 Instr->replaceUsesOfWith(Input, InputCopy);
9472 };
9473
9474 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9475
9476 // Rewrite uses of input valus to parameters.
9477 for (auto InArg : zip(Inputs, ArgRange)) {
9478 Value *Input = std::get<0>(InArg);
9479 Argument &Arg = std::get<1>(InArg);
9480 Value *InputCopy = nullptr;
9481
9482 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9483 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(), ExitBB->begin());
9484 if (!AfterIP)
9485 return AfterIP.takeError();
9486 Builder.restoreIP(*AfterIP);
9487 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9488
9489 // In certain cases a Global may be set up for replacement, however, this
9490 // Global may be used in multiple arguments to the kernel, just segmented
9491 // apart, for example, if we have a global array, that is sectioned into
9492 // multiple mappings (technically not legal in OpenMP, but there is a case
9493 // in Fortran for Common Blocks where this is neccesary), we will end up
9494 // with GEP's into this array inside the kernel, that refer to the Global
9495 // but are technically separate arguments to the kernel for all intents and
9496 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9497 // index, it will fold into an referal to the Global, if we then encounter
9498 // this folded GEP during replacement all of the references to the
9499 // Global in the kernel will be replaced with the argument we have generated
9500 // that corresponds to it, including any other GEP's that refer to the
9501 // Global that may be other arguments. This will invalidate all of the other
9502 // preceding mapped arguments that refer to the same global that may be
9503 // separate segments. To prevent this, we defer global processing until all
9504 // other processing has been performed.
9507 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9508 continue;
9509 }
9510
9512 continue;
9513
9514 ReplaceValue(Input, InputCopy, Func);
9515 }
9516
9517 // Replace all of our deferred Input values, currently just Globals.
9518 for (auto Deferred : DeferredReplacement)
9519 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9520
9521 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9522 ValueReplacementMap);
9523 return Func;
9524}
9525/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9526/// of pointers containing shared data between the parent task and the created
9527/// task.
9529 IRBuilderBase &Builder,
9530 Value *TaskWithPrivates,
9531 Type *TaskWithPrivatesTy) {
9532
9533 Type *TaskTy = OMPIRBuilder.Task;
9534 LLVMContext &Ctx = Builder.getContext();
9535 Value *TaskT =
9536 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9537 Value *Shareds = TaskT;
9538 // TaskWithPrivatesTy can be one of the following
9539 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9540 // %struct.privates }
9541 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9542 //
9543 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9544 // its first member has to be the task descriptor. TaskTy is the type of the
9545 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9546 // first member of TaskT, gives us the pointer to shared data.
9547 if (TaskWithPrivatesTy != TaskTy)
9548 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9549 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9550}
9551/// Create an entry point for a target task with the following.
9552/// It'll have the following signature
9553/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9554/// This function is called from emitTargetTask once the
9555/// code to launch the target kernel has been outlined already.
9556/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9557/// into the task structure so that the deferred target task can access this
9558/// data even after the stack frame of the generating task has been rolled
9559/// back. Offloading arrays contain base pointers, pointers, sizes etc
9560/// of the data that the target kernel will access. These in effect are the
9561/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9563 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9564 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9565 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9566
9567 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9568 // This is because PrivatesTy is the type of the structure in which
9569 // we pass the offloading arrays to the deferred target task.
9570 assert((!NumOffloadingArrays || PrivatesTy) &&
9571 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9572 "to privatize");
9573
9574 Module &M = OMPBuilder.M;
9575 // KernelLaunchFunction is the target launch function, i.e.
9576 // the function that sets up kernel arguments and calls
9577 // __tgt_target_kernel to launch the kernel on the device.
9578 //
9579 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9580
9581 // StaleCI is the CallInst which is the call to the outlined
9582 // target kernel launch function. If there are local live-in values
9583 // that the outlined function uses then these are aggregated into a structure
9584 // which is passed as the second argument. If there are no local live-in
9585 // values or if all values used by the outlined kernel are global variables,
9586 // then there's only one argument, the threadID. So, StaleCI can be
9587 //
9588 // %structArg = alloca { ptr, ptr }, align 8
9589 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9590 // store ptr %20, ptr %gep_, align 8
9591 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9592 // store ptr %21, ptr %gep_8, align 8
9593 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9594 //
9595 // OR
9596 //
9597 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9598 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9599
9600 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9601 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9602 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9603
9604 auto ProxyFnTy =
9605 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9606 /* isVarArg */ false);
9607 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9608 ".omp_target_task_proxy_func", M);
9609 Value *ThreadId = ProxyFn->getArg(0);
9610 Value *TaskWithPrivates = ProxyFn->getArg(1);
9611 ThreadId->setName("thread.id");
9612 TaskWithPrivates->setName("task");
9613
9614 bool HasShareds = SharedArgsOperandNo > 0;
9615 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9616 IRBuilder<>::InsertPointGuard IPG(Builder);
9617 BasicBlock *EntryBB =
9618 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9619 Builder.SetInsertPoint(EntryBB);
9620 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9621
9622 SmallVector<Value *> KernelLaunchArgs;
9623 KernelLaunchArgs.reserve(StaleCI->arg_size());
9624 KernelLaunchArgs.push_back(ThreadId);
9625
9626 if (HasOffloadingArrays) {
9627 assert(TaskTy != TaskWithPrivatesTy &&
9628 "If there are offloading arrays to pass to the target"
9629 "TaskTy cannot be the same as TaskWithPrivatesTy");
9630 (void)TaskTy;
9631 Value *Privates =
9632 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9633 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9634 KernelLaunchArgs.push_back(
9635 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9636 }
9637
9638 if (HasShareds) {
9639 auto *ArgStructAlloca =
9640 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9641 assert(ArgStructAlloca &&
9642 "Unable to find the alloca instruction corresponding to arguments "
9643 "for extracted function");
9644 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9645 std::optional<TypeSize> ArgAllocSize =
9646 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9647 assert(ArgStructType && ArgAllocSize &&
9648 "Unable to determine size of arguments for extracted function");
9649 uint64_t StructSize = ArgAllocSize->getFixedValue();
9650
9651 AllocaInst *NewArgStructAlloca =
9652 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9653
9654 Value *SharedsSize = Builder.getInt64(StructSize);
9655
9657 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9658
9659 Builder.CreateMemCpy(
9660 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9661 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9662 KernelLaunchArgs.push_back(NewArgStructAlloca);
9663 }
9664 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9665 Builder.CreateRetVoid();
9666 return ProxyFn;
9667}
9669
9670 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9671 return GEP->getSourceElementType();
9672 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9673 return Alloca->getAllocatedType();
9674
9675 llvm_unreachable("Unhandled Instruction type");
9676 return nullptr;
9677}
9678// This function returns a struct that has at most two members.
9679// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9680// descriptor. The second member, if needed, is a struct containing arrays
9681// that need to be passed to the offloaded target kernel. For example,
9682// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9683// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9684// respectively, then the types created by this function are
9685//
9686// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9687// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9688// %struct.privates }
9689// %struct.task_with_privates is returned by this function.
9690// If there aren't any offloading arrays to pass to the target kernel,
9691// %struct.kmp_task_ompbuilder_t is returned.
9692static StructType *
9694 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9695
9696 if (OffloadingArraysToPrivatize.empty())
9697 return OMPIRBuilder.Task;
9698
9699 SmallVector<Type *, 4> StructFieldTypes;
9700 for (Value *V : OffloadingArraysToPrivatize) {
9701 assert(V->getType()->isPointerTy() &&
9702 "Expected pointer to array to privatize. Got a non-pointer value "
9703 "instead");
9704 Type *ArrayTy = getOffloadingArrayType(V);
9705 assert(ArrayTy && "ArrayType cannot be nullptr");
9706 StructFieldTypes.push_back(ArrayTy);
9707 }
9708 StructType *PrivatesStructTy =
9709 StructType::create(StructFieldTypes, "struct.privates");
9710 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9711 "struct.task_with_privates");
9712}
9714 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9715 TargetRegionEntryInfo &EntryInfo,
9717 Function *&OutlinedFn, Constant *&OutlinedFnID,
9721 DebugLoc OutlinedFnLoc) {
9722
9723 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9724 [&](StringRef EntryFnName) {
9725 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9726 EntryFnName, Inputs, CBFunc,
9727 ArgAccessorFuncCB, OutlinedFnLoc);
9728 };
9729
9730 return OMPBuilder.emitTargetRegionFunction(
9731 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9732 OutlinedFnID);
9733}
9734
9736 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9738 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9739 bool HasNoWait) {
9740
9741 // The following explains the code-gen scenario for the `target` directive. A
9742 // similar scneario is followed for other device-related directives (e.g.
9743 // `target enter data`) but in similar fashion since we only need to emit task
9744 // that encapsulates the proper runtime call.
9745 //
9746 // When we arrive at this function, the target region itself has been
9747 // outlined into the function OutlinedFn.
9748 // So at ths point, for
9749 // --------------------------------------------------------------
9750 // void user_code_that_offloads(...) {
9751 // omp target depend(..) map(from:a) map(to:b) private(i)
9752 // do i = 1, 10
9753 // a(i) = b(i) + n
9754 // }
9755 //
9756 // --------------------------------------------------------------
9757 //
9758 // we have
9759 //
9760 // --------------------------------------------------------------
9761 //
9762 // void user_code_that_offloads(...) {
9763 // %.offload_baseptrs = alloca [2 x ptr], align 8
9764 // %.offload_ptrs = alloca [2 x ptr], align 8
9765 // %.offload_mappers = alloca [2 x ptr], align 8
9766 // ;; target region has been outlined and now we need to
9767 // ;; offload to it via a target task.
9768 // }
9769 // void outlined_device_function(ptr a, ptr b, ptr n) {
9770 // n = *n_ptr;
9771 // do i = 1, 10
9772 // a(i) = b(i) + n
9773 // }
9774 //
9775 // We have to now do the following
9776 // (i) Make an offloading call to outlined_device_function using the OpenMP
9777 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9778 // emitted by emitKernelLaunch
9779 // (ii) Create a task entry point function that calls kernel_launch_function
9780 // and is the entry point for the target task. See
9781 // '@.omp_target_task_proxy_func in the pseudocode below.
9782 // (iii) Create a task with the task entry point created in (ii)
9783 //
9784 // That is we create the following
9785 // struct task_with_privates {
9786 // struct kmp_task_ompbuilder_t task_struct;
9787 // struct privates {
9788 // [2 x ptr] ; baseptrs
9789 // [2 x ptr] ; ptrs
9790 // [2 x i64] ; sizes
9791 // }
9792 // }
9793 // void user_code_that_offloads(...) {
9794 // %.offload_baseptrs = alloca [2 x ptr], align 8
9795 // %.offload_ptrs = alloca [2 x ptr], align 8
9796 // %.offload_sizes = alloca [2 x i64], align 8
9797 //
9798 // %structArg = alloca { ptr, ptr, ptr }, align 8
9799 // %strucArg[0] = a
9800 // %strucArg[1] = b
9801 // %strucArg[2] = &n
9802 //
9803 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9804 // sizeof(kmp_task_ompbuilder_t),
9805 // sizeof(structArg),
9806 // @.omp_target_task_proxy_func,
9807 // ...)
9808 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9809 // sizeof(structArg))
9810 // memcpy(target_task_with_privates->privates->baseptrs,
9811 // offload_baseptrs, sizeof(offload_baseptrs)
9812 // memcpy(target_task_with_privates->privates->ptrs,
9813 // offload_ptrs, sizeof(offload_ptrs)
9814 // memcpy(target_task_with_privates->privates->sizes,
9815 // offload_sizes, sizeof(offload_sizes)
9816 // dependencies_array = ...
9817 // ;; if nowait not present
9818 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9819 // call @__kmpc_omp_task_begin_if0(...)
9820 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9821 // %target_task_with_privates)
9822 // call @__kmpc_omp_task_complete_if0(...)
9823 // }
9824 //
9825 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9826 // ptr %task) {
9827 // %structArg = alloca {ptr, ptr, ptr}
9828 // %task_ptr = getelementptr(%task, 0, 0)
9829 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9830 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9831 //
9832 // %offloading_arrays = getelementptr(%task, 0, 1)
9833 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9834 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9835 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9836 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9837 // %offload_sizes, %structArg)
9838 // }
9839 //
9840 // We need the proxy function because the signature of the task entry point
9841 // expected by kmpc_omp_task is always the same and will be different from
9842 // that of the kernel_launch function.
9843 //
9844 // kernel_launch_function is generated by emitKernelLaunch and has the
9845 // always_inline attribute. For this example, it'll look like so:
9846 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9847 // %offload_sizes, %structArg) alwaysinline {
9848 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9849 // ; load aggregated data from %structArg
9850 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9851 // ; offload_sizes
9852 // call i32 @__tgt_target_kernel(...,
9853 // outlined_device_function,
9854 // ptr %kernel_args)
9855 // }
9856 // void outlined_device_function(ptr a, ptr b, ptr n) {
9857 // n = *n_ptr;
9858 // do i = 1, 10
9859 // a(i) = b(i) + n
9860 // }
9861 //
9862 BasicBlock *TargetTaskBodyBB =
9863 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9864 BasicBlock *TargetTaskAllocaBB =
9865 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9866
9867 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB->begin());
9868 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB->begin());
9869
9870 auto OI = std::make_unique<OutlineInfo>();
9871 OI->EntryBB = TargetTaskAllocaBB;
9872 OI->OuterAllocBB = AllocaIP.getNodeParent();
9873
9874 // Add the thread ID argument.
9876 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9877 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9878
9879 // Generate the task body which will subsequently be outlined.
9880 Builder.restoreIP(TargetTaskBodyIP);
9881 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9882 return Err;
9883
9884 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9885 // it is given. These blocks are enumerated by
9886 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9887 // to be outside the region. In other words, OI.ExitBlock is expected to be
9888 // the start of the region after the outlining. We used to set OI.ExitBlock
9889 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9890 // except when the task body is a single basic block. In that case,
9891 // OI.ExitBlock is set to the single task body block and will get left out of
9892 // the outlining process. So, simply create a new empty block to which we
9893 // uncoditionally branch from where TaskBodyCB left off
9894 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9895 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9896 /*IsFinished=*/true);
9897
9898 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9899 bool NeedsTargetTask = HasNoWait && DeviceID;
9900 if (NeedsTargetTask) {
9901 for (auto *V :
9902 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9903 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9904 RTArgs.SizesArray}) {
9906 OffloadingArraysToPrivatize.push_back(V);
9907 OI->ExcludeArgsFromAggregate.push_back(V);
9908 }
9909 }
9910 }
9911 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9912 DeviceID, OffloadingArraysToPrivatize](
9913 Function &OutlinedFn) mutable {
9914 assert(OutlinedFn.hasOneUse() &&
9915 "there must be a single user for the outlined function");
9916
9917 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9918
9919 // The first argument of StaleCI is always the thread id.
9920 // The next few arguments are the pointers to offloading arrays
9921 // if any. (see OffloadingArraysToPrivatize)
9922 // Finally, all other local values that are live-in into the outlined region
9923 // end up in a structure whose pointer is passed as the last argument. This
9924 // piece of data is passed in the "shared" field of the task structure. So,
9925 // we know we have to pass shareds to the task if the number of arguments is
9926 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9927 // thread id. Further, for safety, we assert that the number of arguments of
9928 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9929 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9930 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9931 assert((!HasShareds ||
9932 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9933 "Wrong number of arguments for StaleCI when shareds are present");
9934 int SharedArgOperandNo =
9935 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9936
9937 StructType *TaskWithPrivatesTy =
9938 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9939 StructType *PrivatesTy = nullptr;
9940
9941 if (!OffloadingArraysToPrivatize.empty())
9942 PrivatesTy =
9943 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9944
9946 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9947 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9948
9949 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9950 << "\n");
9951
9952 Builder.SetInsertPoint(StaleCI);
9953
9954 // Gather the arguments for emitting the runtime call.
9955 uint32_t SrcLocStrSize;
9956 Constant *SrcLocStr =
9958 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9959
9960 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9961 //
9962 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9963 // the DeviceID to the deferred task and also since
9964 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9965 Function *TaskAllocFn =
9966 !NeedsTargetTask
9967 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9969 OMPRTL___kmpc_omp_target_task_alloc);
9970
9971 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
9972 // call.
9973 Value *ThreadID = getOrCreateThreadID(Ident);
9974
9975 // Argument - `sizeof_kmp_task_t` (TaskSize)
9976 // Tasksize refers to the size in bytes of kmp_task_t data structure
9977 // plus any other data to be passed to the target task, if any, which
9978 // is packed into a struct. kmp_task_t and the struct so created are
9979 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
9980 Value *TaskSize = Builder.getInt64(
9981 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
9982
9983 // Argument - `sizeof_shareds` (SharedsSize)
9984 // SharedsSize refers to the shareds array size in the kmp_task_t data
9985 // structure.
9986 Value *SharedsSize = Builder.getInt64(0);
9987 if (HasShareds) {
9988 auto *ArgStructAlloca =
9989 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
9990 assert(ArgStructAlloca &&
9991 "Unable to find the alloca instruction corresponding to arguments "
9992 "for extracted function");
9993 std::optional<TypeSize> ArgAllocSize =
9994 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9995 assert(ArgAllocSize &&
9996 "Unable to determine size of arguments for extracted function");
9997 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
9998 }
9999
10000 // Argument - `flags`
10001 // Task is tied iff (Flags & 1) == 1.
10002 // Task is untied iff (Flags & 1) == 0.
10003 // Task is final iff (Flags & 2) == 2.
10004 // Task is not final iff (Flags & 2) == 0.
10005 // A target task is not final and is untied.
10006 Value *Flags = Builder.getInt32(0);
10007
10008 // Emit the @__kmpc_omp_task_alloc runtime call
10009 // The runtime call returns a pointer to an area where the task captured
10010 // variables must be copied before the task is run (TaskData)
10011 CallInst *TaskData = nullptr;
10012
10013 SmallVector<llvm::Value *> TaskAllocArgs = {
10014 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
10015 /*flags=*/Flags,
10016 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
10017 /*task_func=*/ProxyFn};
10018
10019 if (NeedsTargetTask) {
10020 assert(DeviceID && "Expected non-empty device ID.");
10021 TaskAllocArgs.push_back(DeviceID);
10022 }
10023
10024 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
10025
10026 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
10027 if (HasShareds) {
10028 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
10030 *this, Builder, TaskData, TaskWithPrivatesTy);
10031 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
10032 SharedsSize);
10033 }
10034 if (!OffloadingArraysToPrivatize.empty()) {
10035 Value *Privates =
10036 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
10037 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
10038 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
10039 [[maybe_unused]] Type *ArrayType =
10040 getOffloadingArrayType(PtrToPrivatize);
10041 assert(ArrayType && "ArrayType cannot be nullptr");
10042
10043 Type *ElementType = PrivatesTy->getElementType(i);
10044 assert(ElementType == ArrayType &&
10045 "ElementType should match ArrayType");
10046 (void)ArrayType;
10047
10048 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
10049 Builder.CreateMemCpy(
10050 Dst, Alignment, PtrToPrivatize, Alignment,
10051 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
10052 }
10053 }
10054
10055 Value *DepArray = nullptr;
10056 Value *NumDeps = nullptr;
10057 if (Dependencies.DepArray) {
10058 DepArray = Dependencies.DepArray;
10059 NumDeps = Dependencies.NumDeps;
10060 } else if (!Dependencies.Deps.empty()) {
10061 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
10062 NumDeps = Builder.getInt32(Dependencies.Deps.size());
10063 }
10064
10065 // ---------------------------------------------------------------
10066 // V5.2 13.8 target construct
10067 // If the nowait clause is present, execution of the target task
10068 // may be deferred. If the nowait clause is not present, the target task is
10069 // an included task.
10070 // ---------------------------------------------------------------
10071 // The above means that the lack of a nowait on the target construct
10072 // translates to '#pragma omp task if(0)'
10073 if (!NeedsTargetTask) {
10074 if (DepArray) {
10075 Function *TaskWaitFn =
10076 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10078 TaskWaitFn,
10079 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10080 /*ndeps=*/NumDeps,
10081 /*dep_list=*/DepArray,
10082 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10083 /*noalias_dep_list=*/
10085 }
10086 // Included task.
10087 Function *TaskBeginFn =
10088 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10089 Function *TaskCompleteFn =
10090 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10091 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10092 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10093 CI->setDebugLoc(StaleCI->getDebugLoc());
10094 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10095 } else if (DepArray) {
10096 // HasNoWait - meaning the task may be deferred. Call
10097 // __kmpc_omp_task_with_deps if there are dependencies,
10098 // else call __kmpc_omp_task
10099 Function *TaskFn =
10100 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10102 TaskFn,
10103 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10104 ConstantInt::get(Builder.getInt32Ty(), 0),
10106 } else {
10107 // Emit the @__kmpc_omp_task runtime call to spawn the task
10108 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10109 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10110 }
10111
10112 Builder.ClearInsertionPoint();
10113 StaleCI->eraseFromParent();
10114 for (Instruction *I : llvm::reverse(ToBeDeleted))
10115 I->eraseFromParent();
10116 };
10117 addOutlineInfo(std::move(OI));
10118
10119 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10120 << *(Builder.GetInsertBlock()) << "\n");
10121 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10122 << *(Builder.getModule()) << "\n");
10123 return Builder.saveIP();
10124}
10125
10127 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10128 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10129 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10130 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10131 if (Error Err =
10132 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10133 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10134 return Err;
10135 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10136 return Error::success();
10137}
10138
10139static void emitTargetCall(
10140 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride,
10145 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10149 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10150 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10151 // Generate a function call to the host fallback implementation of the target
10152 // region. This is called by the host when no offload entry was generated for
10153 // the target region and when the offloading call fails at runtime.
10154 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10156 Builder.restoreIP(IP);
10157 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10158 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10159 FallbackArgs.push_back(
10160 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10161 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10162 return Builder.saveIP();
10163 };
10164
10165 bool HasDependencies = !Dependencies.empty();
10166 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10167
10169
10170 auto TaskBodyCB =
10171 [&](Value *DeviceID, Value *RTLoc,
10172 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10173 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10174 // produce any.
10176 // emitKernelLaunch makes the necessary runtime call to offload the
10177 // kernel. We then outline all that code into a separate function
10178 // ('kernel_launch_function' in the pseudo code above). This function is
10179 // then called by the target task proxy function (see
10180 // '@.omp_target_task_proxy_func' in the pseudo code above)
10181 // "@.omp_target_task_proxy_func' is generated by
10182 // emitTargetTaskProxyFunction.
10183 if (OutlinedFnID && DeviceID)
10184 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10185 EmitTargetCallFallbackCB, KArgs,
10186 DeviceID, RTLoc, TargetTaskAllocaIP);
10187
10188 // We only need to do the outlining if `DeviceID` is set to avoid calling
10189 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10190 // generating the `else` branch of an `if` clause.
10191 //
10192 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10193 // In this case, we execute the host implementation directly.
10194 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10195 }());
10196
10197 OMPBuilder.Builder.restoreIP(AfterIP);
10198 return Error::success();
10199 };
10200
10201 auto &&EmitTargetCallElse =
10202 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10204 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10205 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10206 // produce any.
10208 if (RequiresOuterTargetTask) {
10209 // Arguments that are intended to be directly forwarded to an
10210 // emitKernelLaunch call are pased as nullptr, since
10211 // OutlinedFnID=nullptr results in that call not being done.
10213 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10214 /*RTLoc=*/nullptr, AllocaIP,
10215 Dependencies, EmptyRTArgs, HasNoWait);
10216 }
10217 return EmitTargetCallFallbackCB(Builder.saveIP());
10218 }());
10219
10220 Builder.restoreIP(AfterIP);
10221 return Error::success();
10222 };
10223
10224 auto &&EmitTargetCallThen =
10225 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10227 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10228 Info.HasNoWait = HasNoWait;
10229 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10230
10232 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10233 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10234 /*IsNonContiguous=*/true,
10235 /*ForEndCall=*/false))
10236 return Err;
10237
10238 SmallVector<Value *, 3> NumTeamsC;
10239 for (auto [DefaultVal, RuntimeVal] :
10240 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10241 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10242 : Builder.getInt32(DefaultVal));
10243
10244 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10245 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10246 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10247 if (Clause)
10248 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10249 /*isSigned=*/false);
10250 return Clause;
10251 };
10252 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10253 if (Clause)
10254 Result =
10255 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10256 Result, Clause)
10257 : Clause;
10258 };
10259
10260 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10261 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10262 SmallVector<Value *, 3> NumThreadsC;
10263 Value *MaxThreadsClause =
10264 RuntimeAttrs.TeamsThreadLimit.size() == 1
10265 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10266 : nullptr;
10267
10268 for (auto [TeamsVal, TargetVal] : zip_equal(
10269 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10270 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10271 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10272
10273 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10274 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10275
10276 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10277 }
10278
10279 unsigned NumTargetItems = Info.NumberOfPtrs;
10280 Value *RTLoc = RTLocOverride;
10281 if (!RTLoc) {
10282 uint32_t SrcLocStrSize;
10283 Constant *SrcLocStr =
10284 OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10285 RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10286 llvm::omp::IdentFlag(0), 0);
10287 }
10288
10289 Value *TripCount = RuntimeAttrs.LoopTripCount
10290 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10291 Builder.getInt64Ty(),
10292 /*isSigned=*/false)
10293 : Builder.getInt64(0);
10294
10295 // Request zero groupprivate bytes by default.
10296 if (!DynCGroupMem)
10297 DynCGroupMem = Builder.getInt32(0);
10298
10300 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10301 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10302 DynCGroupMemFallback);
10303
10304 // Assume no error was returned because TaskBodyCB and
10305 // EmitTargetCallFallbackCB don't produce any.
10307 // The presence of certain clauses on the target directive require the
10308 // explicit generation of the target task.
10309 if (RequiresOuterTargetTask)
10310 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10311 RTLoc, AllocaIP, Dependencies,
10312 KArgs.RTArgs, Info.HasNoWait);
10313
10314 return OMPBuilder.emitKernelLaunch(
10315 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10316 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10317 }());
10318
10319 Builder.restoreIP(AfterIP);
10320 return Error::success();
10321 };
10322
10323 // If we don't have an ID for the target region, it means an offload entry
10324 // wasn't created. In this case we just run the host fallback directly and
10325 // ignore any potential 'if' clauses.
10326 if (!OutlinedFnID) {
10327 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10328 return;
10329 }
10330
10331 // If there's no 'if' clause, only generate the kernel launch code path.
10332 if (!IfCond) {
10333 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10334 return;
10335 }
10336
10337 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10338 EmitTargetCallElse, AllocaIP));
10339}
10340
10342 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10343 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10344 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10345 const TargetKernelDefaultAttrs &DefaultAttrs,
10346 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10347 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10350 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10351 bool HasNowait, Value *DynCGroupMem,
10352 OMPDynGroupprivateFallbackType DynCGroupMemFallback, DebugLoc OutlinedFnLoc,
10353 Value *RTLocOverride) {
10354
10355 if (!updateToLocation(Loc))
10356 return InsertPointTy();
10357
10358 Builder.restoreIP(CodeGenIP);
10359
10360 Function *OutlinedFn;
10361 Constant *OutlinedFnID = nullptr;
10362 // The target region is outlined into its own function. The LLVM IR for
10363 // the target region itself is generated using the callbacks CBFunc
10364 // and ArgAccessorFuncCB
10366 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10367 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10368 return Err;
10369
10370 // If we are not on the target device, then we need to generate code
10371 // to make a remote call (offload) to the previously outlined function
10372 // that represents the target region. Do that now.
10373 if (!Config.isTargetDevice())
10374 emitTargetCall(*this, Builder, RTLocOverride, AllocaIP, DeallocBlocks, Info,
10375 DefaultAttrs, RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID,
10376 Inputs, GenMapInfoCB, CustomMapperCB, Dependencies,
10377 HasNowait, DynCGroupMem, DynCGroupMemFallback);
10378 return Builder.saveIP();
10379}
10380
10381std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10382 StringRef FirstSeparator,
10383 StringRef Separator) {
10384 SmallString<128> Buffer;
10385 llvm::raw_svector_ostream OS(Buffer);
10386 StringRef Sep = FirstSeparator;
10387 for (StringRef Part : Parts) {
10388 OS << Sep << Part;
10389 Sep = Separator;
10390 }
10391 return OS.str().str();
10392}
10393
10394std::string
10396 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10397 Config.separator());
10398}
10399
10401 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10402 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10403 if (Elem.second) {
10404 assert(Elem.second->getValueType() == Ty &&
10405 "OMP internal variable has different type than requested");
10406 } else {
10407 // TODO: investigate the appropriate linkage type used for the global
10408 // variable for possibly changing that to internal or private, or maybe
10409 // create different versions of the function for different OMP internal
10410 // variables.
10411 const DataLayout &DL = M.getDataLayout();
10412 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10413 // default global AS is 1.
10414 // See double-target-call-with-declare-target.f90 and
10415 // declare-target-vars-in-target-region.f90 libomptarget
10416 // tests.
10417 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10418 : M.getTargetTriple().isAMDGPU()
10419 ? 0
10420 : DL.getDefaultGlobalsAddressSpace();
10421 auto Linkage = this->M.getTargetTriple().isWasm()
10424 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10425 Constant::getNullValue(Ty), Elem.first(),
10426 /*InsertBefore=*/nullptr,
10427 GlobalValue::NotThreadLocal, AddressSpaceVal);
10428 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10429 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10430 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10431 Elem.second = GV;
10432 }
10433
10434 return Elem.second;
10435}
10436
10437Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10438 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10439 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10440 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10441}
10442
10444 LLVMContext &Ctx = Builder.getContext();
10445 Value *Null =
10446 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10447 Value *SizeGep =
10448 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10449 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10450 return SizePtrToInt;
10451}
10452
10455 std::string VarName) {
10456 llvm::Constant *MaptypesArrayInit =
10457 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10458 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10459 M, MaptypesArrayInit->getType(),
10460 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10461 VarName);
10462 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10463 return MaptypesArrayGlobal;
10464}
10465
10467 InsertPointTy AllocaIP,
10468 unsigned NumOperands,
10469 struct MapperAllocas &MapperAllocas) {
10470 if (!updateToLocation(Loc))
10471 return;
10472
10473 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10474 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10475 Builder.restoreIP(AllocaIP);
10476 AllocaInst *ArgsBase = Builder.CreateAlloca(
10477 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10478 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10479 ".offload_ptrs");
10480 AllocaInst *ArgSizes = Builder.CreateAlloca(
10481 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10483 MapperAllocas.ArgsBase = ArgsBase;
10484 MapperAllocas.Args = Args;
10485 MapperAllocas.ArgSizes = ArgSizes;
10486}
10487
10489 Function *MapperFunc, Value *SrcLocInfo,
10490 Value *MaptypesArg, Value *MapnamesArg,
10492 int64_t DeviceID, unsigned NumOperands) {
10493 if (!updateToLocation(Loc))
10494 return;
10495
10496 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10497 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10498 Value *ArgsBaseGEP =
10499 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10500 {Builder.getInt32(0), Builder.getInt32(0)});
10501 Value *ArgsGEP =
10502 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10503 {Builder.getInt32(0), Builder.getInt32(0)});
10504 Value *ArgSizesGEP =
10505 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10506 {Builder.getInt32(0), Builder.getInt32(0)});
10507 Value *NullPtr =
10508 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10509 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10510 Builder.getInt32(NumOperands),
10511 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10512 MaptypesArg, MapnamesArg, NullPtr});
10513}
10514
10516 TargetDataRTArgs &RTArgs,
10517 TargetDataInfo &Info,
10518 bool ForEndCall) {
10519 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10520 "expected region end call to runtime only when end call is separate");
10521 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10522 auto VoidPtrTy = UnqualPtrTy;
10523 auto VoidPtrPtrTy = UnqualPtrTy;
10524 auto Int64Ty = Type::getInt64Ty(M.getContext());
10525 auto Int64PtrTy = UnqualPtrTy;
10526
10527 if (!Info.NumberOfPtrs) {
10528 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10529 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10530 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10531 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10532 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10533 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10534 return;
10535 }
10536
10537 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10538 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10539 Info.RTArgs.BasePointersArray,
10540 /*Idx0=*/0, /*Idx1=*/0);
10541 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10542 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10543 /*Idx0=*/0,
10544 /*Idx1=*/0);
10545 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10546 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10547 /*Idx0=*/0, /*Idx1=*/0);
10548 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10549 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10550 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10551 : Info.RTArgs.MapTypesArray,
10552 /*Idx0=*/0,
10553 /*Idx1=*/0);
10554
10555 // Only emit the mapper information arrays if debug information is
10556 // requested.
10557 if (!Info.EmitDebug)
10558 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10559 else
10560 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10561 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10562 /*Idx0=*/0,
10563 /*Idx1=*/0);
10564 // If there is no user-defined mapper, set the mapper array to nullptr to
10565 // avoid an unnecessary data privatization
10566 if (!Info.HasMapper)
10567 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10568 else
10569 RTArgs.MappersArray =
10570 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10571}
10572
10574 InsertPointTy CodeGenIP,
10575 MapInfosTy &CombinedInfo,
10576 TargetDataInfo &Info) {
10578 CombinedInfo.NonContigInfo;
10579
10580 // Build an array of struct descriptor_dim and then assign it to
10581 // offload_args.
10582 //
10583 // struct descriptor_dim {
10584 // uint64_t offset;
10585 // uint64_t count;
10586 // uint64_t stride
10587 // };
10588 Type *Int64Ty = Builder.getInt64Ty();
10590 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10591 "struct.descriptor_dim");
10592
10593 enum { OffsetFD = 0, CountFD, StrideFD };
10594 // We need two index variable here since the size of "Dims" is the same as
10595 // the size of Components, however, the size of offset, count, and stride is
10596 // equal to the size of base declaration that is non-contiguous.
10597 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10598 // Skip emitting ir if dimension size is 1 since it cannot be
10599 // non-contiguous.
10600 if (NonContigInfo.Dims[I] == 1)
10601 continue;
10602 Builder.restoreIP(AllocaIP);
10603 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10604 AllocaInst *DimsAddr =
10605 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10606 Builder.restoreIP(CodeGenIP);
10607 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10608 unsigned RevIdx = EE - II - 1;
10609 Value *DimsLVal = Builder.CreateInBoundsGEP(
10610 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10611 // Offset
10612 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10613 Builder.CreateAlignedStore(
10614 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10615 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10616 // Count
10617 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10618 Builder.CreateAlignedStore(
10619 NonContigInfo.Counts[L][RevIdx], CountLVal,
10620 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10621 // Stride
10622 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10623 Builder.CreateAlignedStore(
10624 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10625 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10626 }
10627 // args[I] = &dims
10628 Builder.restoreIP(CodeGenIP);
10629 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10630 DimsAddr, Builder.getPtrTy());
10631 Value *P = Builder.CreateConstInBoundsGEP2_32(
10632 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10633 Info.RTArgs.PointersArray, 0, I);
10634 Builder.CreateAlignedStore(
10635 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10636 ++L;
10637 }
10638}
10639
10640void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10641 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10642 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10643 BasicBlock *ExitBB, bool IsInit) {
10644 StringRef Prefix = IsInit ? ".init" : ".del";
10645
10646 // Evaluate if this is an array section.
10648 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10649 Value *IsArray =
10650 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10651 Value *DeleteBit = Builder.CreateAnd(
10652 MapType,
10653 Builder.getInt64(
10654 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10655 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10656 Value *DeleteCond;
10657 Value *Cond;
10658 if (IsInit) {
10659 // base != begin?
10660 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10661 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10662 DeleteCond = Builder.CreateIsNull(
10663 DeleteBit,
10664 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10665 } else {
10666 Cond = IsArray;
10667 DeleteCond = Builder.CreateIsNotNull(
10668 DeleteBit,
10669 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10670 }
10671 Cond = Builder.CreateAnd(Cond, DeleteCond);
10672 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10673
10674 emitBlock(BodyBB, MapperFn);
10675 // Get the array size by multiplying element size and element number (i.e., \p
10676 // Size).
10677 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10678 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10679 // memory allocation/deletion purpose only.
10680 Value *MapTypeArg = Builder.CreateAnd(
10681 MapType,
10682 Builder.getInt64(
10683 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10684 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10685 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10686 MapTypeArg = Builder.CreateOr(
10687 MapTypeArg,
10688 Builder.getInt64(
10689 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10690 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10691
10692 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10693 // data structure.
10694 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10695 ArraySize, MapTypeArg, MapName};
10697 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10698 OffloadingArgs);
10699}
10700
10703 llvm::Value *BeginArg)>
10704 GenMapInfoCB,
10705 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10706 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10707 SmallVector<Type *> Params;
10708 Params.emplace_back(Builder.getPtrTy());
10709 Params.emplace_back(Builder.getPtrTy());
10710 Params.emplace_back(Builder.getPtrTy());
10711 Params.emplace_back(Builder.getInt64Ty());
10712 Params.emplace_back(Builder.getInt64Ty());
10713 Params.emplace_back(Builder.getPtrTy());
10714
10715 auto *FnTy =
10716 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10717
10718 SmallString<64> TyStr;
10719 raw_svector_ostream Out(TyStr);
10720 Function *MapperFn =
10722 MapperFn->addFnAttr(Attribute::NoInline);
10723 MapperFn->addFnAttr(Attribute::NoUnwind);
10724 MapperFn->addParamAttr(0, Attribute::NoUndef);
10725 MapperFn->addParamAttr(1, Attribute::NoUndef);
10726 MapperFn->addParamAttr(2, Attribute::NoUndef);
10727 MapperFn->addParamAttr(3, Attribute::NoUndef);
10728 MapperFn->addParamAttr(4, Attribute::NoUndef);
10729 MapperFn->addParamAttr(5, Attribute::NoUndef);
10730
10731 // Start the mapper function code generation.
10732 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10734 Builder.SetInsertPoint(EntryBB);
10735 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10736
10737 Value *MapperHandle = MapperFn->getArg(0);
10738 Value *BaseIn = MapperFn->getArg(1);
10739 Value *BeginIn = MapperFn->getArg(2);
10740 Value *Size = MapperFn->getArg(3);
10741 Value *MapType = MapperFn->getArg(4);
10742 Value *MapName = MapperFn->getArg(5);
10743
10744 // Compute the starting and end addresses of array elements.
10745 // Prepare common arguments for array initiation and deletion.
10746 // Convert the size in bytes into the number of array elements.
10747 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10748 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10749 Value *PtrBegin = BeginIn;
10750 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10751
10752 // Emit array initiation if this is an array section and \p MapType indicates
10753 // that memory allocation is required.
10754 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10755 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10756 MapType, MapName, ElementSize, HeadBB,
10757 /*IsInit=*/true);
10758
10759 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10760
10761 // Emit the loop header block.
10762 emitBlock(HeadBB, MapperFn);
10763 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10764 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10765 // Evaluate whether the initial condition is satisfied.
10766 Value *IsEmpty =
10767 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10768 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10769
10770 // Emit the loop body block.
10771 emitBlock(BodyBB, MapperFn);
10772 BasicBlock *LastBB = BodyBB;
10773 PHINode *PtrPHI =
10774 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10775 PtrPHI->addIncoming(PtrBegin, HeadBB);
10776
10777 // Get map clause information. Fill up the arrays with all mapped variables.
10778 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10779 if (!Info)
10780 return Info.takeError();
10781
10782 // Call the runtime API __tgt_mapper_num_components to get the number of
10783 // pre-existing components.
10784 Value *OffloadingArgs[] = {MapperHandle};
10785 Value *PreviousSize = createRuntimeFunctionCall(
10786 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10787 OffloadingArgs);
10788 Value *ShiftedPreviousSize =
10789 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10790
10791 // Fill up the runtime mapper handle for all components.
10792 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10793 Value *CurBaseArg = Info->BasePointers[I];
10794 Value *CurBeginArg = Info->Pointers[I];
10795 Value *CurSizeArg = Info->Sizes[I];
10796 Value *CurNameArg = Info->Names.size()
10797 ? Info->Names[I]
10798 : Constant::getNullValue(Builder.getPtrTy());
10799
10800 Value *OriMapType = Builder.getInt64(
10801 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10802 Info->Types[I]));
10803 auto RawType =
10804 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10805 Info->Types[I]);
10806 constexpr uint64_t MemberOfMask =
10807 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10808 constexpr uint64_t AttachBit =
10809 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10810 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10811
10812 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10813 // current array element (N = __tgt_mapper_num_components() at loop body
10814 // start).
10815 //
10816 // Example 1:
10817 // struct S { int x; int *p; };
10818 //
10819 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10820 // use: S arr[2]; ... map(arr)
10821 // entries per element:
10822 //
10823 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10824 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10825 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10826 //
10827 // Example 2:
10828 // struct S1 { int x; int y; };
10829 // struct S2 { int z; S1 *s1p; };
10830 //
10831 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10832 // s2.s1p->y)
10833 // use: S2 arr[2]; ... map(arr)
10834 // entries per element:
10835 //
10836 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10837 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10838 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10839 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10840 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10841 //
10842 // x/y carry inner MEMBER_OF(2)
10843 // which is shifted by N to become MEMBER_OF(N+2).
10844 //
10845 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10846 // the combined ALLOC entry for the s1p->x..y block, and the individual
10847 // x/y entries that are MEMBER_OF that block, all describe storage
10848 // reached through the attach ptr arr[i].s1p.
10849 //
10850 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10851 // linking them to the parent struct:
10852 //
10853 // * (*) Entries with HasAttachPtr: they represent pointee data that
10854 // occupies a different storage block than the struct being mapped, so
10855 // they are not a member of it. They may still be MEMBER_OF an entry
10856 // within that pointee block, in which case those pre-existing bits are
10857 // shifted -- see (***).
10858 // * (**) ATTACH entries: they are not a member of anything — they just
10859 // link a ptr to its ptee.
10860 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10861 // its pre-shaped entries already carry their final MEMBER_OF bits.
10862 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10863 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10864 // it.
10865 //
10866 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10867 // s1p->x/y entries above), those bits are still shifted by N.
10868 Value *MemberMapType;
10869 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10870 Info->HasAttachPtr[I]) {
10871 if (RawType & MemberOfMask)
10872 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10873 else
10874 MemberMapType = OriMapType;
10875 } else {
10876 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10877 }
10878
10879 // Combine the map type inherited from user-defined mapper with that
10880 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10881 // bits of the \a MapType, which is the input argument of the mapper
10882 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10883 // bits of MemberMapType.
10884 // [OpenMP 5.0], 1.2.6. map-type decay.
10885 // | alloc | to | from | tofrom | release | delete
10886 // ----------------------------------------------------------
10887 // alloc | alloc | alloc | alloc | alloc | release | delete
10888 // to | alloc | to | alloc | to | release | delete
10889 // from | alloc | alloc | from | from | release | delete
10890 // tofrom | alloc | to | from | tofrom | release | delete
10891 Value *LeftToFrom = Builder.CreateAnd(
10892 MapType,
10893 Builder.getInt64(
10894 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10895 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10896 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10897 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10898 BasicBlock *AllocElseBB =
10899 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10900 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10901 BasicBlock *ToElseBB =
10902 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10903 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10904 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10905 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10906 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10907 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10908 emitBlock(AllocBB, MapperFn);
10909 Value *AllocMapType = Builder.CreateAnd(
10910 MemberMapType,
10911 Builder.getInt64(
10912 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10913 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10914 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10915 Builder.CreateBr(EndBB);
10916 emitBlock(AllocElseBB, MapperFn);
10917 Value *IsTo = Builder.CreateICmpEQ(
10918 LeftToFrom,
10919 Builder.getInt64(
10920 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10921 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10922 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10923 // In case of to, clear OMP_MAP_FROM.
10924 emitBlock(ToBB, MapperFn);
10925 Value *ToMapType = Builder.CreateAnd(
10926 MemberMapType,
10927 Builder.getInt64(
10928 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10929 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10930 Builder.CreateBr(EndBB);
10931 emitBlock(ToElseBB, MapperFn);
10932 Value *IsFrom = Builder.CreateICmpEQ(
10933 LeftToFrom,
10934 Builder.getInt64(
10935 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10936 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10937 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10938 // In case of from, clear OMP_MAP_TO.
10939 emitBlock(FromBB, MapperFn);
10940 Value *FromMapType = Builder.CreateAnd(
10941 MemberMapType,
10942 Builder.getInt64(
10943 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10944 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10945 // In case of tofrom, do nothing.
10946 emitBlock(EndBB, MapperFn);
10947 LastBB = EndBB;
10948 PHINode *CurMapType =
10949 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10950 CurMapType->addIncoming(AllocMapType, AllocBB);
10951 CurMapType->addIncoming(ToMapType, ToBB);
10952 CurMapType->addIncoming(FromMapType, FromBB);
10953 CurMapType->addIncoming(MemberMapType, ToElseBB);
10954
10955 // Propagate map-type-modifying bits from the outer map clause to each map
10956 // inserted by the mapper.
10957 //
10958 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10959 // list item from the map clause and to apply the clauses specified in the
10960 // declared mapper to the construct on which the map clause appears...
10961 // If any modifier with the map-type-modifying property appears in the map
10962 // clause then the effect is as if that modifier appears in each map clause
10963 // specified in the declared mapper.
10964 //
10965 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10966 //
10967 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10968 //
10969 // PRESENT is propagated only to entries that have an attach ptr
10970 // (HasAttachPtr): the pointee data, which occupies a different storage
10971 // block than the struct being mapped and so is not covered by the
10972 // present-check on the struct's own storage. A present modifier on the
10973 // outer clause must still require that pointee to be present on the device.
10974 //
10975 // This is gated on \p PropagatePresentToPointee (set by callers only for
10976 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
10977 // applying to the pointee: the spec committee confirmed the divergence
10978 // between the present "motion" modifier (to/from) and the present map-type
10979 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
10980 // so for 5.2 present is ignored for the pointee for both map and to/from.
10981 //
10982 // TODO: PRESENT should also be propagated to the struct's own members
10983 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
10984 // member triggers the present-check. We cannot do that yet: while pointer
10985 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
10986 // the whole struct (including the pointer's storage), so propagating
10987 // PRESENT to it would wrongly require the pointer's pointee to be present.
10988 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
10989 // attach-style maps throughout.
10990 uint64_t ModifierBits =
10991 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10992 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
10993 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
10994 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
10995 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
10996 ModifierBits |=
10997 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10998 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
10999 Value *ImportedModifierBits =
11000 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
11001 Value *CurMapTypeWithModifiers = Builder.CreateOr(
11002 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
11003
11004 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
11005 // reserved for the attach(always) map-type modifier, and other modifier
11006 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
11007 Value *FinalMapType =
11008 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
11009
11010 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
11011 CurSizeArg, FinalMapType, CurNameArg};
11012
11013 auto ChildMapperFn = CustomMapperCB(I);
11014 if (!ChildMapperFn)
11015 return ChildMapperFn.takeError();
11016 if (*ChildMapperFn) {
11017 // Call the corresponding mapper function.
11018 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
11019 ->setDoesNotThrow();
11020 } else {
11021 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
11022 // data structure.
11024 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
11025 OffloadingArgs);
11026 }
11027 }
11028
11029 // Update the pointer to point to the next element that needs to be mapped,
11030 // and check whether we have mapped all elements.
11031 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
11032 "omp.arraymap.next");
11033 PtrPHI->addIncoming(PtrNext, LastBB);
11034 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
11035 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
11036 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
11037
11038 emitBlock(ExitBB, MapperFn);
11039 // Emit array deletion if this is an array section and \p MapType indicates
11040 // that deletion is required.
11041 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
11042 MapType, MapName, ElementSize, DoneBB,
11043 /*IsInit=*/false);
11044
11045 // Emit the function exit block.
11046 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
11047
11048 Builder.CreateRetVoid();
11049 return MapperFn;
11050}
11051
11053 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
11054 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
11055 bool IsNonContiguous,
11056 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
11057
11058 // Reset the array information.
11059 Info.clearArrayInfo();
11060 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
11061
11062 if (Info.NumberOfPtrs == 0)
11063 return Error::success();
11064
11065 Builder.restoreIP(AllocaIP);
11066 // Detect if we have any capture size requiring runtime evaluation of the
11067 // size so that a constant array could be eventually used.
11068 ArrayType *PointerArrayType =
11069 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
11070
11071 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
11072 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
11073
11074 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11075 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11076 AllocaInst *MappersArray = Builder.CreateAlloca(
11077 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11078 Info.RTArgs.MappersArray = MappersArray;
11079
11080 // If we don't have any VLA types or other types that require runtime
11081 // evaluation, we can use a constant array for the map sizes, otherwise we
11082 // need to fill up the arrays as we do for the pointers.
11083 Type *Int64Ty = Builder.getInt64Ty();
11084 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11085 ConstantInt::get(Int64Ty, 0));
11086 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11087 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11088 bool IsNonContigEntry =
11089 IsNonContiguous &&
11090 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11091 CombinedInfo.Types[I] &
11092 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11093 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11094 // descriptor_dim records), not the byte size.
11095 if (IsNonContigEntry) {
11096 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11097 "Index must be in-bounds for NON_CONTIG Dims array");
11098 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11099 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11100 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11101 continue;
11102 }
11103 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11104 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11105 ConstSizes[I] = CI;
11106 continue;
11107 }
11108 }
11109 RuntimeSizes.set(I);
11110 }
11111
11112 if (RuntimeSizes.all()) {
11113 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11114 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11115 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11116 restoreIPandDebugLoc(Builder, CodeGenIP);
11117 } else {
11118 auto *SizesArrayInit = ConstantArray::get(
11119 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11120 std::string Name = createPlatformSpecificName({"offload_sizes"});
11121 auto *SizesArrayGbl =
11122 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11123 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11124 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11125
11126 if (!RuntimeSizes.any()) {
11127 Info.RTArgs.SizesArray = SizesArrayGbl;
11128 } else {
11129 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11130 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11131 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11132 AllocaInst *Buffer = Builder.CreateAlloca(
11133 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11134 Buffer->setAlignment(OffloadSizeAlign);
11135 restoreIPandDebugLoc(Builder, CodeGenIP);
11136 Builder.CreateMemCpy(
11137 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11138 SizesArrayGbl, OffloadSizeAlign,
11139 Builder.getIntN(
11140 IndexSize,
11141 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11142
11143 Info.RTArgs.SizesArray = Buffer;
11144 }
11145 restoreIPandDebugLoc(Builder, CodeGenIP);
11146 }
11147
11148 // The map types are always constant so we don't need to generate code to
11149 // fill arrays. Instead, we create an array constant.
11151 for (auto mapFlag : CombinedInfo.Types)
11152 Mapping.push_back(
11153 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11154 mapFlag));
11155 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11156 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11157 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11158
11159 // The information types are only built if provided.
11160 if (!CombinedInfo.Names.empty()) {
11161 auto *MapNamesArrayGbl = createOffloadMapnames(
11162 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11163 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11164 Info.EmitDebug = true;
11165 } else {
11166 Info.RTArgs.MapNamesArray =
11168 Info.EmitDebug = false;
11169 }
11170
11171 // If there's a present map type modifier, it must not be applied to the end
11172 // of a region, so generate a separate map type array in that case.
11173 if (Info.separateBeginEndCalls()) {
11174 bool EndMapTypesDiffer = false;
11175 for (uint64_t &Type : Mapping) {
11176 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11177 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11178 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11179 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11180 EndMapTypesDiffer = true;
11181 }
11182 }
11183 if (EndMapTypesDiffer) {
11184 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11185 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11186 }
11187 }
11188
11189 PointerType *PtrTy = Builder.getPtrTy();
11190 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11191 Value *BPVal = CombinedInfo.BasePointers[I];
11192 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11193 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11194 0, I);
11195 Builder.CreateAlignedStore(BPVal, BP,
11196 M.getDataLayout().getPrefTypeAlign(PtrTy));
11197
11198 if (Info.requiresDevicePointerInfo()) {
11199 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11200 CodeGenIP = Builder.saveIP();
11201 Builder.restoreIP(AllocaIP);
11202 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11203 restoreIPandDebugLoc(Builder, CodeGenIP);
11204 if (DeviceAddrCB)
11205 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11206 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11207 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11208 if (DeviceAddrCB)
11209 DeviceAddrCB(I, BP);
11210 }
11211 }
11212
11213 Value *PVal = CombinedInfo.Pointers[I];
11214 Value *P = Builder.CreateConstInBoundsGEP2_32(
11215 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11216 I);
11217 // TODO: Check alignment correct.
11218 Builder.CreateAlignedStore(PVal, P,
11219 M.getDataLayout().getPrefTypeAlign(PtrTy));
11220
11221 if (RuntimeSizes.test(I)) {
11222 Value *S = Builder.CreateConstInBoundsGEP2_32(
11223 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11224 /*Idx0=*/0,
11225 /*Idx1=*/I);
11226 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11227 Int64Ty,
11228 /*isSigned=*/true),
11229 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11230 }
11231 // Fill up the mapper array.
11232 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11233 Value *MFunc = ConstantPointerNull::get(PtrTy);
11234
11235 auto CustomMFunc = CustomMapperCB(I);
11236 if (!CustomMFunc)
11237 return CustomMFunc.takeError();
11238 if (*CustomMFunc)
11239 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11240
11241 Value *MAddr = Builder.CreateInBoundsGEP(
11242 PointerArrayType, MappersArray,
11243 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11244 Builder.CreateAlignedStore(
11245 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11246 }
11247
11248 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11249 Info.NumberOfPtrs == 0)
11250 return Error::success();
11251 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11252 return Error::success();
11253}
11254
11256 BasicBlock *CurBB = Builder.GetInsertBlock();
11257
11258 if (!CurBB || CurBB->hasTerminator()) {
11259 // If there is no insert point or the previous block is already
11260 // terminated, don't touch it.
11261 } else {
11262 // Otherwise, create a fall-through branch.
11263 Builder.CreateBr(Target);
11264 }
11265
11266 Builder.ClearInsertionPoint();
11267}
11268
11270 bool IsFinished) {
11271 BasicBlock *CurBB = Builder.GetInsertBlock();
11272
11273 // Fall out of the current block (if necessary).
11274 emitBranch(BB);
11275
11276 if (IsFinished && BB->use_empty()) {
11277 BB->eraseFromParent();
11278 return;
11279 }
11280
11281 // Place the block after the current block, if possible, or else at
11282 // the end of the function.
11283 if (CurBB && CurBB->getParent())
11284 CurFn->insert(std::next(CurBB->getIterator()), BB);
11285 else
11286 CurFn->insert(CurFn->end(), BB);
11287 Builder.SetInsertPoint(BB);
11288}
11289
11291 BodyGenCallbackTy ElseGen,
11292 InsertPointTy AllocaIP,
11293 ArrayRef<BasicBlock *> DeallocBlocks) {
11294 // If the condition constant folds and can be elided, try to avoid emitting
11295 // the condition and the dead arm of the if/else.
11296 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11297 auto CondConstant = CI->getSExtValue();
11298 if (CondConstant)
11299 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11300
11301 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11302 }
11303
11304 Function *CurFn = Builder.GetInsertBlock()->getParent();
11305
11306 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11307 // emit the conditional branch.
11308 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11309 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11310 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11311 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11312 // Emit the 'then' code.
11313 emitBlock(ThenBlock, CurFn);
11314 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11315 return Err;
11316 emitBranch(ContBlock);
11317 // Emit the 'else' code if present.
11318 // There is no need to emit line number for unconditional branch.
11319 emitBlock(ElseBlock, CurFn);
11320 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11321 return Err;
11322 // There is no need to emit line number for unconditional branch.
11323 emitBranch(ContBlock);
11324 // Emit the continuation block for code after the if.
11325 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11326 return Error::success();
11327}
11328
11329bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11330 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11333 "Unexpected Atomic Ordering.");
11334
11335 bool Flush = false;
11337
11338 switch (AK) {
11339 case Read:
11342 FlushAO = AtomicOrdering::Acquire;
11343 Flush = true;
11344 }
11345 break;
11346 case Write:
11347 case Compare:
11348 case Update:
11351 FlushAO = AtomicOrdering::Release;
11352 Flush = true;
11353 }
11354 break;
11355 case Capture:
11356 switch (AO) {
11358 FlushAO = AtomicOrdering::Acquire;
11359 Flush = true;
11360 break;
11362 FlushAO = AtomicOrdering::Release;
11363 Flush = true;
11364 break;
11368 Flush = true;
11369 break;
11370 default:
11371 // do nothing - leave silently.
11372 break;
11373 }
11374 }
11375
11376 if (Flush) {
11377 // Currently Flush RT call still doesn't take memory_ordering, so for when
11378 // that happens, this tries to do the resolution of which atomic ordering
11379 // to use with but issue the flush call
11380 // TODO: pass `FlushAO` after memory ordering support is added
11381 (void)FlushAO;
11382 emitFlush(Loc);
11383 }
11384
11385 // for AO == AtomicOrdering::Monotonic and all other case combinations
11386 // do nothing
11387 return Flush;
11388}
11389
11393 AtomicOrdering AO, InsertPointTy AllocaIP) {
11394 if (!updateToLocation(Loc))
11395 return Loc.IP;
11396
11397 assert(X.Var->getType()->isPointerTy() &&
11398 "OMP Atomic expects a pointer to target memory");
11399 Type *XElemTy = X.ElemTy;
11400 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11401 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11402 "OMP atomic read expected a scalar type");
11403
11404 Value *XRead = nullptr;
11405
11406 if (XElemTy->isIntegerTy()) {
11407 LoadInst *XLD =
11408 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11409 XLD->setAtomic(AO);
11410 XRead = cast<Value>(XLD);
11411 } else if (XElemTy->isStructTy()) {
11412 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11413 // target does not support `atomicrmw` of the size of the struct
11414 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11415 OldVal->setAtomic(AO);
11416 const DataLayout &DL = OldVal->getDataLayout();
11417 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11418 OpenMPIRBuilder::AtomicInfo atomicInfo(
11419 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11420 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11421 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11422 XRead = AtomicLoadRes.first;
11423 OldVal->eraseFromParent();
11424 } else {
11425 // We need to perform atomic op as integer
11426 IntegerType *IntCastTy =
11427 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11428 LoadInst *XLoad =
11429 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11430 XLoad->setAtomic(AO);
11431 if (XElemTy->isFloatingPointTy()) {
11432 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11433 } else {
11434 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11435 }
11436 }
11437 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11438 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11439 return Builder.saveIP();
11440}
11441
11444 AtomicOpValue &X, Value *Expr,
11445 AtomicOrdering AO, InsertPointTy AllocaIP) {
11446 if (!updateToLocation(Loc))
11447 return Loc.IP;
11448
11449 assert(X.Var->getType()->isPointerTy() &&
11450 "OMP Atomic expects a pointer to target memory");
11451 Type *XElemTy = X.ElemTy;
11452 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11453 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11454 "OMP atomic write expected a scalar type");
11455
11456 if (XElemTy->isIntegerTy()) {
11457 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11458 XSt->setAtomic(AO);
11459 } else if (XElemTy->isStructTy()) {
11460 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11461 const DataLayout &DL = OldVal->getDataLayout();
11462 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11463 OpenMPIRBuilder::AtomicInfo atomicInfo(
11464 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11465 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11466 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11467 OldVal->eraseFromParent();
11468 } else {
11469 // We need to bitcast and perform atomic op as integers
11470 IntegerType *IntCastTy =
11471 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11472 Value *ExprCast =
11473 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11474 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11475 XSt->setAtomic(AO);
11476 }
11477
11478 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11479 return Builder.saveIP();
11480}
11481
11484 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11485 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11486 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11487 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11488 if (!updateToLocation(Loc))
11489 return Loc.IP;
11490
11491 LLVM_DEBUG({
11492 Type *XTy = X.Var->getType();
11493 assert(XTy->isPointerTy() &&
11494 "OMP Atomic expects a pointer to target memory");
11495 Type *XElemTy = X.ElemTy;
11496 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11497 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11498 "OMP atomic update expected a scalar or struct type");
11499 });
11500
11501 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11502 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11503 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11504 if (!AtomicResult)
11505 return AtomicResult.takeError();
11506 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11507 return Builder.saveIP();
11508}
11509
11510// FIXME: Duplicating AtomicExpand
11511Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11512 AtomicRMWInst::BinOp RMWOp) {
11513 switch (RMWOp) {
11514 case AtomicRMWInst::Add:
11515 return Builder.CreateAdd(Src1, Src2);
11516 case AtomicRMWInst::Sub:
11517 return Builder.CreateSub(Src1, Src2);
11518 case AtomicRMWInst::And:
11519 return Builder.CreateAnd(Src1, Src2);
11521 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11522 case AtomicRMWInst::Or:
11523 return Builder.CreateOr(Src1, Src2);
11524 case AtomicRMWInst::Xor:
11525 return Builder.CreateXor(Src1, Src2);
11526 case AtomicRMWInst::Max:
11527 return Builder.CreateBinaryIntrinsic(Intrinsic::smax, Src1, Src2);
11528 case AtomicRMWInst::Min:
11529 return Builder.CreateBinaryIntrinsic(Intrinsic::smin, Src1, Src2);
11531 return Builder.CreateBinaryIntrinsic(Intrinsic::umax, Src1, Src2);
11533 return Builder.CreateBinaryIntrinsic(Intrinsic::umin, Src1, Src2);
11535 return Builder.CreateFAdd(Src1, Src2);
11537 return Builder.CreateFSub(Src1, Src2);
11539 return Builder.CreateMaxNum(Src1, Src2);
11541 return Builder.CreateMinNum(Src1, Src2);
11543 return Builder.CreateMaximum(Src1, Src2);
11545 return Builder.CreateMinimum(Src1, Src2);
11554 llvm_unreachable("Unsupported atomic update operation");
11555 }
11556 llvm_unreachable("Unsupported atomic update operation");
11557}
11558
11560 // Loads cannot use Release or AcquireRelease ordering. This load is
11561 // just the initial value for the cmpxchg loop; the cmpxchg itself
11562 // retains the original ordering.
11563 AtomicOrdering LoadAO = AO;
11564
11565 if (AO == AtomicOrdering::Release) {
11567 } else if (AO == AtomicOrdering::AcquireRelease) {
11568 LoadAO = AtomicOrdering::Acquire;
11569 }
11570
11571 return LoadAO;
11572}
11573
11574Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11575 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11577 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11578 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11579 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11580 bool emitRMWOp = false;
11581 switch (RMWOp) {
11582 case AtomicRMWInst::Add:
11583 case AtomicRMWInst::And:
11585 case AtomicRMWInst::Or:
11586 case AtomicRMWInst::Xor:
11588 case AtomicRMWInst::Max:
11589 case AtomicRMWInst::Min:
11597 emitRMWOp = XElemTy;
11598 break;
11599 case AtomicRMWInst::Sub:
11601 emitRMWOp = (IsXBinopExpr && XElemTy);
11602 break;
11603 default:
11604 emitRMWOp = false;
11605 }
11606 emitRMWOp &=
11607 (XElemTy->isIntegerTy() || XElemTy->isFloatTy() || XElemTy->isDoubleTy());
11608
11609 std::pair<Value *, Value *> Res;
11610 if (emitRMWOp) {
11611 AtomicRMWInst *RMWInst =
11612 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11613 if (IsIgnoreDenormalMode)
11614 RMWInst->setMetadata(llvm::LLVMContext::MD_atomic_ignore_denormal_mode,
11615 llvm::MDNode::get(Builder.getContext(), {}));
11616 if (T.isAMDGPU()) {
11617 if (!IsFineGrainedMemory)
11618 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11619 llvm::MDNode::get(Builder.getContext(), {}));
11620 if (!IsRemoteMemory)
11621 RMWInst->setMetadata("amdgpu.no.remote.memory",
11622 llvm::MDNode::get(Builder.getContext(), {}));
11623 }
11624 Res.first = RMWInst;
11625 // not needed except in case of postfix captures. Generate anyway for
11626 // consistency with the else part. Will be removed with any DCE pass.
11627 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11628 if (RMWOp == AtomicRMWInst::Xchg)
11629 Res.second = Res.first;
11630 else
11631 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11632 } else if (XElemTy->isStructTy()) {
11633 LoadInst *OldVal =
11634 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11636 OldVal->setAtomic(LoadAO);
11637 const DataLayout &LoadDL = OldVal->getDataLayout();
11638 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11639
11640 OpenMPIRBuilder::AtomicInfo atomicInfo(
11641 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11642 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11643 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11644 BasicBlock *CurBB = Builder.GetInsertBlock();
11645 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11646 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11647 BasicBlock *ExitBB =
11648 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11649 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11650 X->getName() + ".atomic.cont");
11651 ContBB->getTerminator()->eraseFromParent();
11652 Builder.restoreIP(AllocaIP);
11653 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11654 NewAtomicAddr->setName(X->getName() + "x.new.val");
11655 Builder.SetInsertPoint(ContBB);
11656 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11657 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11658 Value *OldExprVal = PHI;
11659 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11660 if (!CBResult)
11661 return CBResult.takeError();
11662 Value *Upd = *CBResult;
11663 Builder.CreateStore(Upd, NewAtomicAddr);
11666 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11667 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11668 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11669 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11670 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11671 OldVal->eraseFromParent();
11672 Res.first = OldExprVal;
11673 Res.second = Upd;
11674
11675 if (UnreachableInst *ExitTI =
11677 CurBBTI->eraseFromParent();
11678 Builder.SetInsertPoint(ExitBB);
11679 } else {
11680 Builder.SetInsertPoint(ExitTI);
11681 }
11682 } else {
11683 IntegerType *IntCastTy =
11684 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11685 LoadInst *OldVal =
11686 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11688 OldVal->setAtomic(LoadAO);
11689 // CurBB
11690 // | /---\
11691 // ContBB |
11692 // | \---/
11693 // ExitBB
11694 BasicBlock *CurBB = Builder.GetInsertBlock();
11695 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11696 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11697 BasicBlock *ExitBB =
11698 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11699 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11700 X->getName() + ".atomic.cont");
11701 ContBB->getTerminator()->eraseFromParent();
11702 Builder.restoreIP(AllocaIP);
11703 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11704 NewAtomicAddr->setName(X->getName() + "x.new.val");
11705 Builder.SetInsertPoint(ContBB);
11706 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11707 PHI->addIncoming(OldVal, CurBB);
11708 bool IsIntTy = XElemTy->isIntegerTy();
11709 Value *OldExprVal = PHI;
11710 if (!IsIntTy) {
11711 if (XElemTy->isFloatingPointTy()) {
11712 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11713 X->getName() + ".atomic.fltCast");
11714 } else {
11715 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11716 X->getName() + ".atomic.ptrCast");
11717 }
11718 }
11719
11720 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11721 if (!CBResult)
11722 return CBResult.takeError();
11723 Value *Upd = *CBResult;
11724 Builder.CreateStore(Upd, NewAtomicAddr);
11725 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11728 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11729 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11730 Result->setVolatile(VolatileX);
11731 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11732 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11733 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11734 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11735
11736 Res.first = OldExprVal;
11737 Res.second = Upd;
11738
11739 // set Insertion point in exit block
11740 if (UnreachableInst *ExitTI =
11742 CurBBTI->eraseFromParent();
11743 Builder.SetInsertPoint(ExitBB);
11744 } else {
11745 Builder.SetInsertPoint(ExitTI);
11746 }
11747 }
11748
11749 return Res;
11750}
11751
11754 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11755 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11756 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11757 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11758 if (!updateToLocation(Loc))
11759 return Loc.IP;
11760
11761 LLVM_DEBUG({
11762 Type *XTy = X.Var->getType();
11763 assert(XTy->isPointerTy() &&
11764 "OMP Atomic expects a pointer to target memory");
11765 Type *XElemTy = X.ElemTy;
11766 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11767 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11768 "OMP atomic capture expected a scalar or struct type");
11769 });
11770
11771 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11772 // 'x' is simply atomically rewritten with 'expr'.
11773 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11774 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11775 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11776 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11777 if (!AtomicResult)
11778 return AtomicResult.takeError();
11779 Value *CapturedVal =
11780 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11781 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11782
11783 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11784 return Builder.saveIP();
11785}
11786
11790 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11791 bool IsFailOnly, bool IsWeak) {
11792
11794 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11795 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11796}
11797
11801 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11802 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11803
11804 if (!updateToLocation(Loc))
11805 return Loc.IP;
11806
11807 assert(X.Var->getType()->isPointerTy() &&
11808 "OMP atomic expects a pointer to target memory");
11809 // compare capture
11810 if (V.Var) {
11811 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11812 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11813 }
11814
11815 bool IsInteger = E->getType()->isIntegerTy();
11816
11817 if (Op == OMPAtomicCompareOp::EQ) {
11818 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11819 // R.Var handling.
11820 Value *OldValue = nullptr;
11821 Value *SuccessOrFail = nullptr;
11822
11823 if (!IsInteger && HandleFPNegZero) {
11824 // IEEE 754 special cases for cmpxchg (which is bitwise):
11825 // 1. -0.0 == +0.0 but they have different bit patterns.
11826 // 2. NaN != NaN but identical NaN bit patterns would match.
11827 //
11828 // CurBB:
11829 // %e_int = bitcast E to intN
11830 // %d_int = bitcast D to intN
11831 // %x_curr = load atomic intN, X
11832 // %x_fp = bitcast %x_curr to FP
11833 // %e_is_nan = fcmp uno E, E
11834 // %x_is_nan = fcmp uno %x_fp, %x_fp
11835 // %either_nan = or %e_is_nan, %x_is_nan
11836 // br %either_nan, NaNBB, NotNaNBB
11837 // NaNBB: ; NaN == anything is always false
11838 // br ExitBB
11839 // NotNaNBB:
11840 // %x_is_zero = fcmp oeq %x_fp, 0.0
11841 // %e_is_zero = fcmp oeq E, 0.0
11842 // %both_zero = and %x_is_zero, %e_is_zero
11843 // br %both_zero, ZeroBB, NormalBB
11844 // ZeroBB: ; both ±0.0 → x = d
11845 // cmpxchg X, %x_curr, %d_int
11846 // br ExitBB
11847 // NormalBB: ; original path
11848 // cmpxchg X, %e_int, %d_int
11849 // br ExitBB
11850 // ExitBB:
11851 // phi merge
11852 IntegerType *IntCastTy =
11853 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11854 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11855 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11856
11857 // Load X atomically.
11858 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11859 X.Var->getName() + ".atomic.load");
11861 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11862
11863 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11864 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11865 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11866 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11867 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11868
11869 BasicBlock *CurBB = Builder.GetInsertBlock();
11870 Function *F = CurBB->getParent();
11871 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11872 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11873 BasicBlock *ExitBB =
11874 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11876 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11877 BasicBlock *NotNaNBB = BasicBlock::Create(
11878 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11880 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11881 BasicBlock *NormalBB = BasicBlock::Create(
11882 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11883
11884 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11885 CurBB->getTerminator()->eraseFromParent();
11886 Builder.SetInsertPoint(CurBB);
11887 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11888
11889 // NaNBB: NaN == anything is always false; skip cmpxchg.
11890 Builder.SetInsertPoint(NaNBB);
11891 Builder.CreateBr(ExitBB);
11892
11893 // NotNaNBB: check both X and E for ±0.0.
11894 Builder.SetInsertPoint(NotNaNBB);
11895 Value *XIsZero =
11896 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11897 X.Var->getName() + ".atomic.xiszero");
11898 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11899 "atomic.e.iszero");
11900 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11901 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11902
11903 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11904 Builder.SetInsertPoint(ZeroBB);
11905 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11906 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11907 ResZero->setWeak(IsWeak);
11908 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11909 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11910 Builder.CreateBr(ExitBB);
11911
11912 // NormalBB: original bitwise cmpxchg.
11913 Builder.SetInsertPoint(NormalBB);
11914 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11915 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11916 ResNormal->setWeak(IsWeak);
11917 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11918 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11919 Builder.CreateBr(ExitBB);
11920
11921 // ExitBB: merge results from NaN, Zero, and Normal paths.
11922 Builder.SetInsertPoint(ExitBB->begin());
11923 PHINode *OldIntPHI =
11924 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11925 OldIntPHI->addIncoming(XCurr, NaNBB);
11926 OldIntPHI->addIncoming(OldZero, ZeroBB);
11927 OldIntPHI->addIncoming(OldNormal, NormalBB);
11928 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11929 X.Var->getName() + ".atomic.ok");
11930 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11931 SuccessPHI->addIncoming(OkZero, ZeroBB);
11932 SuccessPHI->addIncoming(OkNormal, NormalBB);
11933
11934 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11935 CurBBTI->eraseFromParent();
11936 Builder.SetInsertPoint(ExitBB);
11937 } else {
11938 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11939 }
11940
11941 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11942 X.Var->getName() + ".atomic.old.fp");
11943 SuccessOrFail = SuccessPHI;
11944 } else {
11945 AtomicCmpXchgInst *Result = nullptr;
11946 if (!IsInteger) {
11947 IntegerType *IntCastTy =
11948 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11949 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11950 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11951 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11952 MaybeAlign(), AO, Failure);
11953 } else {
11954 Result =
11955 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11956 }
11957 Result->setWeak(IsWeak);
11958
11959 if (V.Var) {
11960 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11961 if (!IsInteger)
11962 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
11963 assert(OldValue->getType() == V.ElemTy &&
11964 "OldValue and V must be of same type");
11965 if (IsPostfixUpdate) {
11966 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
11967 } else {
11968 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11969 if (IsFailOnly) {
11970 BasicBlock *CurBB = Builder.GetInsertBlock();
11971 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11972 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11973 BasicBlock *ExitBB = CurBB->splitBasicBlock(
11974 CurBBTI, X.Var->getName() + ".atomic.exit");
11975 BasicBlock *ContBB = CurBB->splitBasicBlock(
11976 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
11977 ContBB->getTerminator()->eraseFromParent();
11978 CurBB->getTerminator()->eraseFromParent();
11979
11980 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
11981
11982 Builder.SetInsertPoint(ContBB);
11983 Builder.CreateStore(OldValue, V.Var);
11984 Builder.CreateBr(ExitBB);
11985
11986 if (UnreachableInst *ExitTI =
11988 CurBBTI->eraseFromParent();
11989 Builder.SetInsertPoint(ExitBB);
11990 } else {
11991 Builder.SetInsertPoint(ExitTI);
11992 }
11993 } else {
11994 Value *CapturedValue =
11995 Builder.CreateSelect(SuccessOrFail, E, OldValue);
11996 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
11997 }
11998 }
11999 }
12000 // The comparison result has to be stored.
12001 if (R.Var) {
12002 assert(R.Var->getType()->isPointerTy() &&
12003 "r.var must be of pointer type");
12004 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12005
12006 Value *SuccessFailureVal =
12007 Builder.CreateExtractValue(Result, /*Idxs=*/1);
12008 Value *ResultCast =
12009 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
12010 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
12011 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12012 }
12013 }
12014
12015 // For the HandleFPNegZero path, handle V.Var and R.Var using the
12016 // pre-computed OldValue and SuccessOrFail.
12017 if (HandleFPNegZero && !IsInteger) {
12018 if (V.Var) {
12019 assert(OldValue->getType() == V.ElemTy &&
12020 "OldValue and V must be of same type");
12021 if (IsPostfixUpdate) {
12022 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
12023 } else {
12024 if (IsFailOnly) {
12025 BasicBlock *CurBB = Builder.GetInsertBlock();
12026 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
12027 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
12028 BasicBlock *ExitBB = CurBB->splitBasicBlock(
12029 CurBBTI, X.Var->getName() + ".atomic.exit");
12030 BasicBlock *ContBB = CurBB->splitBasicBlock(
12031 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
12032 ContBB->getTerminator()->eraseFromParent();
12033 CurBB->getTerminator()->eraseFromParent();
12034
12035 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
12036
12037 Builder.SetInsertPoint(ContBB);
12038 Builder.CreateStore(OldValue, V.Var);
12039 Builder.CreateBr(ExitBB);
12040
12041 if (UnreachableInst *ExitTI =
12043 CurBBTI->eraseFromParent();
12044 Builder.SetInsertPoint(ExitBB);
12045 } else {
12046 Builder.SetInsertPoint(ExitTI);
12047 }
12048 } else {
12049 Value *CapturedValue =
12050 Builder.CreateSelect(SuccessOrFail, E, OldValue);
12051 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12052 }
12053 }
12054 }
12055 // The comparison result has to be stored.
12056 if (R.Var) {
12057 assert(R.Var->getType()->isPointerTy() &&
12058 "r.var must be of pointer type");
12059 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12060
12061 Value *ResultCast = R.IsSigned
12062 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
12063 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
12064 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12065 }
12066 }
12067 } else {
12068 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
12069 "Op should be either max or min at this point");
12070 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
12071
12072 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
12073 // Let's take max as example.
12074 // OpenMP form:
12075 // x = x > expr ? expr : x;
12076 // LLVM form:
12077 // *ptr = *ptr > val ? *ptr : val;
12078 // We need to transform to LLVM form.
12079 // x = x <= expr ? x : expr;
12081 if (IsXBinopExpr) {
12082 if (IsInteger) {
12083 if (X.IsSigned)
12084 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
12086 else
12087 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
12089 } else {
12090 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12092 }
12093 } else {
12094 if (IsInteger) {
12095 if (X.IsSigned)
12096 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12098 else
12099 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12101 } else {
12102 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12104 }
12105 }
12106
12107 AtomicRMWInst *OldValue =
12108 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12109 if (V.Var) {
12110 Value *CapturedValue = nullptr;
12111 if (IsPostfixUpdate) {
12112 CapturedValue = OldValue;
12113 } else {
12114 CmpInst::Predicate Pred;
12115 switch (NewOp) {
12116 case AtomicRMWInst::Max:
12117 Pred = CmpInst::ICMP_SGT;
12118 break;
12120 Pred = CmpInst::ICMP_UGT;
12121 break;
12123 Pred = CmpInst::FCMP_OGT;
12124 break;
12125 case AtomicRMWInst::Min:
12126 Pred = CmpInst::ICMP_SLT;
12127 break;
12129 Pred = CmpInst::ICMP_ULT;
12130 break;
12132 Pred = CmpInst::FCMP_OLT;
12133 break;
12134 default:
12135 llvm_unreachable("unexpected comparison op");
12136 }
12137 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12138 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12139 }
12140 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12141 }
12142 }
12143
12144 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12145
12146 return Builder.saveIP();
12147}
12148
12151 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12152 Value *NumTeamsUpper, Value *ThreadLimit,
12153 Value *IfExpr) {
12154 if (!updateToLocation(Loc))
12155 return InsertPointTy();
12156
12157 uint32_t SrcLocStrSize;
12158 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12159 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12160 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12161
12162 // Outer allocation basicblock is the entry block of the current function.
12163 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12164 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12165 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12166 Builder.SetInsertPoint(BodyBB->begin());
12167 }
12168
12169 // The current basic block is split into four basic blocks. After outlining,
12170 // they will be mapped as follows:
12171 // ```
12172 // def current_fn() {
12173 // current_basic_block:
12174 // br label %teams.exit
12175 // teams.exit:
12176 // ; instructions after teams
12177 // }
12178 //
12179 // def outlined_fn() {
12180 // teams.alloca:
12181 // br label %teams.body
12182 // teams.body:
12183 // ; instructions within teams body
12184 // }
12185 // ```
12186 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12187 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12188 BasicBlock *AllocaBB =
12189 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12190
12191 bool SubClausesPresent =
12192 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12193 // Push num_teams
12194 if (!Config.isTargetDevice() && SubClausesPresent) {
12195 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12196 "if lowerbound is non-null, then upperbound must also be non-null "
12197 "for bounds on num_teams");
12198
12199 if (NumTeamsUpper == nullptr)
12200 NumTeamsUpper = Builder.getInt32(0);
12201
12202 if (NumTeamsLower == nullptr)
12203 NumTeamsLower = NumTeamsUpper;
12204
12205 if (IfExpr) {
12206 assert(IfExpr->getType()->isIntegerTy() &&
12207 "argument to if clause must be an integer value");
12208
12209 // upper = ifexpr ? upper : 1
12210 if (IfExpr->getType() != Int1)
12211 IfExpr = Builder.CreateICmpNE(IfExpr,
12212 ConstantInt::get(IfExpr->getType(), 0));
12213 NumTeamsUpper = Builder.CreateSelect(
12214 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12215
12216 // lower = ifexpr ? lower : 1
12217 NumTeamsLower = Builder.CreateSelect(
12218 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12219 }
12220
12221 if (ThreadLimit == nullptr)
12222 ThreadLimit = Builder.getInt32(0);
12223
12224 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12225 // truncate or sign extend the passed values to match the int32 parameters.
12226 Value *NumTeamsLowerInt32 =
12227 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12228 Value *NumTeamsUpperInt32 =
12229 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12230 Value *ThreadLimitInt32 =
12231 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12232
12233 Value *ThreadNum = getOrCreateThreadID(Ident);
12234
12236 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12237 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12238 ThreadLimitInt32});
12239 }
12240 // Generate the body of teams.
12241 InsertPointTy AllocaIP(AllocaBB->begin());
12242 InsertPointTy CodeGenIP(BodyBB->begin());
12243 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12244 return Err;
12245
12246 auto OI = std::make_unique<OutlineInfo>();
12247 OI->EntryBB = AllocaBB;
12248 OI->ExitBB = ExitBB;
12249 OI->OuterAllocBB = &OuterAllocaBB;
12250
12251 // Insert fake values for global tid and bound tid.
12253 InsertPointTy OuterAllocaIP(OuterAllocaBB.begin());
12254 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12255 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12256 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12257 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12258
12259 auto HostPostOutlineCB = [this, Ident,
12260 ToBeDeleted](Function &OutlinedFn) mutable {
12261 // The stale call instruction will be replaced with a new call instruction
12262 // for runtime call with the outlined function.
12263
12264 assert(OutlinedFn.hasOneUse() &&
12265 "there must be a single user for the outlined function");
12266 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12267 ToBeDeleted.push_back(StaleCI);
12268
12269 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12270 "Outlined function must have two or three arguments only");
12271
12272 bool HasShared = OutlinedFn.arg_size() == 3;
12273
12274 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12275 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12276 if (HasShared)
12277 OutlinedFn.getArg(2)->setName("data");
12278
12279 // Call to the runtime function for teams in the current function.
12280 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12281 "outlined function.");
12282 Builder.SetInsertPoint(StaleCI);
12283 SmallVector<Value *> Args = {
12284 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12285 if (HasShared)
12286 Args.push_back(StaleCI->getArgOperand(2));
12289 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12290 Args);
12291
12292 Builder.ClearInsertionPoint();
12293 for (Instruction *I : llvm::reverse(ToBeDeleted))
12294 I->eraseFromParent();
12295 };
12296
12297 if (!Config.isTargetDevice())
12298 OI->PostOutlineCB = HostPostOutlineCB;
12299
12300 addOutlineInfo(std::move(OI));
12301
12302 Builder.SetInsertPoint(ExitBB);
12303
12304 return Builder.saveIP();
12305}
12306
12308 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12309 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12310 if (!updateToLocation(Loc))
12311 return InsertPointTy();
12312
12313 BasicBlock *OuterAllocaBB = OuterAllocIP.getNodeParent();
12314
12315 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12316 BasicBlock *BodyBB =
12317 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12318 Builder.SetInsertPoint(BodyBB->begin());
12319 }
12320 BasicBlock *ExitBB =
12321 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12322 BasicBlock *BodyBB =
12323 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12324 BasicBlock *AllocaBB =
12325 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12326
12327 // Generate the body of distribute clause
12328 InsertPointTy AllocaIP(AllocaBB->begin());
12329 InsertPointTy CodeGenIP(BodyBB->begin());
12330 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12331 return Err;
12332
12333 // When using target we use different runtime functions which require a
12334 // callback.
12335 if (Config.isTargetDevice()) {
12336 auto OI = std::make_unique<OutlineInfo>();
12337 OI->OuterAllocBB = OuterAllocIP.getNodeParent();
12338 OI->EntryBB = AllocaBB;
12339 OI->ExitBB = ExitBB;
12340 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12341 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12342
12343 addOutlineInfo(std::move(OI));
12344 }
12345 Builder.SetInsertPoint(ExitBB);
12346
12347 return Builder.saveIP();
12348}
12349
12352 std::string VarName) {
12353 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12355 Names.size()),
12356 Names);
12357 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12358 M, MapNamesArrayInit->getType(),
12359 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12360 VarName);
12361 return MapNamesArrayGlobal;
12362}
12363
12364// Create all simple and struct types exposed by the runtime and remember
12365// the llvm::PointerTypes of them for easy access later.
12366void OpenMPIRBuilder::initializeTypes(Module &M) {
12367 LLVMContext &Ctx = M.getContext();
12368 StructType *T;
12369 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12370 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12371#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12372#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12373 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12374 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12375#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12376 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12377 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12378#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12379 T = StructType::getTypeByName(Ctx, StructName); \
12380 if (!T) \
12381 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12382 VarName = T; \
12383 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12384#include "llvm/Frontend/OpenMP/OMPKinds.def"
12385}
12386
12389 SmallVectorImpl<BasicBlock *> &BlockVector) {
12391 BlockSet.insert(EntryBB);
12392 BlockSet.insert(ExitBB);
12393
12394 Worklist.push_back(EntryBB);
12395 while (!Worklist.empty()) {
12396 BasicBlock *BB = Worklist.pop_back_val();
12397 BlockVector.push_back(BB);
12398 for (BasicBlock *SuccBB : successors(BB))
12399 if (BlockSet.insert(SuccBB).second)
12400 Worklist.push_back(SuccBB);
12401 }
12402}
12403
12404std::unique_ptr<CodeExtractor>
12406 bool ArgsInZeroAddressSpace,
12407 Twine Suffix) {
12408 return std::make_unique<CodeExtractor>(
12409 Blocks, /* DominatorTree */ nullptr,
12410 /* AggregateArgs */ true,
12411 /* BlockFrequencyInfo */ nullptr,
12412 /* BranchProbabilityInfo */ nullptr,
12413 /* AssumptionCache */ nullptr,
12414 /* AllowVarArgs */ true,
12415 /* AllowAlloca */ true,
12416 /* AllocationBlock*/ OuterAllocBB,
12417 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12418 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12419}
12420
12421std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12422 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12423 return std::make_unique<DeviceSharedMemCodeExtractor>(
12424 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12425 /* AggregateArgs */ true,
12426 /* BlockFrequencyInfo */ nullptr,
12427 /* BranchProbabilityInfo */ nullptr,
12428 /* AssumptionCache */ nullptr,
12429 /* AllowVarArgs */ true,
12430 /* AllowAlloca */ true,
12431 /* AllocationBlock*/ OuterAllocBB,
12432 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12434 : OuterDeallocBBs,
12435 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12436}
12437
12439 uint64_t Size, int32_t Flags,
12441 StringRef Name) {
12442 if (!Config.isGPU()) {
12445 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12446 return;
12447 }
12448 // TODO: Add support for global variables on the device after declare target
12449 // support.
12450 Function *Fn = dyn_cast<Function>(Addr);
12451 if (!Fn)
12452 return;
12453
12454 // Add a function attribute for the kernel.
12455 Fn->addFnAttr("kernel");
12456 if (T.isAMDGCN())
12457 Fn->addFnAttr("uniform-work-group-size");
12458 Fn->addFnAttr(Attribute::MustProgress);
12459}
12460
12461// We only generate metadata for function that contain target regions.
12464
12465 // If there are no entries, we don't need to do anything.
12466 if (OffloadInfoManager.empty())
12467 return;
12468
12469 LLVMContext &C = M.getContext();
12472 16>
12473 OrderedEntries(OffloadInfoManager.size());
12474
12475 // Auxiliary methods to create metadata values and strings.
12476 auto &&GetMDInt = [this](unsigned V) {
12477 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12478 };
12479
12480 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12481
12482 // Create the offloading info metadata node.
12483 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12484 auto &&TargetRegionMetadataEmitter =
12485 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12486 const TargetRegionEntryInfo &EntryInfo,
12488 // Generate metadata for target regions. Each entry of this metadata
12489 // contains:
12490 // - Entry 0 -> Kind of this type of metadata (0).
12491 // - Entry 1 -> Device ID of the file where the entry was identified.
12492 // - Entry 2 -> File ID of the file where the entry was identified.
12493 // - Entry 3 -> Mangled name of the function where the entry was
12494 // identified.
12495 // - Entry 4 -> Line in the file where the entry was identified.
12496 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12497 // - Entry 6 -> Order the entry was created.
12498 // The first element of the metadata node is the kind.
12499 Metadata *Ops[] = {
12500 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12501 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12502 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12503 GetMDInt(E.getOrder())};
12504
12505 // Save this entry in the right position of the ordered entries array.
12506 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12507
12508 // Add metadata to the named metadata node.
12509 MD->addOperand(MDNode::get(C, Ops));
12510 };
12511
12512 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12513
12514 // Create function that emits metadata for each device global variable entry;
12515 auto &&DeviceGlobalVarMetadataEmitter =
12516 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12517 StringRef MangledName,
12519 // Generate metadata for global variables. Each entry of this metadata
12520 // contains:
12521 // - Entry 0 -> Kind of this type of metadata (1).
12522 // - Entry 1 -> Mangled name of the variable.
12523 // - Entry 2 -> Declare target kind.
12524 // - Entry 3 -> Order the entry was created.
12525 // The first element of the metadata node is the kind.
12526 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12527 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12528
12529 // Save this entry in the right position of the ordered entries array.
12530 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12531 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12532
12533 // Add metadata to the named metadata node.
12534 MD->addOperand(MDNode::get(C, Ops));
12535 };
12536
12537 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12538 DeviceGlobalVarMetadataEmitter);
12539
12540 for (const auto &E : OrderedEntries) {
12541 assert(E.first && "All ordered entries must exist!");
12542 if (const auto *CE =
12544 E.first)) {
12545 if (!CE->getID() || !CE->getAddress()) {
12546 // Do not blame the entry if the parent funtion is not emitted.
12547 TargetRegionEntryInfo EntryInfo = E.second;
12548 StringRef FnName = EntryInfo.ParentName;
12549 if (!M.getNamedValue(FnName))
12550 continue;
12551 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12552 continue;
12553 }
12554 createOffloadEntry(CE->getID(), CE->getAddress(),
12555 /*Size=*/0, CE->getFlags(),
12557 } else if (const auto *CE = dyn_cast<
12559 E.first)) {
12562 CE->getFlags());
12563 switch (Flags) {
12566 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12567 continue;
12568 if (!CE->getAddress()) {
12569 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12570 continue;
12571 }
12572 // The vaiable has no definition - no need to add the entry.
12573 if (CE->getVarSize() == 0)
12574 continue;
12575 break;
12577 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12578 (!Config.isTargetDevice() && CE->getAddress())) &&
12579 "Declaret target link address is set.");
12580 if (Config.isTargetDevice())
12581 continue;
12582 if (!CE->getAddress()) {
12584 continue;
12585 }
12586 break;
12589 if (!CE->getAddress()) {
12590 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12591 continue;
12592 }
12593 break;
12594 default:
12595 break;
12596 }
12597
12598 // Hidden or internal symbols on the device are not externally visible.
12599 // We should not attempt to register them by creating an offloading
12600 // entry. Indirect variables are handled separately on the device.
12601 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12602 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12603 (Flags !=
12605 Flags != OffloadEntriesInfoManager::
12606 OMPTargetGlobalVarEntryIndirectVTable))
12607 continue;
12608
12609 // Indirect globals need to use a special name that doesn't match the name
12610 // of the associated host global.
12612 Flags ==
12614 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12615 Flags, CE->getLinkage(), CE->getVarName());
12616 else
12617 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12618 Flags, CE->getLinkage());
12619
12620 } else {
12621 llvm_unreachable("Unsupported entry kind.");
12622 }
12623 }
12624
12625 // Emit requires directive globals to a special entry so the runtime can
12626 // register them when the device image is loaded.
12627 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12628 // entries should be redesigned to better suit this use-case.
12629 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12633 ".requires", /*Size=*/0,
12635 Config.getRequiresFlags());
12636}
12637
12640 unsigned FileID, unsigned Line, unsigned Count) {
12641 raw_svector_ostream OS(Name);
12642 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12643 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12644 if (Count)
12645 OS << "_" << Count;
12646}
12647
12649 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12650 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12652 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12653 EntryInfo.Line, NewCount);
12654}
12655
12658 vfs::FileSystem &VFS,
12659 StringRef ParentName) {
12660 sys::fs::UniqueID ID(0xdeadf17e, 0);
12661 auto FileIDInfo = CallBack();
12662 uint64_t FileID = 0;
12663 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12664 ID = Status->getUniqueID();
12665 FileID = Status->getUniqueID().getFile();
12666 } else {
12667 // If the inode ID could not be determined, create a hash value
12668 // the current file name and use that as an ID.
12669 FileID = hash_value(std::get<0>(FileIDInfo));
12670 }
12671
12672 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12673 std::get<1>(FileIDInfo));
12674}
12675
12677 unsigned Offset = 0;
12678 for (uint64_t Remain =
12679 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12681 !(Remain & 1); Remain = Remain >> 1)
12682 Offset++;
12683 return Offset;
12684}
12685
12688 // Rotate by getFlagMemberOffset() bits.
12689 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12690 << getFlagMemberOffset());
12691}
12692
12695 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12696 // If the entry is PTR_AND_OBJ but has not been marked with the special
12697 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12698 // marked as MEMBER_OF.
12699 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12701 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12704 return;
12705
12706 // Entries with ATTACH are not members-of anything. They are handled
12707 // separately by the runtime after other maps have been handled.
12708 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12710 return;
12711
12712 // Reset the placeholder value to prepare the flag for the assignment of the
12713 // proper MEMBER_OF value.
12714 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12715 Flags |= MemberOfFlag;
12716}
12717
12721 bool IsDeclaration, bool IsExternallyVisible,
12722 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12723 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12724 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12725 std::function<Constant *()> GlobalInitializer,
12726 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12727 // TODO: convert this to utilise the IRBuilder Config rather than
12728 // a passed down argument.
12729 if (OpenMPSIMD)
12730 return nullptr;
12731
12734 CaptureClause ==
12736 Config.hasRequiresUnifiedSharedMemory())) {
12737 SmallString<64> PtrName;
12738 {
12739 raw_svector_ostream OS(PtrName);
12740 OS << MangledName;
12741 if (!IsExternallyVisible)
12742 OS << format("_%x", EntryInfo.FileID);
12743 OS << "_decl_tgt_ref_ptr";
12744 }
12745
12746 Value *Ptr = M.getNamedValue(PtrName);
12747
12748 if (!Ptr) {
12749 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12750 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12751
12752 auto *GV = cast<GlobalVariable>(Ptr);
12753 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12754
12755 if (!Config.isTargetDevice()) {
12756 if (GlobalInitializer)
12757 GV->setInitializer(GlobalInitializer());
12758 else
12759 GV->setInitializer(GlobalValue);
12760 }
12761
12763 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12764 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12765 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12766 }
12767
12768 return cast<Constant>(Ptr);
12769 }
12770
12771 return nullptr;
12772}
12773
12777 bool IsDeclaration, bool IsExternallyVisible,
12778 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12779 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12780 std::vector<Triple> TargetTriple,
12781 std::function<Constant *()> GlobalInitializer,
12782 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12783 Constant *Addr) {
12785 (TargetTriple.empty() && !Config.isTargetDevice()))
12786 return;
12787
12789 StringRef VarName;
12790 int64_t VarSize;
12792
12794 CaptureClause ==
12796 !Config.hasRequiresUnifiedSharedMemory()) {
12798 VarName = MangledName;
12799 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12800
12801 if (!IsDeclaration)
12802 VarSize = divideCeil(
12803 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12804 else
12805 VarSize = 0;
12806 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12807
12808 // This is a workaround carried over from Clang which prevents undesired
12809 // optimisation of internal variables.
12810 if (Config.isTargetDevice() &&
12811 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12812 // Do not create a "ref-variable" if the original is not also available
12813 // on the host.
12814 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12815 return;
12816
12817 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12818
12819 if (!M.getNamedValue(RefName)) {
12820 Constant *AddrRef =
12821 getOrCreateInternalVariable(Addr->getType(), RefName);
12822 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12823 GvAddrRef->setConstant(true);
12824 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12825 GvAddrRef->setInitializer(Addr);
12826 GeneratedRefs.push_back(GvAddrRef);
12827 }
12828 }
12829 } else {
12832 else
12834
12835 if (Config.isTargetDevice()) {
12836 VarName = (Addr) ? Addr->getName() : "";
12837 Addr = nullptr;
12838 } else {
12840 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12841 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12842 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12843 VarName = (Addr) ? Addr->getName() : "";
12844 }
12845 VarSize = M.getDataLayout().getPointerSize();
12847 }
12848
12849 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12850 Flags, Linkage);
12851}
12852
12853/// Loads all the offload entries information from the host IR
12854/// metadata.
12856 // If we are in target mode, load the metadata from the host IR. This code has
12857 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12858
12859 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12860 if (!MD)
12861 return;
12862
12863 for (MDNode *MN : MD->operands()) {
12864 auto &&GetMDInt = [MN](unsigned Idx) {
12865 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12866 return cast<ConstantInt>(V->getValue())->getZExtValue();
12867 };
12868
12869 auto &&GetMDString = [MN](unsigned Idx) {
12870 auto *V = cast<MDString>(MN->getOperand(Idx));
12871 return V->getString();
12872 };
12873
12874 switch (GetMDInt(0)) {
12875 default:
12876 llvm_unreachable("Unexpected metadata!");
12877 break;
12878 case OffloadEntriesInfoManager::OffloadEntryInfo::
12879 OffloadingEntryInfoTargetRegion: {
12880 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12881 /*DeviceID=*/GetMDInt(1),
12882 /*FileID=*/GetMDInt(2),
12883 /*Line=*/GetMDInt(4),
12884 /*Count=*/GetMDInt(5));
12885 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12886 /*Order=*/GetMDInt(6));
12887 break;
12888 }
12889 case OffloadEntriesInfoManager::OffloadEntryInfo::
12890 OffloadingEntryInfoDeviceGlobalVar:
12891 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12892 /*MangledName=*/GetMDString(1),
12894 /*Flags=*/GetMDInt(2)),
12895 /*Order=*/GetMDInt(3));
12896 break;
12897 }
12898 }
12899}
12900
12902 StringRef HostFilePath) {
12903 if (HostFilePath.empty())
12904 return;
12905
12906 auto Buf = VFS.getBufferForFile(HostFilePath);
12907 if (std::error_code Err = Buf.getError()) {
12908 report_fatal_error(("error opening host file from host file path inside of "
12909 "OpenMPIRBuilder: " +
12910 Err.message())
12911 .c_str());
12912 }
12913
12914 LLVMContext Ctx;
12916 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12917 if (std::error_code Err = M.getError()) {
12919 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12920 .c_str());
12921 }
12922
12923 loadOffloadInfoMetadata(*M.get());
12924}
12925
12928 llvm::StringRef Name) {
12929 Builder.restoreIP(Loc.IP);
12930
12931 BasicBlock *CurBB = Builder.GetInsertBlock();
12932 assert(CurBB &&
12933 "expected a valid insertion block for creating an iterator loop");
12934 Function *F = CurBB->getParent();
12935
12936 InsertPointTy SplitIP = Builder.saveIP();
12937 if (SplitIP == CurBB->end())
12938 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12939 SplitIP = Terminator->getIterator();
12940
12941 BasicBlock *ContBB =
12942 splitBB(SplitIP, /*CreateBranch=*/false,
12943 Builder.getCurrentDebugLocation(), "omp.it.cont");
12944
12945 CanonicalLoopInfo *CLI =
12946 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12947 /*PreInsertBefore=*/ContBB,
12948 /*PostInsertBefore=*/ContBB, Name);
12949
12950 // Enter loop from original block.
12951 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12952
12953 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12954 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12955 T->eraseFromParent();
12956
12957 InsertPointTy BodyIP = CLI->getBodyIP();
12958 // Blocks that exist before BodyGen, other than the body and latch, are
12959 // outside the loop body.
12960 SmallPtrSet<BasicBlock *, 32> ExistingBlocks;
12961 for (BasicBlock &Block : *F)
12962 ExistingBlocks.insert(&Block);
12963 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
12964 return Err;
12965
12966 // The body may span several blocks. Branch its single unterminated block to
12967 // the latch; otherwise some block must already branch there.
12968 BasicBlock *Latch = CLI->getLatch();
12969 BasicBlock *OpenBB = nullptr;
12970 bool ReachesLatch = false;
12971 SmallVector<BasicBlock *> Worklist{CLI->getBody()};
12972 SmallPtrSet<BasicBlock *, 8> Visited{CLI->getBody()};
12973 while (!Worklist.empty()) {
12974 BasicBlock *BB = Worklist.pop_back_val();
12975 if (!BB->hasTerminator()) {
12976 if (OpenBB)
12978 "iterator bodygen must leave at most one unterminated block",
12980 OpenBB = BB;
12981 continue;
12982 }
12983 for (BasicBlock *Succ : successors(BB)) {
12984 if (Succ == Latch) {
12985 ReachesLatch = true;
12986 continue;
12987 }
12988 if (Succ != CLI->getBody() && ExistingBlocks.contains(Succ))
12990 "iterator bodygen must not branch out of the loop body",
12992 if (Visited.insert(Succ).second)
12993 Worklist.push_back(Succ);
12994 }
12995 }
12996
12997 if (OpenBB) {
12998 Builder.SetInsertPoint(OpenBB);
12999 Builder.CreateBr(Latch);
13000 } else if (!ReachesLatch) {
13001 return make_error<StringError>("iterator bodygen must reach the loop latch",
13003 }
13004
13005 // Link After -> ContBB
13006 Builder.SetInsertPoint(CLI->getAfter()->begin());
13007 if (!CLI->getAfter()->hasTerminator())
13008 Builder.CreateBr(ContBB);
13009
13010 return ContBB->begin();
13011}
13012
13013/// Mangle the parameter part of the vector function name according to
13014/// their OpenMP classification. The mangling function is defined in
13015/// section 4.5 of the AAVFABI(2021Q1).
13016static std::string mangleVectorParameters(
13018 SmallString<256> Buffer;
13019 llvm::raw_svector_ostream Out(Buffer);
13020 for (const auto &ParamAttr : ParamAttrs) {
13021 switch (ParamAttr.Kind) {
13023 Out << 'l';
13024 break;
13026 Out << 'R';
13027 break;
13029 Out << 'U';
13030 break;
13032 Out << 'L';
13033 break;
13035 Out << 'u';
13036 break;
13038 Out << 'v';
13039 break;
13040 }
13041 if (ParamAttr.HasVarStride)
13042 Out << "s" << ParamAttr.StrideOrArg;
13043 else if (ParamAttr.Kind ==
13045 ParamAttr.Kind ==
13047 ParamAttr.Kind ==
13049 ParamAttr.Kind ==
13051 // Don't print the step value if it is not present or if it is
13052 // equal to 1.
13053 if (ParamAttr.StrideOrArg < 0)
13054 Out << 'n' << -ParamAttr.StrideOrArg;
13055 else if (ParamAttr.StrideOrArg != 1)
13056 Out << ParamAttr.StrideOrArg;
13057 }
13058
13059 if (!!ParamAttr.Alignment)
13060 Out << 'a' << ParamAttr.Alignment;
13061 }
13062
13063 return std::string(Out.str());
13064}
13065
13067 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
13069 struct ISADataTy {
13070 char ISA;
13071 unsigned VecRegSize;
13072 };
13073 ISADataTy ISAData[] = {
13074 {'b', 128}, // SSE
13075 {'c', 256}, // AVX
13076 {'d', 256}, // AVX2
13077 {'e', 512}, // AVX512
13078 };
13080 switch (Branch) {
13082 Masked.push_back('N');
13083 Masked.push_back('M');
13084 break;
13086 Masked.push_back('N');
13087 break;
13089 Masked.push_back('M');
13090 break;
13091 }
13092 for (char Mask : Masked) {
13093 for (const ISADataTy &Data : ISAData) {
13095 llvm::raw_svector_ostream Out(Buffer);
13096 Out << "_ZGV" << Data.ISA << Mask;
13097 if (!VLENVal) {
13098 assert(NumElts && "Non-zero simdlen/cdtsize expected");
13099 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
13100 } else {
13101 Out << VLENVal;
13102 }
13103 Out << mangleVectorParameters(ParamAttrs);
13104 Out << '_' << Fn->getName();
13105 Fn->addFnAttr(Out.str());
13106 }
13107 }
13108}
13109
13110// Function used to add the attribute. The parameter `VLEN` is templated to
13111// allow the use of `x` when targeting scalable functions for SVE.
13112template <typename T>
13113static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
13114 char ISA, StringRef ParSeq,
13115 StringRef MangledName, bool OutputBecomesInput,
13116 llvm::Function *Fn) {
13117 SmallString<256> Buffer;
13118 llvm::raw_svector_ostream Out(Buffer);
13119 Out << Prefix << ISA << LMask << VLEN;
13120 if (OutputBecomesInput)
13121 Out << 'v';
13122 Out << ParSeq << '_' << MangledName;
13123 Fn->addFnAttr(Out.str());
13124}
13125
13126// Helper function to generate the Advanced SIMD names depending on the value
13127// of the NDS when simdlen is not present.
13128static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13129 StringRef Prefix, char ISA,
13130 StringRef ParSeq, StringRef MangledName,
13131 bool OutputBecomesInput,
13132 llvm::Function *Fn) {
13133 switch (NDS) {
13134 case 8:
13135 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13136 OutputBecomesInput, Fn);
13137 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13138 OutputBecomesInput, Fn);
13139 break;
13140 case 16:
13141 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13142 OutputBecomesInput, Fn);
13143 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13144 OutputBecomesInput, Fn);
13145 break;
13146 case 32:
13147 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13148 OutputBecomesInput, Fn);
13149 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13150 OutputBecomesInput, Fn);
13151 break;
13152 case 64:
13153 case 128:
13154 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13155 OutputBecomesInput, Fn);
13156 break;
13157 default:
13158 llvm_unreachable("Scalar type is too wide.");
13159 }
13160}
13161
13162/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13164 llvm::Function *Fn, unsigned UserVLEN,
13166 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13167 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13168
13169 // Sort out parameter sequence.
13170 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13171 StringRef Prefix = "_ZGV";
13172 StringRef MangledName = Fn->getName();
13173
13174 // Generate simdlen from user input (if any).
13175 if (UserVLEN) {
13176 if (ISA == 's') {
13177 // SVE generates only a masked function.
13178 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13179 OutputBecomesInput, Fn);
13180 return;
13181 }
13182
13183 switch (Branch) {
13185 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13186 OutputBecomesInput, Fn);
13187 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13188 OutputBecomesInput, Fn);
13189 break;
13191 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13192 OutputBecomesInput, Fn);
13193 break;
13195 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13196 OutputBecomesInput, Fn);
13197 break;
13198 }
13199 return;
13200 }
13201
13202 if (ISA == 's') {
13203 // SVE, section 3.4.1, item 1.
13204 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13205 OutputBecomesInput, Fn);
13206 return;
13207 }
13208
13209 switch (Branch) {
13211 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13212 MangledName, OutputBecomesInput, Fn);
13213 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13214 MangledName, OutputBecomesInput, Fn);
13215 break;
13217 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13218 MangledName, OutputBecomesInput, Fn);
13219 break;
13221 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13222 MangledName, OutputBecomesInput, Fn);
13223 break;
13224 }
13225}
13226
13227//===----------------------------------------------------------------------===//
13228// OffloadEntriesInfoManager
13229//===----------------------------------------------------------------------===//
13230
13232 return OffloadEntriesTargetRegion.empty() &&
13233 OffloadEntriesDeviceGlobalVar.empty();
13234}
13235
13236unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13237 const TargetRegionEntryInfo &EntryInfo) const {
13238 auto It = OffloadEntriesTargetRegionCount.find(
13239 getTargetRegionEntryCountKey(EntryInfo));
13240 if (It == OffloadEntriesTargetRegionCount.end())
13241 return 0;
13242 return It->second;
13243}
13244
13245void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13246 const TargetRegionEntryInfo &EntryInfo) {
13247 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13248 EntryInfo.Count + 1;
13249}
13250
13251/// Initialize target region entry.
13253 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13254 OffloadEntriesTargetRegion[EntryInfo] =
13255 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13257 ++OffloadingEntriesNum;
13258}
13259
13261 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13263 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13264
13265 // Update the EntryInfo with the next available count for this location.
13266 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13267
13268 // If we are emitting code for a target, the entry is already initialized,
13269 // only has to be registered.
13270 if (OMPBuilder->Config.isTargetDevice()) {
13271 // This could happen if the device compilation is invoked standalone.
13272 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13273 return;
13274 }
13275 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13276 Entry.setAddress(Addr);
13277 Entry.setID(ID);
13278 Entry.setFlags(Flags);
13279 } else {
13281 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13282 return;
13283 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13284 "Target region entry already registered!");
13285 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13286 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13287 ++OffloadingEntriesNum;
13288 }
13289 incrementTargetRegionEntryInfoCount(EntryInfo);
13290}
13291
13293 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13294
13295 // Update the EntryInfo with the next available count for this location.
13296 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13297
13298 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13299 if (It == OffloadEntriesTargetRegion.end()) {
13300 return false;
13301 }
13302 // Fail if this entry is already registered.
13303 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13304 return false;
13305 return true;
13306}
13307
13309 const OffloadTargetRegionEntryInfoActTy &Action) {
13310 // Scan all target region entries and perform the provided action.
13311 for (const auto &It : OffloadEntriesTargetRegion) {
13312 Action(It.first, It.second);
13313 }
13314}
13315
13317 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13318 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13319 ++OffloadingEntriesNum;
13320}
13321
13323 StringRef VarName, Constant *Addr, int64_t VarSize,
13325 if (OMPBuilder->Config.isTargetDevice()) {
13326 // This could happen if the device compilation is invoked standalone.
13327 if (!hasDeviceGlobalVarEntryInfo(VarName))
13328 return;
13329 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13330 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13331 if (Entry.getVarSize() == 0) {
13332 Entry.setVarSize(VarSize);
13333 Entry.setLinkage(Linkage);
13334 }
13335 return;
13336 }
13337 Entry.setVarSize(VarSize);
13338 Entry.setLinkage(Linkage);
13339 Entry.setAddress(Addr);
13340 } else {
13341 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13342 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13343 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13344 "Entry not initialized!");
13345 if (Entry.getVarSize() == 0) {
13346 Entry.setVarSize(VarSize);
13347 Entry.setLinkage(Linkage);
13348 }
13349 return;
13350 }
13352 Flags ==
13354 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13355 Addr, VarSize, Flags, Linkage,
13356 VarName.str());
13357 else
13358 OffloadEntriesDeviceGlobalVar.try_emplace(
13359 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13360 ++OffloadingEntriesNum;
13361 }
13362}
13363
13366 // Scan all target region entries and perform the provided action.
13367 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13368 Action(E.getKey(), E.getValue());
13369}
13370
13371//===----------------------------------------------------------------------===//
13372// CanonicalLoopInfo
13373//===----------------------------------------------------------------------===//
13374
13375void CanonicalLoopInfo::collectControlBlocks(
13377 // We only count those BBs as control block for which we do not need to
13378 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13379 // flow. For consistency, this also means we do not add the Body block, which
13380 // is just the entry to the body code.
13381 BBs.reserve(BBs.size() + 6);
13382 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13383}
13384
13386 assert(isValid() && "Requires a valid canonical loop");
13387 for (BasicBlock *Pred : predecessors(Header)) {
13388 if (Pred != Latch)
13389 return Pred;
13390 }
13391 llvm_unreachable("Missing preheader");
13392}
13393
13394void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13395 assert(isValid() && "Requires a valid canonical loop");
13396
13397 Instruction *CmpI = &getCond()->front();
13398 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13399 CmpI->setOperand(1, TripCount);
13400
13401#ifndef NDEBUG
13402 assertOK();
13403#endif
13404}
13405
13406void CanonicalLoopInfo::mapIndVar(
13407 llvm::function_ref<Value *(Instruction *)> Updater) {
13408 assert(isValid() && "Requires a valid canonical loop");
13409
13410 Instruction *OldIV = getIndVar();
13411
13412 // Record all uses excluding those introduced by the updater. Uses by the
13413 // CanonicalLoopInfo itself to keep track of the number of iterations are
13414 // excluded.
13415 SmallVector<Use *> ReplacableUses;
13416 for (Use &U : OldIV->uses()) {
13417 auto *User = dyn_cast<Instruction>(U.getUser());
13418 if (!User)
13419 continue;
13420 if (User->getParent() == getCond())
13421 continue;
13422 if (User->getParent() == getLatch())
13423 continue;
13424 ReplacableUses.push_back(&U);
13425 }
13426
13427 // Run the updater that may introduce new uses
13428 Value *NewIV = Updater(OldIV);
13429
13430 // Replace the old uses with the value returned by the updater.
13431 for (Use *U : ReplacableUses)
13432 U->set(NewIV);
13433
13434#ifndef NDEBUG
13435 assertOK();
13436#endif
13437}
13438
13440#ifndef NDEBUG
13441 // No constraints if this object currently does not describe a loop.
13442 if (!isValid())
13443 return;
13444
13445 BasicBlock *Preheader = getPreheader();
13446 BasicBlock *Body = getBody();
13447 BasicBlock *After = getAfter();
13448
13449 // Verify standard control-flow we use for OpenMP loops.
13450 assert(Preheader);
13451 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13452 "Preheader must terminate with unconditional branch");
13453 assert(Preheader->getSingleSuccessor() == Header &&
13454 "Preheader must jump to header");
13455
13456 assert(Header);
13457 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13458 "Header must terminate with unconditional branch");
13459 assert(Header->getSingleSuccessor() == Cond &&
13460 "Header must jump to exiting block");
13461
13462 assert(Cond);
13463 assert(Cond->getSinglePredecessor() == Header &&
13464 "Exiting block only reachable from header");
13465
13466 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13467 "Exiting block must terminate with conditional branch");
13468 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13469 "Exiting block's first successor jump to the body");
13470 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13471 "Exiting block's second successor must exit the loop");
13472
13473 assert(Body);
13474 assert(Body->getSinglePredecessor() == Cond &&
13475 "Body only reachable from exiting block");
13476 assert(!isa<PHINode>(Body->front()));
13477
13478 assert(Latch);
13479 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13480 "Latch must terminate with unconditional branch");
13481 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13482 // TODO: To support simple redirecting of the end of the body code that has
13483 // multiple; introduce another auxiliary basic block like preheader and after.
13484 assert(Latch->getSinglePredecessor() != nullptr);
13485 assert(!isa<PHINode>(Latch->front()));
13486
13487 assert(Exit);
13488 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13489 "Exit block must terminate with unconditional branch");
13490 assert(Exit->getSingleSuccessor() == After &&
13491 "Exit block must jump to after block");
13492
13493 assert(After);
13494 assert(After->getSinglePredecessor() == Exit &&
13495 "After block only reachable from exit block");
13496 assert(After->empty() || !isa<PHINode>(After->front()));
13497
13498 Instruction *IndVar = getIndVar();
13499 assert(IndVar && "Canonical induction variable not found?");
13500 assert(isa<IntegerType>(IndVar->getType()) &&
13501 "Induction variable must be an integer");
13502 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13503 "Induction variable must be a PHI in the loop header");
13504 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == Preheader);
13505 assert(
13506 cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))->isZero());
13507 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13508
13509 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13510 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13511 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13512 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13513 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13514 ->isOne());
13515
13516 Value *TripCount = getTripCount();
13517 assert(TripCount && "Loop trip count not found?");
13518 assert(IndVar->getType() == TripCount->getType() &&
13519 "Trip count and induction variable must have the same type");
13520
13521 auto *CmpI = cast<CmpInst>(&Cond->front());
13522 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13523 "Exit condition must be a signed less-than comparison");
13524 assert(CmpI->getOperand(0) == IndVar &&
13525 "Exit condition must compare the induction variable");
13526 assert(CmpI->getOperand(1) == TripCount &&
13527 "Exit condition must compare with the trip count");
13528#endif
13529}
13530
13532 Header = nullptr;
13533 Cond = nullptr;
13534 Latch = nullptr;
13535 Exit = nullptr;
13536}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
Rewrite undef for PHI
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static cl::opt< ITMode > IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT), cl::values(clEnumValN(DefaultIT, "arm-default-it", "Generate any type of IT block"), clEnumValN(RestrictedIT, "arm-restrict-it", "Disallow complex IT blocks")))
Expand Atomic instructions
@ ParamAttr
This file contains the simple types necessary to represent the attributes associated with functions a...
static const Function * getParent(const Value *V)
#define X(NUM, ENUM, NAME)
Definition ELF.h:857
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Hexagon Common GEP
Hexagon Hardware Loops
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
This header defines various interfaces for pass management in LLVM.
iv Induction Variable Users
Definition IVUsers.cpp:48
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
static LVOptions Options
Definition LVOptions.cpp:25
static bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
Definition Lint.cpp:540
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file contains the declarations for metadata subclasses.
#define T
uint64_t IntrinsicInst * II
#define OMP_KERNEL_ARG_VERSION
Provides definitions for Target specific Grid Values.
static void emitTargetCall(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride, OpenMPIRBuilder::InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, OpenMPIRBuilder::TargetDataInfo &Info, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, const OpenMPIRBuilder::TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID, SmallVectorImpl< Value * > &Args, OpenMPIRBuilder::GenMapInfoCallbackTy GenMapInfoCB, OpenMPIRBuilder::CustomMapperCallbackTy CustomMapperCB, const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait, Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback)
static Value * removeASCastIfPresent(Value *V)
static void createTargetLoopWorkshareCall(OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType, BasicBlock *InsertBlock, Value *Ident, Value *LoopBodyArg, Value *TripCount, Function &LoopBodyFn, bool NoLoop)
Value * createFakeIntVal(IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy OuterAllocaIP, llvm::SmallVectorImpl< Instruction * > &ToBeDeleted, OpenMPIRBuilder::InsertPointTy InnerAllocaIP, const Twine &Name="", bool AsPtr=true, bool Is64Bit=false)
static Function * createTargetParallelWrapper(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn)
Create wrapper function used to gather the outlined function's argument structure from a shared buffe...
static void redirectTo(BasicBlock *Source, BasicBlock *Target, DebugLoc DL)
Make Source branch to Target.
static FunctionCallee getKmpcDistForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void applyParallelAccessesMetadata(CanonicalLoopInfo *CLI, LLVMContext &Ctx, Loop *Loop, LoopInfo &LoopInfo, SmallVector< Metadata * > &LoopMDList)
static Expected< Function * > createOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, StringRef FuncName, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB, DebugLoc OutlinedFnLoc)
static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static FunctionCallee getKmpcForDynamicFiniForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for finalizing the dynamic loop using depending on type.
static void FixupDebugInfoForOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func, DenseMap< Value *, std::tuple< Value *, unsigned > > &ValueReplacementMap)
static OMPScheduleType getOpenMPOrderingScheduleType(OMPScheduleType BaseScheduleType, bool HasOrderedClause)
Adds ordering modifier flags to schedule type.
static OMPScheduleType getOpenMPMonotonicityScheduleType(OMPScheduleType ScheduleType, bool HasSimdModifier, bool HasMonotonic, bool HasNonmonotonic, bool HasOrderedClause)
Adds monotonicity modifier flags to schedule type.
static std::string mangleVectorParameters(ArrayRef< llvm::OpenMPIRBuilder::DeclareSimdAttrTy > ParamAttrs)
Mangle the parameter part of the vector function name according to their OpenMP classification.
static bool isGenericKernel(Function &Fn)
static void workshareLoopTargetCallback(OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident, Function &OutlinedFn, const SmallVector< Instruction *, 4 > &ToBeDeleted, WorksharingLoopType LoopType, bool NoLoop)
static bool isValidWorkshareLoopScheduleType(OMPScheduleType SchedType)
static bool isAtomicableReductionSet(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos)
static llvm::CallInst * emitNoUnwindRuntimeCall(IRBuilder<> &Builder, llvm::FunctionCallee Callee, ArrayRef< llvm::Value * > Args, const llvm::Twine &Name)
static Error populateReductionFunction(Function *ReductionFunc, ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, IRBuilder<> &Builder, ArrayRef< bool > IsByRef, bool IsGPU)
static Function * getFreshReductionFunc(Module &M)
static void raiseUserConstantDataAllocasToEntryBlock(IRBuilderBase &Builder, Function *Function)
static FunctionCallee getKmpcForDynamicNextForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for updating the next loop using OpenMP dynamic scheduling depending...
static bool isConflictIP(IRBuilder<>::InsertPoint IP1, IRBuilder<>::InsertPoint IP2)
Return whether IP1 and IP2 are ambiguous, i.e.
static void checkReductionInfos(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, bool IsGPU)
static Type * getOffloadingArrayType(Value *V)
static OMPScheduleType getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasDistScheduleChunks)
Determine which scheduling algorithm to use, determined from schedule clause arguments.
static OMPScheduleType computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasMonotonicModifier, bool HasNonmonotonicModifier, bool HasOrderedClause, bool HasDistScheduleChunks)
Determine the schedule type using schedule and ordering clause arguments.
static FunctionCallee getKmpcForDynamicInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for initializing loop bounds using OpenMP dynamic scheduling dependi...
static std::optional< omp::OMPTgtExecModeFlags > getTargetKernelExecMode(Function &Kernel)
Given a function, if it represents the entry point of a target kernel, this returns the execution mod...
static StructType * createTaskWithPrivatesTy(OpenMPIRBuilder &OMPIRBuilder, ArrayRef< Value * > OffloadingArraysToPrivatize)
static cl::opt< double > UnrollThresholdFactor("openmp-ir-builder-unroll-threshold-factor", cl::Hidden, cl::desc("Factor for the unroll threshold to account for code " "simplifications still taking place"), cl::init(1.5))
static cl::opt< bool > UseDefaultMaxThreads("openmp-ir-builder-use-default-max-threads", cl::Hidden, cl::desc("Use a default max threads if none is provided."), cl::init(true))
static int32_t computeHeuristicUnrollFactor(CanonicalLoopInfo *CLI)
Heuristically determine the best-performant unroll factor for CLI.
static Error emitTargetOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry, TargetRegionEntryInfo &EntryInfo, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, Function *&OutlinedFn, Constant *&OutlinedFnID, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB, DebugLoc OutlinedFnLoc)
static Value * emitTaskDependencies(OpenMPIRBuilder &OMPBuilder, const SmallVectorImpl< OpenMPIRBuilder::DependData > &Dependencies)
static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value, bool Min)
static OpenMPIRBuilder::InsertPointTy getInsertPointAfterInstr(Instruction *I)
static void redirectAllPredecessorsTo(BasicBlock *OldTarget, BasicBlock *NewTarget, DebugLoc DL)
Redirect all edges that branch to OldTarget to NewTarget.
static void hoistNonEntryAllocasToEntryBlock(llvm::BasicBlock &Block)
static std::unique_ptr< TargetMachine > createTargetMachine(Function *F, CodeGenOptLevel OptLevel)
Create the TargetMachine object to query the backend for optimization preferences.
static FunctionCallee getKmpcForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup, LoopInfo &LI)
Attach llvm.access.group metadata to the memref instructions of Block.
static void addBasicBlockMetadata(BasicBlock *BB, ArrayRef< Metadata * > Properties)
Attach metadata Properties to the basic block described by BB.
static void restoreIPandDebugLoc(llvm::IRBuilderBase &Builder, llvm::IRBuilderBase::InsertPoint IP)
This is a wrapper over IRBuilderBase::restoreIP that also restores a current debug location when the ...
static LoadInst * loadSharedDataFromTaskDescriptor(OpenMPIRBuilder &OMPIRBuilder, IRBuilderBase &Builder, Value *TaskWithPrivates, Type *TaskWithPrivatesTy)
Given a task descriptor, TaskWithPrivates, return the pointer to the block of pointers containing sha...
static cl::opt< bool > OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden, cl::desc("Use optimistic attributes describing " "'as-if' properties of runtime calls."), cl::init(false))
static bool hasGridValue(const Triple &T)
static FunctionCallee getKmpcForStaticLoopForType(Type *Ty, OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType)
static const omp::GV & getGridValue(const Triple &T, Function *Kernel)
static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static Function * emitTargetTaskProxyFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI, StructType *PrivatesTy, StructType *TaskWithPrivatesTy, const size_t NumOffloadingArrays, const int SharedArgsOperandNo)
Create an entry point for a target task with the following.
static void addLoopMetadata(CanonicalLoopInfo *Loop, ArrayRef< Metadata * > Properties)
Attach loop metadata Properties to the loop described by Loop.
static AtomicOrdering TransformReleaseAcquireRelease(AtomicOrdering AO)
static void removeUnusedBlocksFromParent(ArrayRef< BasicBlock * > BBs)
static void targetParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition, Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr, Value *ThreadID, const SmallVector< Instruction *, 4 > &ToBeDeleted)
static void hostParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, Value *Ident, Value *IfCondition, Instruction *PrivTID, AllocaInst *PrivTIDAddr, const SmallVector< Instruction *, 4 > &ToBeDeleted)
#define P(N)
FunctionAnalysisManager FAM
Function * Fun
This file defines the Pass Instrumentation classes that provide instrumentation points into the pass ...
const SmallVectorImpl< MachineOperand > & Cond
Remove Loads Into Fake Uses
static bool isValid(const char C)
Returns true if C is a valid mangled character: <0-9a-zA-Z_>.
SmallPtrSet< BasicBlock *, 0 > BlockSet
This file implements the SmallBitVector class.
This file defines the SmallSet class.
This file defines less commonly used SmallVector utilities.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static SymbolRef::Type getType(const Symbol *Sym)
Definition TapiFile.cpp:39
Defines the virtual file system interface vfs::FileSystem.
Value * RHS
Value * LHS
static 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:406
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:453
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:440
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:456
bool hasTerminator() const LLVM_READONLY
Returns whether the block has a terminator.
Definition BasicBlock.h:232
bool empty() const
Definition BasicBlock.h:462
const Instruction & back() const
Definition BasicBlock.h:465
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 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:463
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 InstListType::const_iterator getFirstNonPHIOrPseudoProbe() const
Returns an iterator to the first instruction in this block that is not a PHINode or a pseudo operatio...
LLVM_ABI SymbolTableList< BasicBlock >::iterator eraseFromParent()
Unlink 'this' from the containing function and delete it.
reverse_iterator rend()
Definition BasicBlock.h:458
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:367
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:638
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:641
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:766
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:352
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
adds the attribute to the list of attributes for the given arg.
Definition Function.cpp:669
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition Function.h:740
size_t arg_size() const
Definition Function.h:886
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:217
iterator end()
Definition Function.h:840
void setCallingConv(CallingConv::ID CC)
Definition Function.h:277
Argument * getArg(unsigned i) const
Definition Function.h:871
bool hasMetadata() const
Return true if this GlobalObject has any metadata attached to it.
LLVM_ABI void addMetadata(unsigned KindID, MDNode &MD)
Add a metadata attachment.
LinkageTypes getLinkage() const
void setLinkage(LinkageTypes LT)
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
PointerType * getType() const
Global values are always pointers.
@ HiddenVisibility
The GV is hidden.
Definition GlobalValue.h:69
@ ProtectedVisibility
The GV is protected.
Definition GlobalValue.h:70
void setVisibility(VisibilityTypes V)
LinkageTypes
An enumeration for the kinds of linkage for global values.
Definition GlobalValue.h:52
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
@ CommonLinkage
Tentative definitions.
Definition GlobalValue.h:63
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ WeakODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:58
@ WeakAnyLinkage
Keep one copy of named function when linking (weak)
Definition GlobalValue.h:57
@ AppendingLinkage
Special purpose, only applies to global arrays.
Definition GlobalValue.h:59
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:56
Type * getValueType() const
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
Common base class shared among various IRBuilders.
Definition IRBuilder.h:111
InsertPoint saveIP() const
Returns the current insert point.
Definition IRBuilder.h:256
BasicBlock::iterator InsertPoint
InsertPoint - A saved insertion point.
Definition IRBuilder.h:253
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:266
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2909
LLVM_ABI void removeFromParent()
This method unlinks 'this' from the containing basic block, but does not delete it.
LLVM_ABI unsigned getNumSuccessors() const LLVM_READONLY
Return the number of successors that this instruction has.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
user_iterator user_begin()
user_iterator user_end()
LLVM_ABI void moveBeforePreserving(InstListType::iterator MovePos)
Perform a moveBefore operation, while signalling that the caller intends to preserve the original ord...
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
LLVM_ABI void insertAfter(Instruction *InsertPos)
Insert an unlinked instruction into a basic block immediately after the specified instruction.
LLVM_ABI const DataLayout & getDataLayout() const
Get the data layout of the module this instruction belongs to.
Class to represent integer types.
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:338
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:76
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:594
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h:1081
LLVM_ABI void replaceOperandWith(unsigned I, Metadata *New)
Replace a specific operand.
static MDTuple * getDistinct(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1587
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1435
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:597
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
size_type size() const
Definition MapVector.h:58
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:332
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:325
A tuple of MDNodes.
Definition Metadata.h:1797
iterator_range< op_iterator > operands()
Definition Metadata.h:1893
LLVM_ABI void addOperand(MDNode *M)
Class that manages information about offload code regions and data.
function_ref< void(StringRef, const OffloadEntryInfoDeviceGlobalVar &)> OffloadDeviceGlobalVarEntryInfoActTy
Applies action Action on all registered entries.
OMPTargetDeviceClauseKind
Kind of device clause for declare target variables and functions NOTE: Currently not used as a part o...
@ OMPTargetDeviceClauseAny
The target is marked for all devices.
LLVM_ABI void registerDeviceGlobalVarEntryInfo(StringRef VarName, Constant *Addr, int64_t VarSize, OMPTargetGlobalVarEntryKind Flags, GlobalValue::LinkageTypes Linkage)
Register device global variable entry.
LLVM_ABI void initializeDeviceGlobalVarEntryInfo(StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order)
Initialize device global variable entry.
LLVM_ABI void actOnDeviceGlobalVarEntriesInfo(const OffloadDeviceGlobalVarEntryInfoActTy &Action)
OMPTargetRegionEntryKind
Kind of the target registry entry.
@ OMPTargetRegionEntryTargetRegion
Mark the entry as target region.
LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, const TargetRegionEntryInfo &EntryInfo)
LLVM_ABI bool hasTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId=false) const
Return true if a target region entry with the provided information exists.
LLVM_ABI void registerTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID, OMPTargetRegionEntryKind Flags)
Register target region entry.
LLVM_ABI void actOnTargetRegionEntriesInfo(const OffloadTargetRegionEntryInfoActTy &Action)
LLVM_ABI void initializeTargetRegionEntryInfo(const TargetRegionEntryInfo &EntryInfo, unsigned Order)
Initialize target region entry.
OMPTargetGlobalVarEntryKind
Kind of the global variable entry..
@ OMPTargetGlobalVarEntryEnter
Mark the entry as a declare target enter.
@ OMPTargetGlobalRegisterRequires
Mark the entry as a register requires global.
@ OMPTargetGlobalVarEntryIndirect
Mark the entry as a declare target indirect global.
@ OMPTargetGlobalVarEntryLink
Mark the entry as a to declare target link.
@ OMPTargetGlobalVarEntryTo
Mark the entry as a to declare target.
@ OMPTargetGlobalVarEntryIndirectVTable
Mark the entry as a declare target indirect vtable.
function_ref< void(const TargetRegionEntryInfo &EntryInfo, const OffloadEntryInfoTargetRegion &)> OffloadTargetRegionEntryInfoActTy
brief Applies action Action on all registered entries.
bool hasDeviceGlobalVarEntryInfo(StringRef VarName) const
Checks if the variable with the given name has been registered already.
LLVM_ABI bool empty() const
Return true if a there are no entries defined.
std::optional< bool > IsTargetDevice
Flag to define whether to generate code for the role of the OpenMP host (if set to false) or device (...
std::optional< bool > IsGPU
Flag for specifying if the compilation is done for an accelerator.
LLVM_ABI int64_t getRequiresFlags() const
Returns requires directive clauses as flags compatible with those expected by libomptarget.
std::optional< bool > OpenMPOffloadMandatory
Flag for specifying if offloading is mandatory.
LLVM_ABI void setHasRequiresReverseOffload(bool Value)
LLVM_ABI bool hasRequiresUnifiedSharedMemory() const
LLVM_ABI void setHasRequiresUnifiedSharedMemory(bool Value)
unsigned getDefaultTargetAS() const
LLVM_ABI bool hasRequiresDynamicAllocators() const
LLVM_ABI void setHasRequiresUnifiedAddress(bool Value)
LLVM_ABI void setHasRequiresDynamicAllocators(bool Value)
LLVM_ABI bool hasRequiresReverseOffload() const
LLVM_ABI bool hasRequiresUnifiedAddress() const
Struct that keeps the information that should be kept throughout a 'target data' region.
An interface to create LLVM-IR for OpenMP directives.
LLVM_ABI InsertPointOrErrorTy createOrderedThreadsSimd(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsThreads)
Generator for 'omp ordered [threads | simd]'.
LLVM_ABI void emitAArch64DeclareSimdFunction(llvm::Function *Fn, unsigned VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch, char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput)
Emit AArch64 vector-function ABI attributes for a declare simd function.
LLVM_ABI Constant * getOrCreateIdent(Constant *SrcLocStr, uint32_t SrcLocStrSize, omp::IdentFlag Flags=omp::IdentFlag(0), unsigned Reserve2Flags=0)
Return an ident_t* encoding the source location SrcLocStr and Flags.
LLVM_ABI void registerDeclareTargetGlobalReplacement(GlobalValue *Original, GlobalValue *Replacement)
Register a module-scope replacement of a declare target global variable.
LLVM_ABI FunctionCallee getOrCreateRuntimeFunction(Module &M, omp::RuntimeFunction FnID)
Return the function declaration for the runtime function with FnID.
LLVM_ABI InsertPointOrErrorTy createCancel(const LocationDescription &Loc, Value *IfCondition, omp::Directive CanceledDirective)
Generator for 'omp cancel'.
std::function< Expected< Function * >(StringRef FunctionName)> FunctionGenCallback
Functions used to generate a function with the given name.
LLVM_ABI CallInst * createOMPAllocShared(const LocationDescription &Loc, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_alloc_shared.
ReductionGenCBKind
Enum class for the RedctionGen CallBack type to be used.
LLVM_ABI CanonicalLoopInfo * collapseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, InsertPointTy ComputeIP)
Collapse a loop nest into a single loop.
LLVM_ABI void createTaskyield(const LocationDescription &Loc)
Generator for 'omp taskyield'.
std::function< Error(InsertPointTy CodeGenIP)> FinalizeCallbackTy
Callback type for variable finalization (think destructors).
LLVM_ABI void emitBranch(BasicBlock *Target)
LLVM_ABI Error emitCancelationCheckImpl(Value *CancelFlag, omp::Directive CanceledDirective)
Generate control flow and cleanup for cancellation.
static LLVM_ABI void writeThreadBoundsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI void emitTaskwaitImpl(const LocationDescription &Loc)
Generate a taskwait runtime call.
LLVM_ABI Constant * registerTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, Function *OutlinedFunction, StringRef EntryFnName, StringRef EntryFnIDName)
Registers the given function and sets up the attribtues of the function Returns the FunctionID.
LLVM_ABI GlobalVariable * emitKernelExecutionMode(StringRef KernelName, omp::OMPTgtExecModeFlags Mode)
Emit the kernel execution mode.
LLVM_ABI void initialize()
Initialize the internal state, this will put structures types and potentially other helpers into the ...
LLVM_ABI InsertPointTy createAtomicCompare(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOpValue &R, Value *E, Value *D, AtomicOrdering AO, omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate, bool IsFailOnly, bool IsWeak=false)
LLVM_ABI InsertPointTy createAtomicWrite(const LocationDescription &Loc, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic write for : X = Expr — Only Scalar data types.
LLVM_ABI void loadOffloadInfoMetadata(Module &M)
Loads all the offload entries information from the host IR metadata.
function_ref< MapInfosTy &(InsertPointTy CodeGenIP)> GenMapInfoCallbackTy
Callback type for creating the map infos for the kernel parameters.
LLVM_ABI Error emitOffloadingArrays(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Emit the arrays used to pass the captures and map information to the offloading runtime library.
LLVM_ABI void unrollLoopFull(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully unroll a loop.
function_ref< Error(InsertPointTy CodeGenIP, Value *IndVar)> LoopBodyGenCallbackTy
Callback type for loop body code generation.
LLVM_ABI InsertPointOrErrorTy emitScanReduction(const LocationDescription &Loc, ArrayRef< llvm::OpenMPIRBuilder::ReductionInfo > ReductionInfos, ScanInfo *ScanRedInfo)
This function performs the scan reduction of the values updated in the input phase.
LLVM_ABI void emitFlush(const LocationDescription &Loc)
Generate a flush runtime call.
LLVM_ABI InsertPointOrErrorTy createScope(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait)
Generator for 'omp scope'.
static LLVM_ABI std::pair< int32_t, int32_t > readThreadBoundsForKernel(const Triple &T, Function &Kernel)
}
OpenMPIRBuilderConfig Config
The OpenMPIRBuilder Configuration.
LLVM_ABI CallInst * createOMPInteropDestroy(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_destroy.
LLVM_ABI void emitUsed(StringRef Name, ArrayRef< llvm::WeakTrackingVH > List)
Emit the llvm.used metadata.
LLVM_ABI InsertPointOrErrorTy createSingle(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef< llvm::Value * > CPVars={}, ArrayRef< llvm::Function * > CPFuncs={})
Generator for 'omp single'.
LLVM_ABI InsertPointOrErrorTy createTarget(const LocationDescription &Loc, bool IsOffloadEntry, OpenMPIRBuilder::InsertPointTy AllocaIP, OpenMPIRBuilder::InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo, const TargetKernelDefaultAttrs &DefaultAttrs, const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, SmallVectorImpl< Value * > &Inputs, GenMapInfoCallbackTy GenMapInfoCB, TargetBodyGenCallbackTy BodyGenCB, TargetGenArgAccessorsCallbackTy ArgAccessorFuncCB, CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies={}, bool HasNowait=false, Value *DynCGroupMem=nullptr, omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback=omp::OMPDynGroupprivateFallbackType::Abort, DebugLoc OutlinedFnLoc={}, Value *RTLocOverride=nullptr)
Generator for 'omp target'.
LLVM_ABI InsertPointOrErrorTy createTeams(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower=nullptr, Value *NumTeamsUpper=nullptr, Value *ThreadLimit=nullptr, Value *IfExpr=nullptr)
Generator for #omp teams
std::forward_list< CanonicalLoopInfo > LoopInfos
Collection of owned canonical loop objects that eventually need to be free'd.
LLVM_ABI llvm::StructType * getKmpTaskAffinityInfoTy()
Return the LLVM struct type matching runtime kmp_task_affinity_info_t.
LLVM_ABI Constant * emitKernelEnvironment(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
The omp target interface.
LLVM_ABI std::string createPlatformSpecificName(ArrayRef< StringRef > Parts) const
Get the create a name using the platform specific separators.
LLVM_ABI FunctionCallee createDispatchNextFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_next_* runtime function for the specified size IVSize and sign IVSigned.
static LLVM_ABI void getKernelArgsVector(TargetKernelArgs &KernelArgs, IRBuilderBase &Builder, SmallVector< Value * > &ArgsVector)
Create the kernel args vector used by emitTargetKernel.
LLVM_ABI void unrollLoopHeuristic(DebugLoc DL, CanonicalLoopInfo *Loop)
Fully or partially unroll a loop.
LLVM_ABI omp::OpenMPOffloadMappingFlags getMemberOfFlag(unsigned Position)
Get OMP_MAP_MEMBER_OF flag with extra bits reserved based on the position given.
LLVM_ABI void addAttributes(omp::RuntimeFunction FnID, Function &Fn)
Add attributes known for FnID to Fn.
Module & M
The underlying LLVM-IR module.
StringMap< Constant * > SrcLocStrMap
Map to remember source location strings.
LLVM_ABI void createMapperAllocas(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumOperands, struct MapperAllocas &MapperAllocas)
Create the allocas instruction used in call to mapper functions.
SmallVector< DeclareTargetGlobalReplacement, 8 > DeclareTargetGlobalReplacements
Collection of declare target globals to rewrite uses of during device module finalizaiton.
LLVM_ABI Constant * getOrCreateSrcLocStr(StringRef LocStr, uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the source location LocStr.
LLVM_ABI Error emitTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry, Function *&OutlinedFn, Constant *&OutlinedFnID)
Create a unique name for the entry function using the source location information of the current targ...
LLVM_ABI InsertPointOrErrorTy createIteratorLoop(LocationDescription Loc, llvm::Value *TripCount, IteratorBodyGenTy BodyGen, llvm::StringRef Name="iterator")
Create a canonical iterator loop at the current insertion point.
LLVM_ABI Expected< SmallVector< llvm::CanonicalLoopInfo * > > createCanonicalScanLoops(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo)
Generator for the control flow structure of an OpenMP canonical loops if the parent directive has an ...
LLVM_ABI FunctionCallee createDispatchFiniFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_fini_* runtime function for the specified size IVSize and sign IVSigned.
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> TargetBodyGenCallbackTy
LLVM_ABI void unrollLoopPartial(DebugLoc DL, CanonicalLoopInfo *Loop, int32_t Factor, CanonicalLoopInfo **UnrolledCLI)
Partially unroll a loop.
function_ref< Error(Value *DeviceID, Value *RTLoc, IRBuilderBase::InsertPoint TargetTaskAllocaIP)> TargetTaskBodyCallbackTy
Callback type for generating the bodies of device directives that require outer target tasks (e....
Expected< MapInfosTy & > MapInfosOrErrorTy
bool HandleFPNegZero
Emit atomic compare for constructs: — Only scalar data types cond-expr-stmt: x = x ordop expr ?
LLVM_ABI void emitTaskyieldImpl(const LocationDescription &Loc)
Generate a taskyield runtime call.
LLVM_ABI void emitMapperCall(const LocationDescription &Loc, Function *MapperFunc, Value *SrcLocInfo, Value *MaptypesArg, Value *MapnamesArg, struct MapperAllocas &MapperAllocas, int64_t DeviceID, unsigned NumOperands)
Create the call for the target mapper function.
LLVM_ABI InsertPointOrErrorTy createDistribute(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for #omp distribute
function_ref< Expected< Function * >(unsigned int)> CustomMapperCallbackTy
LLVM_ABI InsertPointTy createOrderedDepend(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumLoops, ArrayRef< llvm::Value * > StoreValues, const Twine &Name, bool IsDependSource)
Generator for 'omp ordered depend (source | sink)'.
LLVM_ABI InsertPointTy createCopyinClauseBlocks(InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr, llvm::IntegerType *IntPtrTy, bool BranchtoEnd=true)
Generate conditional branch and relevant BasicBlocks through which private threads copy the 'copyin' ...
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Original, Value &Inner, Value *&ReplVal)> PrivatizeCallbackTy
Callback type for variable privatization (think copy & default constructor).
LLVM_ABI bool isFinalized()
Check whether the finalize function has already run.
SmallVector< FinalizationInfo, 8 > FinalizationStack
The finalization stack made up of finalize callbacks currently in-flight, wrapped into FinalizationIn...
LLVM_ABI std::vector< CanonicalLoopInfo * > tileLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, ArrayRef< Value * > TileSizes)
Tile a loop nest.
LLVM_ABI CallInst * createOMPInteropInit(const LocationDescription &Loc, Value *InteropVar, omp::OMPInteropType InteropType, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_init.
LLVM_ABI Error emitIfClause(Value *Cond, BodyGenCallbackTy ThenGen, BodyGenCallbackTy ElseGen, InsertPointTy AllocaIP={}, ArrayRef< BasicBlock * > DeallocBlocks={})
Emits code for OpenMP 'if' clause using specified BodyGenCallbackTy Here is the logic: if (Cond) { Th...
LLVM_ABI void finalize(Function *Fn=nullptr)
Finalize the underlying module, e.g., by outlining regions.
LLVM_ABI Function * getOrCreateRuntimeFunctionPtr(omp::RuntimeFunction FnID)
void addOutlineInfo(std::unique_ptr< OutlineInfo > &&OI)
Add a new region that will be outlined later.
LLVM_ABI InsertPointTy createTargetInit(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
Create a runtime call for kmpc_target_init.
LLVM_ABI InsertPointOrErrorTy createReductions(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false)
Generator for 'omp reduction'.
const Triple T
The target triple of the underlying module.
DenseMap< std::pair< Constant *, uint64_t >, Constant * > IdentMap
Map to remember existing ident_t*.
LLVM_ABI CallInst * createOMPFree(const LocationDescription &Loc, Value *Addr, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_free.
LLVM_ABI InsertPointOrErrorTy createReductionsGPU(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false, bool IsSPMD=false, ReductionGenCBKind ReductionGenCBKind=ReductionGenCBKind::MLIR, std::optional< omp::GV > GridValue={}, Value *SrcLocInfo=nullptr)
Design of OpenMP reductions on the GPU.
LLVM_ABI FunctionCallee createForStaticInitFunction(unsigned IVSize, bool IVSigned, bool IsGPUDistribute)
Returns __kmpc_for_static_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI CallInst * createOMPAlloc(const LocationDescription &Loc, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_alloc.
LLVM_ABI void emitNonContiguousDescriptor(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info)
Emit an array of struct descriptors to be assigned to the offload args.
LLVM_ABI InsertPointOrErrorTy createSection(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp section'.
LLVM_ABI InsertPointOrErrorTy createTaskgroup(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for the taskgroup construct.
LLVM_ABI InsertPointOrErrorTy createParallel(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition, Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable)
Generator for 'omp parallel'.
function_ref< InsertPointOrErrorTy(InsertPointTy)> EmitFallbackCallbackTy
Callback function type for functions emitting the host fallback code that is executed when the kernel...
static LLVM_ABI TargetRegionEntryInfo getTargetEntryUniqueInfo(FileIdentifierInfoCallbackTy CallBack, vfs::FileSystem &VFS, StringRef ParentName="")
Creates a unique info for a target entry when provided a filename and line number from.
LLVM_ABI void emitTaskDependency(IRBuilderBase &Builder, Value *Entry, const DependData &Dep)
Store one kmp_depend_info entry at the given Entry pointer.
LLVM_ABI void emitBlock(BasicBlock *BB, Function *CurFn, bool IsFinished=false)
LLVM_ABI Value * getOrCreateThreadID(Value *Ident)
Return the current thread ID.
LLVM_ABI InsertPointOrErrorTy createMaster(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp master'.
LLVM_ABI InsertPointOrErrorTy createTargetData(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, Value *DeviceID, Value *IfCond, TargetDataInfo &Info, GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB, omp::RuntimeFunction *MapperFunc=nullptr, function_ref< InsertPointOrErrorTy(InsertPointTy CodeGenIP, BodyGenTy BodyGenType)> BodyGenCB=nullptr, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr, Value *SrcLocInfo=nullptr)
Generator for 'omp target data'.
LLVM_ABI CallInst * createRuntimeFunctionCall(FunctionCallee Callee, ArrayRef< Value * > Args, StringRef Name="")
LLVM_ABI InsertPointOrErrorTy emitKernelLaunch(const LocationDescription &Loc, Value *OutlinedFnID, EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args, Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP)
Generate a target region entry call and host fallback call.
StringMap< GlobalVariable *, BumpPtrAllocator > InternalVars
An ordered map of auto-generated variables to their unique names.
LLVM_ABI InsertPointOrErrorTy createCancellationPoint(const LocationDescription &Loc, omp::Directive CanceledDirective)
Generator for 'omp cancellation point'.
LLVM_ABI CallInst * createOMPAlignedAlloc(const LocationDescription &Loc, Value *Align, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_align_alloc.
LLVM_ABI FunctionCallee createDispatchInitFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI InsertPointOrErrorTy createScan(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< llvm::Value * > ScanVars, ArrayRef< llvm::Type * > ScanVarsType, bool IsInclusive, ScanInfo *ScanRedInfo)
This directive split and directs the control flow to input phase blocks or scan phase blocks based on...
LLVM_ABI CallInst * createOMPFreeShared(const LocationDescription &Loc, Value *Addr, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_free_shared.
LLVM_ABI CallInst * createOMPInteropUse(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_use.
IRBuilder<>::InsertPoint InsertPointTy
Type used throughout for insertion points.
LLVM_ABI GlobalVariable * getOrCreateInternalVariable(Type *Ty, const StringRef &Name, std::optional< unsigned > AddressSpace={})
Gets (if variable with the given name already exist) or creates internal global variable with the spe...
LLVM_ABI GlobalVariable * createOffloadMapnames(SmallVectorImpl< llvm::Constant * > &Names, std::string VarName)
Create the global variable holding the offload names information.
LLVM_ABI InsertPointOrErrorTy createTask(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, bool Tied=true, Value *Final=nullptr, Value *IfCondition=nullptr, const DependenciesInfo &Dependencies={}, const AffinityData &Affinities={}, bool Mergeable=false, Value *EventHandle=nullptr, Value *Priority=nullptr, bool FreeAgent=false)
Generator for #omp taskloop
std::forward_list< ScanInfo > ScanInfos
Collection of owned ScanInfo objects that eventually need to be free'd.
static LLVM_ABI void writeTeamsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI Value * calculateCanonicalLoopTripCount(const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, const Twine &Name="loop")
Calculate the trip count of a canonical loop.
LLVM_ABI InsertPointOrErrorTy createBarrier(const LocationDescription &Loc, omp::Directive Kind, bool ForceSimpleCall=false, bool CheckCancelFlag=true)
Emitter methods for OpenMP directives.
LLVM_ABI void setCorrectMemberOfFlag(omp::OpenMPOffloadMappingFlags &Flags, omp::OpenMPOffloadMappingFlags MemberOfFlag)
Given an initial flag set, this function modifies it to contain the passed in MemberOfFlag generated ...
LLVM_ABI Error emitOffloadingArraysAndArgs(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info, TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, bool ForEndCall=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Allocates memory for and populates the arrays required for offloading (offload_{baseptrs|ptrs|mappers...
LLVM_ABI Constant * getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the default source location.
LLVM_ABI InsertPointOrErrorTy createCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst)
Generator for 'omp critical'.
LLVM_ABI void createError(const LocationDescription &Loc, bool IsFatal, Value *Message)
Generate a call to the runtime to emit the diagnostic of an OpenMP error directive with at(execution)...
LLVM_ABI void createOffloadEntry(Constant *ID, Constant *Addr, uint64_t Size, int32_t Flags, GlobalValue::LinkageTypes, StringRef Name="")
Creates offloading entry for the provided entry ID ID, address Addr, size Size, and flags Flags.
static LLVM_ABI unsigned getOpenMPDefaultSimdAlign(const Triple &TargetTriple, const StringMap< bool > &Features)
Get the default alignment value for given target.
LLVM_ABI unsigned getFlagMemberOffset()
Get the offset of the OMP_MAP_MEMBER_OF field.
LLVM_ABI InsertPointOrErrorTy createAtomicCapture(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, AtomicOpValue &V, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: — Only Scalar data types V = X; X = X BinOp Expr ,...
LLVM_ABI CanonicalLoopInfo * createLoopSkeleton(DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore, BasicBlock *PostInsertBefore, const Twine &Name={}, bool IsCollapsed=false)
Create the control flow structure of a canonical OpenMP loop.
LLVM_ABI void createOffloadEntriesAndInfoMetadata(EmitMetadataErrorReportFunctionTy &ErrorReportFunction)
LLVM_ABI void applySimd(CanonicalLoopInfo *Loop, MapVector< Value *, Value * > AlignedVars, Value *IfCond, omp::OrderKind Order, ConstantInt *Simdlen, ConstantInt *Safelen)
Add metadata to simd-ize a loop.
SmallVector< std::unique_ptr< OutlineInfo >, 16 > OutlineInfos
Collection of regions that need to be outlined during finalization.
LLVM_ABI InsertPointOrErrorTy createAtomicUpdate(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: X = X BinOp Expr ,or X = Expr BinOp X For complex Operations: X = ...
std::function< std::tuple< std::string, uint64_t >()> FileIdentifierInfoCallbackTy
bool isLastFinalizationInfoCancellable(omp::Directive DK)
Return true if the last entry in the finalization stack is of kind DK and cancellable.
LLVM_ABI InsertPointTy emitTargetKernel(const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return, Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads, Value *HostPtr, ArrayRef< Value * > KernelArgs)
Generate a target region entry call.
LLVM_ABI GlobalVariable * createOffloadMaptypes(SmallVectorImpl< uint64_t > &Mappings, std::string VarName)
Create the global variable holding the offload mappings information.
LLVM_ABI Expected< Function * > emitUserDefinedMapper(function_ref< MapInfosOrErrorTy(InsertPointTy CodeGenIP, llvm::Value *PtrPHI, llvm::Value *BeginArg)> PrivAndGenMapInfoCB, llvm::Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB, bool PreserveMemberOfFlags=false, bool PropagatePresentToPointee=false)
Emit the user-defined mapper function.
LLVM_ABI CallInst * createCachedThreadPrivate(const LocationDescription &Loc, llvm::Value *Pointer, llvm::ConstantInt *Size, const llvm::Twine &Name=Twine(""))
Create a runtime call for kmpc_threadprivate_cached.
IRBuilder Builder
The LLVM-IR Builder used to create IR.
LLVM_ABI GlobalValue * createGlobalFlag(unsigned Value, StringRef Name)
Create a hidden global flag Name in the module with initial value Value.
LLVM_ABI void emitOffloadingArraysArgument(IRBuilderBase &Builder, OpenMPIRBuilder::TargetDataRTArgs &RTArgs, OpenMPIRBuilder::TargetDataInfo &Info, bool ForEndCall=false)
Emit the arguments to be passed to the runtime library based on the arrays of base pointers,...
LLVM_ABI InsertPointOrErrorTy createMasked(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, Value *Filter)
Generator for 'omp masked'.
LLVM_ABI Expected< CanonicalLoopInfo * > createCanonicalLoop(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *TripCount, const Twine &Name="loop")
Generator for the control flow structure of an OpenMP canonical loop.
function_ref< Expected< InsertPointTy >( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value *DestPtr, Value *SrcPtr)> TaskDupCallbackTy
Callback type for task duplication function code generation.
LLVM_ABI Value * getSizeInBytes(Value *BasePtr)
Computes the size of type in bytes.
llvm::function_ref< llvm::Error( InsertPointTy BodyIP, llvm::Value *LinearIV)> IteratorBodyGenTy
LLVM_ABI FunctionCallee createDispatchDeinitFunction()
Returns __kmpc_dispatch_deinit runtime function.
LLVM_ABI void registerTargetGlobalVariable(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy, Constant *Addr)
Registers a target variable for device or host.
LLVM_ABI void createTargetDeinit(const LocationDescription &Loc, int32_t TeamsReductionDataSize=0)
Create a runtime call for kmpc_target_deinit.
BodyGenTy
Type of BodyGen to use for region codegen.
LLVM_ABI CanonicalLoopInfo * fuseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops)
Fuse a sequence of loops.
LLVM_ABI void emitX86DeclareSimdFunction(llvm::Function *Fn, unsigned NumElements, const llvm::APSInt &VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch)
Emit x86 vector-function ABI attributes for a declare simd function.
SmallVector< llvm::Function *, 16 > ConstantAllocaRaiseCandidates
A collection of candidate target functions that's constant allocas will attempt to be raised on a cal...
OffloadEntriesInfoManager OffloadInfoManager
Info manager to keep track of target regions.
static LLVM_ABI std::pair< int32_t, int32_t > readTeamBoundsForKernel(const Triple &T, Function &Kernel)
Read/write a bounds on teams for Kernel.
const std::string ompOffloadInfoName
OMP Offload Info Metadata name string.
LLVM_ABI InsertPointOrErrorTy applyWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP, bool NeedsBarrier, llvm::omp::ScheduleKind SchedKind=llvm::omp::OMP_SCHEDULE_Default, Value *ChunkSize=nullptr, bool HasSimdModifier=false, bool HasMonotonicModifier=false, bool HasNonmonotonicModifier=false, bool HasOrderedClause=false, omp::WorksharingLoopType LoopType=omp::WorksharingLoopType::ForStaticLoop, bool NoLoop=false, bool HasDistSchedule=false, Value *DistScheduleChunkSize=nullptr, bool NeedsLastIter=false)
Modifies the canonical loop to be a workshare loop.
Expected< InsertPointTy > InsertPointOrErrorTy
Type used to represent an insertion point or an error value.
LLVM_ABI InsertPointTy createCopyPrivate(const LocationDescription &Loc, llvm::Value *BufSize, llvm::Value *CpyBuf, llvm::Value *CpyFn, llvm::Value *DidIt)
Generator for __kmpc_copyprivate.
LLVM_ABI InsertPointOrErrorTy createSections(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< StorableBodyGenCallbackTy > SectionCBs, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait)
Generator for 'omp sections'.
std::function< void(EmitMetadataErrorKind, TargetRegionEntryInfo)> EmitMetadataErrorReportFunctionTy
Callback function type.
function_ref< InsertPointOrErrorTy( Argument &Arg, Value *Input, Value *&RetVal, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< InsertPointTy > DeallocIPs)> TargetGenArgAccessorsCallbackTy
LLVM_ABI Expected< ScanInfo * > scanInfoInitialize()
Creates a ScanInfo object, allocates and returns the pointer.
LLVM_ABI InsertPointOrErrorTy emitTargetTask(TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc, OpenMPIRBuilder::InsertPointTy AllocaIP, const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs, bool HasNoWait)
Generate a target-task for the target construct.
LLVM_ABI InsertPointTy createAtomicRead(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic Read for : V = X — Only Scalar data types.
function_ref< Error(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> BodyGenCallbackTy
Callback type for body (=inner region) code generation.
bool updateToLocation(const LocationDescription &Loc)
Update the internal location to Loc.
LLVM_ABI void createFlush(const LocationDescription &Loc)
Generator for 'omp flush'.
LLVM_ABI Constant * getAddrOfDeclareTargetVar(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, Type *LlvmPtrTy, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage)
Retrieve (or create if non-existent) the address of a declare target variable, used in conjunction wi...
LLVM_ABI void createTaskwait(const LocationDescription &Loc, DependenciesInfo Dependencies={}, bool IsNowait=false)
Generator for 'omp taskwait'.
origPtr *with the address space normalization required by the runtime entry point *The NULL descriptor makes the runtime walk the enclosing taskgroups to *find the matching task_reduction registration for the item The lookups *are emitted at p Loc
EmitMetadataErrorKind
The kind of errors that can occur when emitting the offload entries and metadata.
unsigned getOpcode() const
Return the opcode for this Instruction or ConstantExpr.
Definition Operator.h:43
The optimization diagnostic interface.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
Pseudo-analysis pass that exposes the PassInstrumentation to pass managers.
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:887
PostDominatorTree Class - Concrete subclass of DominatorTree that is used to compute the post-dominat...
Analysis pass that exposes the ScalarEvolution for a function.
LLVM_ABI ScalarEvolution run(Function &F, FunctionAnalysisManager &AM)
The main scalar evolution driver.
ScanInfo holds the information to assist in lowering of Scan reduction.
llvm::SmallDenseMap< llvm::Value *, llvm::Value * > * ScanBuffPtrs
Maps the private reduction variable to the pointer of the temporary buffer.
llvm::BasicBlock * OMPScanLoopExit
Exit block of loop body.
llvm::Value * IV
Keeps track of value of iteration variable for input/scan loop to be used for Scan directive lowering...
llvm::BasicBlock * OMPAfterScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanInit
Block before loop body where scan initializations are done.
llvm::BasicBlock * OMPBeforeScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanFinish
Block after loop body where scan finalizations are done.
llvm::Value * Span
Stores the span of canonical loop being lowered to be used for temporary buffer allocation or Finaliz...
bool OMPFirstScanLoop
If true, it indicates Input phase is lowered; else it indicates ScanPhase is lowered.
llvm::BasicBlock * OMPScanDispatch
Controls the flow to before or after scan blocks.
A vector that has set insertion semantics.
Definition SetVector.h:57
bool remove_if(UnaryPredicate P)
Remove items from the set vector based on a predicate function.
Definition SetVector.h:236
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallBitVector & set()
bool test(unsigned Idx) const
Returns true if bit Idx is set.
bool all() const
Returns true if all bits are set.
bool any() const
Returns true if any bit is set.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
void append(StringRef RHS)
Append from a StringRef.
Definition SmallString.h:68
StringRef str() const
Explicit conversion to StringRef.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void resize(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
void setAlignment(Align Align)
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this store instruction.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:129
ValueTy lookup(StringRef Key) const
lookup - Return the entry for the specified key, or a default constructed value if no such entry exis...
Definition StringMap.h:250
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
size_t count(char C) const
Return the number of occurrences of C in the string.
Definition StringRef.h:471
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition StringRef.h:270
StringRef drop_back(size_t N=1) const
Return a StringRef equal to 'this' but with the last N elements dropped.
Definition StringRef.h:642
Class to represent struct types.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:467
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:662
Type * getElementType(unsigned N) const
Multiway switch.
LLVM_ABI void addCase(ConstantInt *OnVal, BasicBlock *Dest)
Add an entry to the switch instruction.
Analysis pass providing the TargetTransformInfo.
LLVM_ABI Result run(const Function &F, FunctionAnalysisManager &)
Analysis pass providing the TargetLibraryInfo.
Target - Wrapper for Target specific information.
TargetMachine * createTargetMachine(const Triple &TT, StringRef CPU, StringRef Features, const TargetOptions &Options, std::optional< Reloc::Model > RM, std::optional< CodeModel::Model > CM=std::nullopt, CodeGenOptLevel OL=CodeGenOptLevel::Default, bool JIT=false) const
createTargetMachine - Create a target specific machine implementation for the specified Triple.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isPPC() const
Tests whether the target is PowerPC (32- or 64-bit LE or BE).
Definition Triple.h:1141
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1198
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1212
bool isSystemZ() const
Tests whether the target is SystemZ.
Definition Triple.h:1195
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
bool isFloatTy() const
Return true if this is 'float', a 32-bit IEEE fp type.
Definition Type.h:155
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 isDoubleTy() const
Return true if this is 'double', a 64-bit IEEE fp type.
Definition Type.h:158
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:390
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:549
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:1000
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:143
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:181
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:708
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:557
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:315
An efficient, type-erasing, non-owning reference to a callable.
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
A raw_ostream that writes to an SmallVector or SmallString.
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
CallInst * Call
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ SPIR_KERNEL
Used for SPIR kernel functions.
@ PTX_Kernel
Call to a PTX kernel. Passes all arguments in parameter space.
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:83
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
Flag
These should be considered private to the implementation of the MCInstrDesc class.
constexpr StringLiteral MaxNTID("nvvm.maxntid")
constexpr StringLiteral MaxClusterRank("nvvm.maxclusterrank")
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
LLVM_ABI GlobalVariable * emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr=nullptr)
Definition Utility.cpp:104
OpenMPOffloadMappingFlags
Values for bit flags used to specify the mapping type for offloading.
@ OMP_MAP_PTR_AND_OBJ
The element being mapped is a pointer-pointee pair; both the pointer and the pointee should be mapped...
@ OMP_MAP_MEMBER_OF
The 16 MSBs of the flags indicate whether the entry is member of some struct/class.
IdentFlag
IDs for all omp runtime library ident_t flag encodings (see their defintion in openmp/runtime/src/kmp...
RuntimeFunction
IDs for all omp runtime library (RTL) functions.
constexpr const GV & getAMDGPUGridValues()
static constexpr GV SPIRVGridValues
For generic SPIR-V GPUs.
OMPDynGroupprivateFallbackType
The fallback types for the dyn_groupprivate clause.
static constexpr GV NVPTXGridValues
For Nvidia GPUs.
@ OMP_TGT_EXEC_MODE_SPMD_NO_LOOP
Function * Kernel
Summary of a kernel (=entry point for target offloading).
Definition OpenMPOpt.h:21
WorksharingLoopType
A type of worksharing loop construct.
OMPAtomicCompareOp
Atomic compare operations. Currently OpenMP only supports ==, >, and <.
EnumSet< Property > Properties
NodeAddr< PhiNode * > Phi
Definition RDFGraph.h:390
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:316
LLVM_ABI BasicBlock * splitBBWithSuffix(IRBuilderBase &Builder, bool CreateBranch, llvm::Twine Suffix=".split")
Like splitBB, but reuses the current block's name for the new name.
@ Offset
Definition DWP.cpp:577
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
Definition STLExtras.h:846
LLVM_ABI unsigned computeUnrollCount(Loop *L, const TargetTransformInfo &TTI, DominatorTree &DT, LoopInfo *LI, AssumptionCache *AC, ScalarEvolution &SE, const SmallPtrSetImpl< const Value * > &EphValues, OptimizationRemarkEmitter *ORE, unsigned TripCount, unsigned MaxTripCount, bool MaxOrZero, unsigned TripMultiple, const UnrollCostEstimator &UCE, TargetTransformInfo::UnrollingPreferences &UP, TargetTransformInfo::PeelingPreferences &PP)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1755
hash_code hash_value(const FixedPointSemantics &Val)
LLVM_ABI Expected< std::unique_ptr< Module > > parseBitcodeFile(MemoryBufferRef Buffer, LLVMContext &Context, ParserCallbacks Callbacks={})
Read the specified bitcode file, returning the module.
detail::zippy< detail::zip_first, T, U, Args... > zip_equal(T &&t, U &&u, Args &&...args)
zip iterator that assumes that all iteratees have the same length.
Definition STLExtras.h:856
LLVM_ABI BasicBlock * CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap, const Twine &NameSuffix="", Function *F=nullptr, ClonedCodeInfo *CodeInfo=nullptr, bool MapAtoms=true)
Return a copy of the specified basic block, but without embedding the block into a particular functio...
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2570
unsigned getPointerAddressSpace(const Type *T)
Definition SPIRVUtils.h:393
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto successors(const MachineBasicBlock *BB)
@ Load
The value being inserted comes from a load (InsertElement only).
@ Store
The extracted value is stored (ExtractElement only).
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
constexpr from_range_t from_range
auto dyn_cast_if_present(const Y &Val)
dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a null (or none in the case ...
Definition Casting.h:732
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
LLVM_ABI BasicBlock * splitBB(IRBuilderBase::InsertPoint IP, bool CreateBranch, DebugLoc DL, llvm::Twine Name={})
Split a BasicBlock at an InsertPoint, even if the block is degenerate (missing the terminator).
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h:2224
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
LLVM_ABI TargetTransformInfo::UnrollingPreferences gatherUnrollingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, llvm::OptimizationRemarkEmitter &ORE, int OptLevel, std::optional< unsigned > UserThreshold, std::optional< bool > UserAllowPartial, std::optional< bool > UserRuntime, std::optional< bool > UserUpperBound, std::optional< unsigned > UserFullUnrollMaxCount)
Gather the various unrolling parameters based on the defaults, compiler flags, TTI overrides and user...
std::string utostr(uint64_t X, bool isNeg=false)
void * PointerTy
ErrorOr< T > expectedToErrorOrAndEmitErrors(LLVMContext &Ctx, Expected< T > Val)
bool isa_and_nonnull(const Y &Val)
Definition Casting.h:676
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
LLVM_ABI bool convertUsersOfConstantsToInstructions(ArrayRef< Constant * > Consts, Function *RestrictToFunc=nullptr, bool RemoveDeadConstants=true, bool IncludeSelf=false)
Replace constant expressions users of the given constants with instructions.
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
Definition MathExtras.h:326
auto reverse(ContainerTy &&C)
Definition STLExtras.h:408
LLVM_ABI TargetTransformInfo::PeelingPreferences gatherPeelingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, std::optional< bool > UserAllowPeeling, std::optional< bool > UserAllowProfileBasedPeeling, bool UnrollingSpecficValues=false)
LLVM_ABI void SplitBlockAndInsertIfThenElse(Value *Cond, BasicBlock::iterator SplitBefore, Instruction **ThenTerm, Instruction **ElseTerm, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr)
SplitBlockAndInsertIfThenElse is similar to SplitBlockAndInsertIfThen, but also creates the ElseBlock...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1769
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:227
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
AtomicOrdering
Atomic ordering for LLVM's memory model.
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h:389
TargetTransformInfo TTI
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
LLVM_ABI bool MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, MemoryDependenceResults *MemDep=nullptr, bool PredecessorWithTwoSuccessors=false, DominatorTree *DT=nullptr)
Attempts to merge a block into its predecessor, if possible.
@ Mul
Product of integers.
@ Add
Sum of integers.
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
IntPtrTy
Definition InstrProf.h:82
DWARFExpression::Operation Op
LLVM_ABI void remapInstructionsInBlocks(ArrayRef< BasicBlock * > Blocks, ValueToValueMapTy &VMap)
Remaps instructions in Blocks using the mapping in VMap.
ArrayRef(const T &OneElt) -> ArrayRef< T >
OutputIt copy(R &&Range, OutputIt Out)
Definition STLExtras.h:1901
constexpr unsigned BitWidth
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
LLVM_ABI void spliceBB(IRBuilderBase::InsertPoint IP, BasicBlock *New, bool CreateBranch, DebugLoc DL)
Move the instruction after an InsertPoint to the beginning of another BasicBlock.
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
constexpr auto seq(T Begin, T End)
Iterate over an integral type from Begin up to - but not including - End.
Definition Sequence.h:341
auto predecessors(const MachineBasicBlock *BB)
auto filter_to_vector(ContainerTy &&C, PredicateFn &&Pred)
Filter a range to a SmallVector with the element types deduced.
PointerUnion< const Value *, const PseudoSourceValue * > ValueType
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
LLVM_ABI Constant * ConstantFoldInsertValueInstruction(Constant *Agg, Constant *Val, ArrayRef< unsigned > Idxs)
Attempt to constant fold an insertvalue instruction with the specified operands and indices.
@ Continue
Definition DWP.h:26
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
LLVM_ABI void DeleteDeadBlocks(ArrayRef< BasicBlock * > BBs, DomTreeUpdater *DTU=nullptr, bool KeepOneInputPHIs=false)
Delete the specified blocks from BB.
bool to_integer(StringRef S, N &Num, unsigned Base=0)
Convert the string S to an integer of the specified type using the radix Base. If Base is 0,...
@ End
Run the machine verifier once, at the end of the pipeline.
Definition CodeGen.h:267
static auto filterDbgVars(iterator_range< simple_ilist< DbgRecord >::iterator > R)
Filter the DbgRecord range to DbgVariableRecord types only and downcast.
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
static LLVM_ABI void collectEphemeralValues(const Loop *L, AssumptionCache *AC, SmallPtrSetImpl< const Value * > &EphValues)
Collect a loop's ephemeral values (those used only by an assume or similar intrinsics in the loop).
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.
Definition Alignment.h:111
A struct to pack the relevant information for an OpenMP affinity clause.
a struct to pack relevant information while generating atomic Ops
A struct to pack the relevant information for an OpenMP depend clause.
omp::RTLDependenceKindTy DepKind
A struct to pack static and dynamic dependency information for a task.
LLVM_ABI Error mergeFiniBB(IRBuilderBase &Builder, BasicBlock *ExistingFiniBB)
For cases where there is an unavoidable existing finalization block (e.g.
LLVM_ABI Expected< BasicBlock * > getFiniBB(IRBuilderBase &Builder)
The basic block to which control should be transferred to implement the FiniCB.
Description of a LLVM-IR insertion point (IP) and a debug/source location (filename,...
This structure contains combined information generated for mappable clauses, including base pointers,...
MapDeviceInfoArrayTy DevicePointers
StructNonContiguousInfo NonContigInfo
Helper that contains information about regions we need to outline during finalization.
void collectBlocks(SmallPtrSetImpl< BasicBlock * > &BlockSet, SmallVectorImpl< BasicBlock * > &BlockVector)
Collect all blocks in between EntryBB and ExitBB in both the given vector and set.
virtual std::unique_ptr< CodeExtractor > createCodeExtractor(ArrayRef< BasicBlock * > Blocks, bool ArgsInZeroAddressSpace, Twine Suffix=Twine(""))
Create a CodeExtractor instance based on the information stored in this structure,...
Information about an OpenMP reduction.
EvalKind EvaluationKind
Reduction evaluation kind - scalar, complex or aggregate.
ReductionGenAtomicCBTy AtomicReductionGen
Callback for generating the atomic reduction body, may be null.
ReductionGenCBTy ReductionGen
Callback for generating the reduction body.
Value * Variable
Reduction variable of pointer type.
Value * PrivateVariable
Thread-private partial reduction variable.
ReductionGenClangCBTy ReductionGenClang
Clang callback for generating the reduction body.
Type * ElementType
Reduction element type, must match pointee type of variable.
ReductionGenDataPtrPtrCBTy DataPtrPtrGen
Container for the arguments used to pass data to the runtime library.
Value * SizesArray
The array of sizes passed to the runtime library.
Value * PointersArray
The array of section pointers passed to the runtime library.
Value * MappersArray
The array of user-defined mappers passed to the runtime library.
Value * MapTypesArrayEnd
The array of map types passed to the runtime library for the end of the region, or nullptr if there a...
Value * BasePointersArray
The array of base pointer passed to the runtime library.
Value * MapTypesArray
The array of map types passed to the runtime library for the beginning of the region or for the entir...
Value * MapNamesArray
The array of original declaration names of mapped pointers sent to the runtime library for debugging.
Data structure that contains the needed information to construct the kernel args vector.
bool StrictBlocks
True if the kernel strictly requires the number of blocks and threads above to run.
ArrayRef< Value * > NumThreads
The number of threads.
TargetDataRTArgs RTArgs
Arguments passed to the runtime library.
Value * NumIterations
The number of iterations.
Value * DynCGroupMem
The size of the dynamic shared memory.
unsigned NumTargetItems
Number of arguments passed to the runtime library.
bool HasNoWait
True if the kernel has 'no wait' clause.
ArrayRef< Value * > NumTeams
The number of teams.
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback
The fallback mechanism for the shared memory.
Container to pass the default attributes with which a kernel must be launched, used to set kernel att...
Container to pass LLVM IR runtime values or constants related to the number of teams and threads with...
Value * DeviceID
Device ID value used in the kernel launch.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic kernel.
SmallVector< Value * > MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Data structure to contain the information needed to uniquely identify a target entry.
static LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, StringRef ParentName, unsigned DeviceID, unsigned FileID, unsigned Line, unsigned Count)
static constexpr const char * KernelNamePrefix
The prefix used for kernel names.
static LLVM_ABI const Target * lookupTarget(const Triple &TheTriple, std::string &Error)
lookupTarget - Lookup a target based on a target triple.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
bool Force
Apply loop unroll on any kind of loop (mainly to loops that fail runtime unrolling).
unsigned PartialOptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size, like OptSizeThreshold,...
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
unsigned OptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size (set to UINT_MAX to disable).
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...