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"
28#include "llvm/IR/Attributes.h"
29#include "llvm/IR/BasicBlock.h"
30#include "llvm/IR/CFG.h"
31#include "llvm/IR/CallingConv.h"
32#include "llvm/IR/Constant.h"
33#include "llvm/IR/Constants.h"
34#include "llvm/IR/DIBuilder.h"
37#include "llvm/IR/Function.h"
39#include "llvm/IR/IRBuilder.h"
41#include "llvm/IR/LLVMContext.h"
42#include "llvm/IR/MDBuilder.h"
43#include "llvm/IR/Metadata.h"
45#include "llvm/IR/PassManager.h"
47#include "llvm/IR/Value.h"
49#include "llvm/Support/Error.h"
56
57#include <cstdint>
58#include <optional>
59
60#define DEBUG_TYPE "openmp-ir-builder"
61
62using namespace llvm;
63using namespace omp;
64
65static cl::opt<bool>
66 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
67 cl::desc("Use optimistic attributes describing "
68 "'as-if' properties of runtime calls."),
69 cl::init(false));
70
72 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
73 cl::desc("Use a default max threads if none is provided."), cl::init(true));
74
75#ifndef NDEBUG
76/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
77/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
78/// an InsertPoint stores the instruction before something is inserted. For
79/// instance, if both point to the same instruction, two IRBuilders alternating
80/// creating instruction will cause the instructions to be interleaved.
83 if (!IP1.isValid() || !IP2.isValid())
84 return false;
85 return IP1 == IP2;
86}
87
89 // Valid ordered/unordered and base algorithm combinations.
90 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
91 case OMPScheduleType::UnorderedStaticChunked:
92 case OMPScheduleType::UnorderedStatic:
93 case OMPScheduleType::UnorderedDynamicChunked:
94 case OMPScheduleType::UnorderedGuidedChunked:
95 case OMPScheduleType::UnorderedRuntime:
96 case OMPScheduleType::UnorderedAuto:
97 case OMPScheduleType::UnorderedTrapezoidal:
98 case OMPScheduleType::UnorderedGreedy:
99 case OMPScheduleType::UnorderedBalanced:
100 case OMPScheduleType::UnorderedGuidedIterativeChunked:
101 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
102 case OMPScheduleType::UnorderedSteal:
103 case OMPScheduleType::UnorderedStaticBalancedChunked:
104 case OMPScheduleType::UnorderedGuidedSimd:
105 case OMPScheduleType::UnorderedRuntimeSimd:
106 case OMPScheduleType::OrderedStaticChunked:
107 case OMPScheduleType::OrderedStatic:
108 case OMPScheduleType::OrderedDynamicChunked:
109 case OMPScheduleType::OrderedGuidedChunked:
110 case OMPScheduleType::OrderedRuntime:
111 case OMPScheduleType::OrderedAuto:
112 case OMPScheduleType::OrderdTrapezoidal:
113 case OMPScheduleType::NomergeUnorderedStaticChunked:
114 case OMPScheduleType::NomergeUnorderedStatic:
115 case OMPScheduleType::NomergeUnorderedDynamicChunked:
116 case OMPScheduleType::NomergeUnorderedGuidedChunked:
117 case OMPScheduleType::NomergeUnorderedRuntime:
118 case OMPScheduleType::NomergeUnorderedAuto:
119 case OMPScheduleType::NomergeUnorderedTrapezoidal:
120 case OMPScheduleType::NomergeUnorderedGreedy:
121 case OMPScheduleType::NomergeUnorderedBalanced:
122 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
123 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
124 case OMPScheduleType::NomergeUnorderedSteal:
125 case OMPScheduleType::NomergeOrderedStaticChunked:
126 case OMPScheduleType::NomergeOrderedStatic:
127 case OMPScheduleType::NomergeOrderedDynamicChunked:
128 case OMPScheduleType::NomergeOrderedGuidedChunked:
129 case OMPScheduleType::NomergeOrderedRuntime:
130 case OMPScheduleType::NomergeOrderedAuto:
131 case OMPScheduleType::NomergeOrderedTrapezoidal:
132 case OMPScheduleType::OrderedDistributeChunked:
133 case OMPScheduleType::OrderedDistribute:
134 break;
135 default:
136 return false;
137 }
138
139 // Must not set both monotonicity modifiers at the same time.
140 OMPScheduleType MonotonicityFlags =
141 SchedType & OMPScheduleType::MonotonicityMask;
142 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
143 return false;
144
145 return true;
146}
147#endif
148
149/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
150/// debug location when the insert point is at the end of a block. It picks a
151/// location scoped to the current function: the block's last instruction
152/// location if the block is non-empty, otherwise a location synthesized from
153/// the function's subprogram (when the function has debug info).
156 Builder.restoreIP(IP);
157 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
158 // set the debug location from that instruction, so leave it alone.
159 llvm::BasicBlock *BB = Builder.GetInsertBlock();
160 if (Builder.GetInsertPoint() != BB->end())
161 return;
162
163 // At the end of a block, pick a location guaranteed to belong to the current
164 // insertion function's subprogram. Prefer the block's own last instruction;
165 // otherwise synthesize a location from the function's subprogram.
166 if (!BB->empty())
167 Builder.SetCurrentDebugLocation(BB->back().getDebugLoc());
168 else if (llvm::DISubprogram *FSP =
169 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
170 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
171 Builder.SetCurrentDebugLocation(
172 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
173 }
174}
175
176static bool hasGridValue(const Triple &T) {
177 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
178}
179
180static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
181 if (T.isAMDGPU()) {
182 StringRef Features =
183 Kernel->getFnAttribute("target-features").getValueAsString();
184 if (Features.count("+wavefrontsize64"))
187 }
188 if (T.isNVPTX())
190 if (T.isSPIRV())
192 llvm_unreachable("No grid value available for this architecture!");
193}
194
195/// Determine which scheduling algorithm to use, determined from schedule clause
196/// arguments.
197static OMPScheduleType
198getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
199 bool HasSimdModifier, bool HasDistScheduleChunks) {
200 // Currently, the default schedule it static.
201 switch (ClauseKind) {
202 case OMP_SCHEDULE_Default:
203 case OMP_SCHEDULE_Static:
204 return HasChunks ? OMPScheduleType::BaseStaticChunked
205 : OMPScheduleType::BaseStatic;
206 case OMP_SCHEDULE_Dynamic:
207 return OMPScheduleType::BaseDynamicChunked;
208 case OMP_SCHEDULE_Guided:
209 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
210 : OMPScheduleType::BaseGuidedChunked;
211 case OMP_SCHEDULE_Auto:
213 case OMP_SCHEDULE_Runtime:
214 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
215 : OMPScheduleType::BaseRuntime;
216 case OMP_SCHEDULE_Distribute:
217 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
218 : OMPScheduleType::BaseDistribute;
219 }
220 llvm_unreachable("unhandled schedule clause argument");
221}
222
223/// Adds ordering modifier flags to schedule type.
224static OMPScheduleType
226 bool HasOrderedClause) {
227 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
228 OMPScheduleType::None &&
229 "Must not have ordering nor monotonicity flags already set");
230
231 OMPScheduleType OrderingModifier = HasOrderedClause
232 ? OMPScheduleType::ModifierOrdered
233 : OMPScheduleType::ModifierUnordered;
234 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
235
236 // Unsupported combinations
237 if (OrderingScheduleType ==
238 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
239 return OMPScheduleType::OrderedGuidedChunked;
240 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
241 OMPScheduleType::ModifierOrdered))
242 return OMPScheduleType::OrderedRuntime;
243
244 return OrderingScheduleType;
245}
246
247/// Adds monotonicity modifier flags to schedule type.
248static OMPScheduleType
250 bool HasSimdModifier, bool HasMonotonic,
251 bool HasNonmonotonic, bool HasOrderedClause) {
252 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
253 OMPScheduleType::None &&
254 "Must not have monotonicity flags already set");
255 assert((!HasMonotonic || !HasNonmonotonic) &&
256 "Monotonic and Nonmonotonic are contradicting each other");
257
258 if (HasMonotonic) {
259 return ScheduleType | OMPScheduleType::ModifierMonotonic;
260 } else if (HasNonmonotonic) {
261 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
262 } else {
263 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
264 // If the static schedule kind is specified or if the ordered clause is
265 // specified, and if the nonmonotonic modifier is not specified, the
266 // effect is as if the monotonic modifier is specified. Otherwise, unless
267 // the monotonic modifier is specified, the effect is as if the
268 // nonmonotonic modifier is specified.
269 OMPScheduleType BaseScheduleType =
270 ScheduleType & ~OMPScheduleType::ModifierMask;
271 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
272 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
273 HasOrderedClause) {
274 // The monotonic is used by default in openmp runtime library, so no need
275 // to set it.
276 return ScheduleType;
277 } else {
278 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
279 }
280 }
281}
282
283/// Determine the schedule type using schedule and ordering clause arguments.
284static OMPScheduleType
285computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
286 bool HasSimdModifier, bool HasMonotonicModifier,
287 bool HasNonmonotonicModifier, bool HasOrderedClause,
288 bool HasDistScheduleChunks) {
290 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
291 OMPScheduleType OrderedSchedule =
292 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
294 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
295 HasNonmonotonicModifier, HasOrderedClause);
296
298 return Result;
299}
300
301/// Given a function, if it represents the entry point of a target kernel, this
302/// returns the execution mode flags associated with that kernel.
303static std::optional<omp::OMPTgtExecModeFlags>
305 CallInst *TargetInitCall = nullptr;
306 for (Instruction &Inst : Kernel.getEntryBlock()) {
307 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
308 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
309 TargetInitCall = Call;
310 break;
311 }
312 }
313 }
314
315 if (!TargetInitCall)
316 return std::nullopt;
317
318 // Get the kernel mode information from the global variable associated to the
319 // first argument to the call to __kmpc_target_init. Refer to
320 // createTargetInit() to see how this is initialized.
321 Value *InitOperand = TargetInitCall->getArgOperand(0);
322 GlobalVariable *KernelEnv = nullptr;
323 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
324 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
325 else
326 KernelEnv = cast<GlobalVariable>(InitOperand);
327 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
328 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
329 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
330 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
331}
332
333static bool isGenericKernel(Function &Fn) {
334 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
336 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
337}
338
339/// Make \p Source branch to \p Target.
340///
341/// Handles two situations:
342/// * \p Source already has an unconditional branch.
343/// * \p Source is a degenerate block (no terminator because the BB is
344/// the current head of the IR construction).
346 if (Instruction *Term = Source->getTerminatorOrNull()) {
347 auto *Br = cast<UncondBrInst>(Term);
348 BasicBlock *Succ = Br->getSuccessor();
349 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
350 Br->setSuccessor(Target);
351 return;
352 }
353
354 auto *NewBr = UncondBrInst::Create(Target, Source);
355 NewBr->setDebugLoc(DL);
356}
357
359 bool CreateBranch, DebugLoc DL) {
360 assert(New->getFirstInsertionPt() == New->begin() &&
361 "Target BB must not have PHI nodes");
362
363 // Move instructions to new block.
364 BasicBlock *Old = IP.getNodeParent();
365 // If the `Old` block is empty then there are no instructions to move. But in
366 // the new debug scheme, it could have trailing debug records which will be
367 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
368 // reasons:
369 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
370 // 2. Even if `New` is not empty, the rationale to move those records to `New`
371 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
372 // assumes that `Old` is optimized out and is going away. This is not the case
373 // here. The `Old` block is still being used e.g. a branch instruction is
374 // added to it later in this function.
375 // So we call `BasicBlock::splice` only when `Old` is not empty.
376 if (!Old->empty())
377 New->splice(New->begin(), Old, IP, Old->end());
378
379 if (CreateBranch) {
380 auto *NewBr = UncondBrInst::Create(New, Old);
381 NewBr->setDebugLoc(DL);
382 }
383}
384
385void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
386 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
387 BasicBlock *Old = Builder.GetInsertBlock();
388
389 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
390 if (CreateBranch)
391 Builder.SetInsertPoint(Old->getTerminator());
392 else
393 Builder.SetInsertPoint(Old);
394
395 // SetInsertPoint also updates the Builder's debug location, but we want to
396 // keep the one the Builder was configured to use.
397 Builder.SetCurrentDebugLocation(DebugLoc);
398}
399
401 DebugLoc DL, llvm::Twine Name) {
402 BasicBlock *Old = IP.getNodeParent();
404 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
405 Old->getParent(), Old->getNextNode());
406 spliceBB(IP, New, CreateBranch, DL);
407 New->replaceSuccessorsPhiUsesWith(Old, New);
408 return New;
409}
410
411BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
412 llvm::Twine Name) {
413 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
414 BasicBlock *Old = Builder.GetInsertBlock();
415 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
416 if (CreateBranch)
417 Builder.SetInsertPoint(Old->getTerminator());
418 else
419 Builder.SetInsertPoint(Old);
420 // SetInsertPoint also updates the Builder's debug location, but we want to
421 // keep the one the Builder was configured to use.
422 Builder.SetCurrentDebugLocation(DebugLoc);
423 return New;
424}
425
426BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
427 llvm::Twine Name) {
428 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
429 BasicBlock *Old = Builder.GetInsertBlock();
430 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
431 if (CreateBranch)
432 Builder.SetInsertPoint(Old->getTerminator());
433 else
434 Builder.SetInsertPoint(Old);
435 // SetInsertPoint also updates the Builder's debug location, but we want to
436 // keep the one the Builder was configured to use.
437 Builder.SetCurrentDebugLocation(DebugLoc);
438 return New;
439}
440
442 llvm::Twine Suffix) {
443 BasicBlock *Old = Builder.GetInsertBlock();
444 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
445}
446
447// This function creates a fake integer value and a fake use for the integer
448// value. It returns the fake value created. This is useful in modeling the
449// extra arguments to the outlined functions.
451 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
453 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
454 const Twine &Name = "", bool AsPtr = true,
455 bool Is64Bit = false) {
456 Builder.restoreIP(OuterAllocaIP);
457 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
458 Instruction *FakeVal;
459 AllocaInst *FakeValAddr =
460 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
461 ToBeDeleted.push_back(FakeValAddr);
462
463 if (AsPtr) {
464 FakeVal = FakeValAddr;
465 // The runtime passes these extra arguments to the outlined function as
466 // generic pointers, so cast away a non-zero alloca address space.
467 if (FakeValAddr->getAddressSpace() != 0) {
468 FakeVal = cast<Instruction>(Builder.CreateAddrSpaceCast(
469 FakeValAddr, Builder.getPtrTy(), Name + ".ascast"));
470 ToBeDeleted.push_back(FakeVal);
471 }
472 } else {
473 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
474 ToBeDeleted.push_back(FakeVal);
475 }
476
477 // Generate a fake use of this value
478 Builder.restoreIP(InnerAllocaIP);
479 Instruction *UseFakeVal;
480 if (AsPtr) {
481 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
482 } else {
483 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
484 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
485 }
486 ToBeDeleted.push_back(UseFakeVal);
487 return FakeVal;
488}
489
490//===----------------------------------------------------------------------===//
491// OpenMPIRBuilderConfig
492//===----------------------------------------------------------------------===//
493
494namespace {
496/// Values for bit flags for marking which requires clauses have been used.
497enum OpenMPOffloadingRequiresDirFlags {
498 /// flag undefined.
499 OMP_REQ_UNDEFINED = 0x000,
500 /// no requires directive present.
501 OMP_REQ_NONE = 0x001,
502 /// reverse_offload clause.
503 OMP_REQ_REVERSE_OFFLOAD = 0x002,
504 /// unified_address clause.
505 OMP_REQ_UNIFIED_ADDRESS = 0x004,
506 /// unified_shared_memory clause.
507 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
508 /// dynamic_allocators clause.
509 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
510 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
511};
512
513class OMPCodeExtractor : public CodeExtractor {
514public:
515 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
516 DominatorTree *DT = nullptr, bool AggregateArgs = false,
517 BlockFrequencyInfo *BFI = nullptr,
518 BranchProbabilityInfo *BPI = nullptr,
519 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
520 bool AllowAlloca = false,
521 BasicBlock *AllocationBlock = nullptr,
522 ArrayRef<BasicBlock *> DeallocationBlocks = {},
523 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
524 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
525 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
526 ArgsInZeroAddressSpace),
527 OMPBuilder(OMPBuilder) {}
528
529 virtual ~OMPCodeExtractor() = default;
530
531protected:
532 OpenMPIRBuilder &OMPBuilder;
533};
534
535class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
536public:
537 using OMPCodeExtractor::OMPCodeExtractor;
538 virtual ~DeviceSharedMemCodeExtractor() = default;
539
540protected:
541 virtual Instruction *
542 allocateVar(IRBuilder<>::InsertPoint AllocaIP, DebugLoc DL, Type *VarType,
543 const Twine &Name = Twine(""),
544 AddrSpaceCastInst **CastedAlloc = nullptr) override {
545 return OMPBuilder.createOMPAllocShared({AllocaIP, DL}, VarType, Name);
546 }
547
548 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
549 DebugLoc DL, Value *Var,
550 Type *VarType) override {
551 return OMPBuilder.createOMPFreeShared({DeallocIP, DL}, Var, VarType);
552 }
553};
554
555/// Helper storing information about regions to outline using device shared
556/// memory for intermediate allocations.
557struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
558 OpenMPIRBuilder &OMPBuilder;
559
560 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
561 : OMPBuilder(OMPBuilder) {}
562 virtual ~DeviceSharedMemOutlineInfo() = default;
563
564 virtual std::unique_ptr<CodeExtractor>
565 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
566 bool ArgsInZeroAddressSpace,
567 Twine Suffix = Twine("")) override;
568};
569
570} // anonymous namespace
571
573 : RequiresFlags(OMP_REQ_UNDEFINED) {}
574
577 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
578 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
581 RequiresFlags(OMP_REQ_UNDEFINED) {
582 if (HasRequiresReverseOffload)
583 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
584 if (HasRequiresUnifiedAddress)
585 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
586 if (HasRequiresUnifiedSharedMemory)
587 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
588 if (HasRequiresDynamicAllocators)
589 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
590}
591
593 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
594}
595
597 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
598}
599
601 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
602}
603
605 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
606}
607
609 return hasRequiresFlags() ? RequiresFlags
610 : static_cast<int64_t>(OMP_REQ_NONE);
611}
612
614 if (Value)
615 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
616 else
617 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
618}
619
621 if (Value)
622 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
623 else
624 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
625}
626
628 if (Value)
629 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
630 else
631 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
632}
633
635 if (Value)
636 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
637 else
638 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
639}
640
641//===----------------------------------------------------------------------===//
642// OpenMPIRBuilder
643//===----------------------------------------------------------------------===//
644
647 SmallVector<Value *> &ArgsVector) {
649 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
650 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
651 constexpr size_t MaxDim = 3;
652 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
653
654 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
655
656 Value *DynCGroupMemFallbackFlag =
657 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
658 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
659
660 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
661 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
662
663 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
664 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
665
666 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
667 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
668 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
669
670 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
671
672 Value *NumTeams3D =
673 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
674 Value *NumThreads3D =
675 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
676 for (unsigned I :
677 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
678 NumTeams3D =
679 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
680 for (unsigned I :
681 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
682 NumThreads3D =
683 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
684
685 ArgsVector = {Version,
686 PointerNum,
687 KernelArgs.RTArgs.BasePointersArray,
688 KernelArgs.RTArgs.PointersArray,
689 KernelArgs.RTArgs.SizesArray,
690 KernelArgs.RTArgs.MapTypesArray,
691 KernelArgs.RTArgs.MapNamesArray,
692 KernelArgs.RTArgs.MappersArray,
693 KernelArgs.NumIterations,
694 Flags,
695 NumTeams3D,
696 NumThreads3D,
697 KernelArgs.DynCGroupMem};
698}
699
701 LLVMContext &Ctx = Fn.getContext();
702
703 // Get the function's current attributes.
704 auto Attrs = Fn.getAttributes();
705 auto FnAttrs = Attrs.getFnAttrs();
706 auto RetAttrs = Attrs.getRetAttrs();
708 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
709 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
710
711 // Add AS to FnAS while taking special care with integer extensions.
712 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
713 bool Param = true) -> void {
714 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
715 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
716 if (HasSignExt || HasZeroExt) {
717 assert(AS.getNumAttributes() == 1 &&
718 "Currently not handling extension attr combined with others.");
719 if (Param) {
720 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
721 FnAS = FnAS.addAttribute(Ctx, AK);
722 } else if (auto AK =
723 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
724 FnAS = FnAS.addAttribute(Ctx, AK);
725 } else {
726 FnAS = FnAS.addAttributes(Ctx, AS);
727 }
728 };
729
730#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
731#include "llvm/Frontend/OpenMP/OMPKinds.def"
732
733 // Add attributes to the function declaration.
734 switch (FnID) {
735#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
736 case Enum: \
737 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
738 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
739 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
740 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
741 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
742 break;
743#include "llvm/Frontend/OpenMP/OMPKinds.def"
744 default:
745 // Attributes are optional.
746 break;
747 }
748}
749
752 FunctionType *FnTy = nullptr;
753 Function *Fn = nullptr;
754
755 // Try to find the declation in the module first.
756 switch (FnID) {
757#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
758 case Enum: \
759 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
760 IsVarArg); \
761 Fn = M.getFunction(Str); \
762 break;
763#include "llvm/Frontend/OpenMP/OMPKinds.def"
764 }
765
766 if (!Fn) {
767 // Create a new declaration if we need one.
768 switch (FnID) {
769#define OMP_RTL(Enum, Str, ...) \
770 case Enum: \
771 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
772 break;
773#include "llvm/Frontend/OpenMP/OMPKinds.def"
774 }
775 Fn->setCallingConv(Config.getRuntimeCC());
776 // Add information if the runtime function takes a callback function
777 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
778 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
779 LLVMContext &Ctx = Fn->getContext();
780 MDBuilder MDB(Ctx);
781 // Annotate the callback behavior of the runtime function:
782 // - The callback callee is argument number 2 (microtask).
783 // - The first two arguments of the callback callee are unknown (-1).
784 // - All variadic arguments to the runtime function are passed to the
785 // callback callee.
786 Fn->addMetadata(
787 LLVMContext::MD_callback,
789 2, {-1, -1}, /* VarArgsArePassed */ true)}));
790 }
791 }
792
793 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
794 << " with type " << *Fn->getFunctionType() << "\n");
795 addAttributes(FnID, *Fn);
796
797 } else {
798 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
799 << " with type " << *Fn->getFunctionType() << "\n");
800 }
801
802 assert(Fn && "Failed to create OpenMP runtime function");
803
804 return {FnTy, Fn};
805}
806
809 if (!FiniBB) {
810 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
812 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
813 Builder.SetInsertPoint(FiniBB);
814 // FiniCB adds the branch to the exit stub.
815 if (Error Err = FiniCB(Builder.saveIP()))
816 return Err;
817 }
818 return FiniBB;
819}
820
822 BasicBlock *OtherFiniBB) {
823 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
824 if (!FiniBB) {
825 FiniBB = OtherFiniBB;
826
827 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
828 if (Error Err = FiniCB(Builder.saveIP()))
829 return Err;
830
831 return Error::success();
832 }
833
834 // Move instructions from FiniBB to the start of OtherFiniBB.
835 auto EndIt = FiniBB->end();
836 if (FiniBB->size() >= 1)
837 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
838 EndIt = Prev;
839 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
840 EndIt);
841
842 FiniBB->replaceAllUsesWith(OtherFiniBB);
843 FiniBB->eraseFromParent();
844 FiniBB = OtherFiniBB;
845 return Error::success();
846}
847
850 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
851 assert(Fn && "Failed to create OpenMP runtime function pointer");
852 return Fn;
853}
854
857 StringRef Name) {
858 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
859 Call->setCallingConv(Config.getRuntimeCC());
860 return Call;
861}
862
863void OpenMPIRBuilder::initialize() { initializeTypes(M); }
864
867 BasicBlock &EntryBlock = Function->getEntryBlock();
868 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
869
870 // Loop over blocks looking for constant allocas, skipping the entry block
871 // as any allocas there are already in the desired location.
872 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
873 Block++) {
874 for (auto Inst = Block->getReverseIterator()->begin();
875 Inst != Block->getReverseIterator()->end();) {
877 Inst++;
879 continue;
880 AllocaInst->moveBeforePreserving(MoveLocInst);
881 } else {
882 Inst++;
883 }
884 }
885 }
886}
887
890
891 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
892 // TODO: For now, we support simple static allocations, we might need to
893 // move non-static ones as well. However, this will need further analysis to
894 // move the lenght arguments as well.
896 };
897
898 for (llvm::Instruction &Inst : Block)
900 if (ShouldHoistAlloca(*AllocaInst))
901 AllocasToMove.push_back(AllocaInst);
902
903 auto InsertPoint =
904 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
905
906 for (llvm::Instruction *AllocaInst : AllocasToMove)
908}
909
911 PostDominatorTree PostDomTree(*Func);
912 for (llvm::BasicBlock &BB : *Func)
913 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
915}
916
918 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
920 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
921 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
922 // Skip functions that have not finalized yet; may happen with nested
923 // function generation.
924 if (Fn && OI->getFunction() != Fn) {
925 DeferredOutlines.push_back(std::move(OI));
926 continue;
927 }
928
929 ParallelRegionBlockSet.clear();
930 Blocks.clear();
931 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
932
933 Function *OuterFn = OI->getFunction();
934 CodeExtractorAnalysisCache CEAC(*OuterFn);
935 // If we generate code for the target device, we need to allocate
936 // struct for aggregate params in the device default alloca address space.
937 // OpenMP runtime requires that the params of the extracted functions are
938 // passed as zero address space pointers. This flag ensures that
939 // CodeExtractor generates correct code for extracted functions
940 // which are used by OpenMP runtime.
941 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
942 std::unique_ptr<CodeExtractor> Extractor =
943 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
944
945 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
946 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
947 << " Exit: " << OI->ExitBB->getName() << "\n");
948 assert(Extractor->isEligible() &&
949 "Expected OpenMP outlining to be possible!");
950
951 for (auto *V : OI->ExcludeArgsFromAggregate)
952 Extractor->excludeArgFromAggregate(V);
953
954 Function *OutlinedFn =
955 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
956
957 // Forward target-cpu, target-features attributes to the outlined function.
958 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
959 if (TargetCpuAttr.isStringAttribute())
960 OutlinedFn->addFnAttr(TargetCpuAttr);
961
962 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
963 if (TargetFeaturesAttr.isStringAttribute())
964 OutlinedFn->addFnAttr(TargetFeaturesAttr);
965
966 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
967 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
968 assert(OutlinedFn->getReturnType()->isVoidTy() &&
969 "OpenMP outlined functions should not return a value!");
970
971 // For compability with the clang CG we move the outlined function after the
972 // one with the parallel region.
973 OutlinedFn->removeFromParent();
974 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
975
976 // Remove the artificial entry introduced by the extractor right away, we
977 // made our own entry block after all.
978 {
979 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
980 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
981 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
982 // Move instructions from the to-be-deleted ArtificialEntry to the entry
983 // basic block of the parallel region. CodeExtractor generates
984 // instructions to unwrap the aggregate argument and may sink
985 // allocas/bitcasts for values that are solely used in the outlined region
986 // and do not escape.
987 assert(!ArtificialEntry.empty() &&
988 "Expected instructions to add in the outlined region entry");
989 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
990 End = ArtificialEntry.rend();
991 It != End;) {
992 Instruction &I = *It;
993 It++;
994
995 if (I.isTerminator()) {
996 // Absorb any debug value that terminator may have
997 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
998 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
999 continue;
1000 }
1001
1002 I.moveBeforePreserving(*OI->EntryBB,
1003 OI->EntryBB->getFirstInsertionPt());
1004 }
1005
1006 OI->EntryBB->moveBefore(&ArtificialEntry);
1007 ArtificialEntry.eraseFromParent();
1008 }
1009 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1010 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1011
1012 // Run a user callback, e.g. to add attributes.
1013 if (OI->PostOutlineCB)
1014 OI->PostOutlineCB(*OutlinedFn);
1015
1016 if (OI->FixUpNonEntryAllocas)
1018 }
1019
1020 // Remove work items that have been completed.
1021 OutlineInfos = std::move(DeferredOutlines);
1022
1023 // The createTarget functions embeds user written code into
1024 // the target region which may inject allocas which need to
1025 // be moved to the entry block of our target or risk malformed
1026 // optimisations by later passes (however, we do not block on
1027 // that here, it's up to the inserter to the list to do so).
1028 // This notbaly has to occur after the OutlinedInfo candidates
1029 // have been extracted so we have an end product that will not
1030 // be implicitly adversely affected by any raises unless
1031 // intentionally appended to the list.
1032 // NOTE: This only does so for ConstantData, it could be extended
1033 // to ConstantExpr's with further effort, however, they should
1034 // largely be folded when they get here. Extending it to runtime
1035 // defined/read+writeable allocation sizes would be non-trivial
1036 // (need to factor in movement of any stores to variables the
1037 // allocation size depends on, as well as the usual loads,
1038 // otherwise it'll yield the wrong result after movement) and
1039 // likely be more suitable as an LLVM optimisation pass.
1042
1043 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1044 [](EmitMetadataErrorKind Kind,
1045 const TargetRegionEntryInfo &EntryInfo) -> void {
1046 errs() << "Error of kind: " << Kind
1047 << " when emitting offload entries and metadata during "
1048 "OMPIRBuilder finalization \n";
1049 };
1050
1051 if (!OffloadInfoManager.empty())
1053
1054 // Rewrite uses of globals to their replacement declare target globals if
1055 // we are processing a device module.
1056 if (Config.isTargetDevice())
1057 applyDeclareTargetGlobalReplacements();
1058
1059 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1060 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1061 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1062 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1063 }
1064
1065 IsFinalized = true;
1066}
1067
1068bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1069
1071 GlobalValue *Original, GlobalValue *Replacement) {
1072 assert(Original && Replacement &&
1073 "Null values provided to registerDeclareTargetGlobalReplacement");
1074 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1075}
1076
1077void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1078 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1079 GlobalValue *OldGV = R.Original;
1080 GlobalValue *NewGV = R.Replacement;
1081
1082 assert(OldGV && NewGV &&
1083 "A null value was inserted into DeclareTargetGlobalReplacements");
1084
1085 // The assert above should catch this case, but this is kept to attempt
1086 // to proceed without issue when asserts are off.
1087 if (!OldGV || !NewGV)
1088 continue;
1089
1090 // The replacement global is a reference pointer that holds the
1091 // address of the device-resident storage. Every use must load the
1092 // reference pointer first and use the loaded address.
1093 //
1094 // Constant expression users (e.g. a constant GEP embedded in another
1095 // global's initializer or in an instruction) cannot have a load inserted
1096 // in place, so first expand any constant-expression users that live inside
1097 // functions into instructions. Any remaining constant users are handled
1098 // via a direct constant rewrite below as we cannot materialize a load
1099 // there.
1100 //
1101 // NOTE: We extend the constant rewrite to module scope, as we replace all
1102 // usages.
1103 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1105 /*RestrictToFunc=*/nullptr,
1106 /*RemoveDeadConstants=*/false);
1107
1108 IRBuilderBase::InsertPointGuard Guard(Builder);
1110 for (User *U : Users) {
1111 auto *Insn = dyn_cast<Instruction>(U);
1112 if (!Insn)
1113 continue;
1114
1115 // A PHI node cannot have a load inserted immediately before it, as PHIs
1116 // must remain grouped at the top of their basic block. So we need to
1117 // make sure any loads we emit are generated in the preceding edge, a
1118 // PHI may reference the global on more than one edge, so every matching
1119 // slot must be handled.
1120 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1121 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1122 if (PHI->getIncomingValue(I) != OldGV)
1123 continue;
1124
1125 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1126 Builder.SetInsertPoint(IncomingBB->getTerminator());
1127 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1128 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1129 PHI->setIncomingValue(I, EdgeLoad);
1130 }
1131 continue;
1132 }
1133
1134 Builder.SetInsertPoint(Insn);
1135 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1136 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1137
1138 // The replacement declare target global lives in the default address
1139 // space, whereas the original global may reside in a non-default
1140 // address space. In that case the initial lowering may have
1141 // emitted an addrspacecast that is no longer valid. Replace the
1142 // whole addrspacecast with the load and erase it rather than
1143 // feeding the load back into the (now pointless) cast.
1144 // NOTE: If we end up with replacement declare target globals in
1145 // non-zero AS's the below will need some minor extensions to have the
1146 // option to alter the address space cast to the new address space where
1147 // required rather than just replacing it.
1148 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1149 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1150 assert(NewGVAS == 0 &&
1151 "Non-default address space declare target global");
1152 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1153 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1154 if (DestAS == 0 && NewGVAS != OldGVAS) {
1155 ASC->replaceAllUsesWith(Load);
1156 ASC->eraseFromParent();
1157 continue;
1158 }
1159 }
1160
1161 Insn->replaceUsesOfWith(OldGV, Load);
1162 }
1163 }
1164
1166}
1167
1169 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1170}
1171
1173 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1174 auto *GV =
1175 new GlobalVariable(M, I32Ty,
1176 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1177 ConstantInt::get(I32Ty, Value), Name);
1178 GV->setVisibility(GlobalValue::HiddenVisibility);
1179
1180 return GV;
1181}
1182
1184 if (List.empty())
1185 return;
1186
1187 // Convert List to what ConstantArray needs.
1189 UsedArray.resize(List.size());
1190 for (unsigned I = 0, E = List.size(); I != E; ++I)
1192 cast<Constant>(&*List[I]), Builder.getPtrTy());
1193
1194 if (UsedArray.empty())
1195 return;
1196 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1197
1198 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1199 ConstantArray::get(ATy, UsedArray), Name);
1200
1201 GV->setSection("llvm.metadata");
1202}
1203
1206 OMPTgtExecModeFlags Mode) {
1207 auto *Int8Ty = Builder.getInt8Ty();
1208 auto *GVMode = new GlobalVariable(
1209 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1210 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1211 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1212 return GVMode;
1213}
1214
1216 uint32_t SrcLocStrSize,
1217 IdentFlag LocFlags,
1218 unsigned Reserve2Flags) {
1219 // Enable "C-mode".
1220 LocFlags |= OMP_IDENT_FLAG_KMPC;
1221
1222 Constant *&Ident =
1223 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1224 if (!Ident) {
1225 Constant *I32Null = ConstantInt::getNullValue(Int32);
1226 Constant *IdentData[] = {I32Null,
1227 ConstantInt::get(Int32, uint32_t(LocFlags)),
1228 ConstantInt::get(Int32, Reserve2Flags),
1229 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1230
1231 size_t SrcLocStrArgIdx = 4;
1232 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1234 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1235 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1236 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1237 Constant *Initializer =
1238 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1239
1240 // Look for existing encoding of the location + flags, not needed but
1241 // minimizes the difference to the existing solution while we transition.
1242 for (GlobalVariable &GV : M.globals())
1243 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1244 if (GV.getInitializer() == Initializer)
1245 Ident = &GV;
1246
1247 if (!Ident) {
1248 auto *GV = new GlobalVariable(
1249 M, OpenMPIRBuilder::Ident,
1250 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1252 M.getDataLayout().getDefaultGlobalsAddressSpace());
1253 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1254 GV->setAlignment(Align(8));
1255 Ident = GV;
1256 }
1257 }
1258
1259 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1260}
1261
1263 uint32_t &SrcLocStrSize) {
1264 SrcLocStrSize = LocStr.size();
1265 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1266 if (!SrcLocStr) {
1267 Constant *Initializer =
1268 ConstantDataArray::getString(M.getContext(), LocStr);
1269
1270 // Look for existing encoding of the location, not needed but minimizes the
1271 // difference to the existing solution while we transition.
1272 for (GlobalVariable &GV : M.globals())
1273 if (GV.isConstant() && GV.hasInitializer() &&
1274 GV.getInitializer() == Initializer)
1275 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1276
1277 SrcLocStr = Builder.CreateGlobalString(
1278 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1279 &M);
1280 }
1281 return SrcLocStr;
1282}
1283
1285 StringRef FileName,
1286 unsigned Line, unsigned Column,
1287 uint32_t &SrcLocStrSize) {
1288 SmallString<128> Buffer;
1289 Buffer.push_back(';');
1290 Buffer.append(FileName);
1291 Buffer.push_back(';');
1292 Buffer.append(FunctionName);
1293 Buffer.push_back(';');
1294 Buffer.append(std::to_string(Line));
1295 Buffer.push_back(';');
1296 Buffer.append(std::to_string(Column));
1297 Buffer.push_back(';');
1298 Buffer.push_back(';');
1299 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1300}
1301
1302Constant *
1304 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1305 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1306}
1307
1309 uint32_t &SrcLocStrSize,
1310 const Function *F) {
1311 DILocation *DIL = DL.get();
1312 if (!DIL)
1313 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1314 StringRef FileName =
1315 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1316 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1317 if (Function.empty() && F)
1318 Function = F->getName();
1319 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1320 DIL->getColumn(), SrcLocStrSize);
1321}
1322
1324 uint32_t &SrcLocStrSize) {
1325 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1326 Loc.IP.getNodeParent()->getParent());
1327}
1328
1331 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1332 "omp_global_thread_num");
1333}
1334
1335OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1336 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1337 ArrayRef<Type *> ResultPtrTys,
1338 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1339 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1340 "expected one result pointer type per in_reduction item");
1341 if (!updateToLocation(Loc))
1342 return Loc.IP;
1343 if (OrigPtrs.empty())
1344 return Builder.saveIP();
1345
1346 // Compute the executing thread's gtid once for the whole target body and
1347 // reuse it for every in_reduction lookup, so a target with several
1348 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1349 // item.
1350 uint32_t SrcLocStrSize;
1351 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1352 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1353 Value *Gtid = getOrCreateThreadID(Ident);
1354
1355 // The runtime entry point takes (and returns) a generic, default-address-
1356 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1357 // taskgroups to find the matching task_reduction registration for the item.
1358 Type *PtrTy = PointerType::getUnqual(M.getContext());
1359 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1360 FunctionCallee GetThData =
1361 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1362
1363 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1364 // Normalize a non-default-address-space original pointer to the generic
1365 // address space before the call.
1366 Value *OrigPtr = OrigPtrs[Idx];
1367 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1368 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1369 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1370
1371 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1372 "omp.inred.priv");
1373
1374 // Cast the returned private pointer back to the requested address space
1375 // when it differs.
1376 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1377 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1378 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1379
1380 MapPrivateCB(Idx, Priv);
1381 }
1382 return Builder.saveIP();
1383}
1384
1387 bool ForceSimpleCall, bool CheckCancelFlag) {
1388 if (!updateToLocation(Loc))
1389 return Loc.IP;
1390
1391 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1392 // __kmpc_barrier(loc, thread_id);
1393
1394 IdentFlag BarrierLocFlags;
1395 switch (Kind) {
1396 case OMPD_for:
1397 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1398 break;
1399 case OMPD_sections:
1400 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1401 break;
1402 case OMPD_single:
1403 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1404 break;
1405 case OMPD_barrier:
1406 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1407 break;
1408 default:
1409 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1410 break;
1411 }
1412
1413 uint32_t SrcLocStrSize;
1414 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1415 Value *Args[] = {
1416 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1417 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1418
1419 // If we are in a cancellable parallel region, barriers are cancellation
1420 // points.
1421 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1422 bool UseCancelBarrier =
1423 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1424
1426 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1427 ? OMPRTL___kmpc_cancel_barrier
1428 : OMPRTL___kmpc_barrier),
1429 Args);
1430
1431 if (UseCancelBarrier && CheckCancelFlag)
1432 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1433 return Err;
1434
1435 return Builder.saveIP();
1436}
1437
1440 Value *IfCondition,
1441 omp::Directive CanceledDirective) {
1442 if (!updateToLocation(Loc))
1443 return Loc.IP;
1444
1445 // LLVM utilities like blocks with terminators.
1446 auto *UI = Builder.CreateUnreachable();
1447
1448 Instruction *ThenTI = UI, *ElseTI = nullptr;
1449 if (IfCondition) {
1450 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1451
1452 // Even if the if condition evaluates to false, this should count as a
1453 // cancellation point
1454 Builder.SetInsertPoint(ElseTI);
1455 auto ElseIP = Builder.saveIP();
1456
1458 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1459 if (!IPOrErr)
1460 return IPOrErr;
1461 }
1462
1463 Builder.SetInsertPoint(ThenTI);
1464
1465 Value *CancelKind = nullptr;
1466 switch (CanceledDirective) {
1467#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1468 case DirectiveEnum: \
1469 CancelKind = Builder.getInt32(Value); \
1470 break;
1471#include "llvm/Frontend/OpenMP/OMPKinds.def"
1472 default:
1473 llvm_unreachable("Unknown cancel kind!");
1474 }
1475
1476 uint32_t SrcLocStrSize;
1477 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1478 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1479 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1481 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1482
1483 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1484 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1485 return Err;
1486
1487 // Update the insertion point and remove the terminator we introduced.
1488 Builder.SetInsertPoint(UI->getParent());
1489 UI->eraseFromParent();
1490
1491 return Builder.saveIP();
1492}
1493
1496 omp::Directive CanceledDirective) {
1497 if (!updateToLocation(Loc))
1498 return Loc.IP;
1499
1500 // LLVM utilities like blocks with terminators.
1501 auto *UI = Builder.CreateUnreachable();
1502 Builder.SetInsertPoint(UI);
1503
1504 Value *CancelKind = nullptr;
1505 switch (CanceledDirective) {
1506#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1507 case DirectiveEnum: \
1508 CancelKind = Builder.getInt32(Value); \
1509 break;
1510#include "llvm/Frontend/OpenMP/OMPKinds.def"
1511 default:
1512 llvm_unreachable("Unknown cancel kind!");
1513 }
1514
1515 uint32_t SrcLocStrSize;
1516 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1517 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1518 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1520 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1521
1522 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1523 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1524 return Err;
1525
1526 // Update the insertion point and remove the terminator we introduced.
1527 Builder.SetInsertPoint(UI->getParent());
1528 UI->eraseFromParent();
1529
1530 return Builder.saveIP();
1531}
1532
1534 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1535 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1536 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1537 if (!updateToLocation(Loc))
1538 return Loc.IP;
1539
1540 Builder.restoreIP(AllocaIP);
1541 auto *KernelArgsPtr =
1542 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1544
1545 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1546 llvm::Value *Arg =
1547 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1548 Builder.CreateAlignedStore(
1549 KernelArgs[I], Arg,
1550 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1551 }
1552
1553 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1554 NumThreads, HostPtr, KernelArgsPtr};
1555
1557 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1558 OffloadingArgs);
1559
1560 return Builder.saveIP();
1561}
1562
1564 const LocationDescription &Loc, Value *OutlinedFnID,
1565 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1566 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1567
1568 if (!updateToLocation(Loc))
1569 return Loc.IP;
1570
1571 // On top of the arrays that were filled up, the target offloading call
1572 // takes as arguments the device id as well as the host pointer. The host
1573 // pointer is used by the runtime library to identify the current target
1574 // region, so it only has to be unique and not necessarily point to
1575 // anything. It could be the pointer to the outlined function that
1576 // implements the target region, but we aren't using that so that the
1577 // compiler doesn't need to keep that, and could therefore inline the host
1578 // function if proven worthwhile during optimization.
1579
1580 // From this point on, we need to have an ID of the target region defined.
1581 assert(OutlinedFnID && "Invalid outlined function ID!");
1582 (void)OutlinedFnID;
1583
1584 // Return value of the runtime offloading call.
1585 Value *Return = nullptr;
1586
1587 // Arguments for the target kernel.
1588 SmallVector<Value *> ArgsVector;
1589 getKernelArgsVector(Args, Builder, ArgsVector);
1590
1591 // The target region is an outlined function launched by the runtime
1592 // via calls to __tgt_target_kernel().
1593 //
1594 // Note that on the host and CPU targets, the runtime implementation of
1595 // these calls simply call the outlined function without forking threads.
1596 // The outlined functions themselves have runtime calls to
1597 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1598 // the compiler in emitTeamsCall() and emitParallelCall().
1599 //
1600 // In contrast, on the NVPTX target, the implementation of
1601 // __tgt_target_teams() launches a GPU kernel with the requested number
1602 // of teams and threads so no additional calls to the runtime are required.
1603 // Check the error code and execute the host version if required.
1604 Builder.restoreIP(emitTargetKernel(
1605 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1606 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1607
1608 BasicBlock *OffloadFailedBlock =
1609 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1610 BasicBlock *OffloadContBlock =
1611 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1612 Value *Failed = Builder.CreateIsNotNull(Return);
1613 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1614
1615 auto CurFn = Builder.GetInsertBlock()->getParent();
1616 emitBlock(OffloadFailedBlock, CurFn);
1617 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1618 if (!AfterIP)
1619 return AfterIP.takeError();
1620 Builder.restoreIP(*AfterIP);
1621 emitBranch(OffloadContBlock);
1622 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1623 return Builder.saveIP();
1624}
1625
1627 Value *CancelFlag, omp::Directive CanceledDirective) {
1628 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1629 "Unexpected cancellation!");
1630
1631 // For a cancel barrier we create two new blocks.
1632 BasicBlock *BB = Builder.GetInsertBlock();
1633 BasicBlock *NonCancellationBlock;
1634 if (Builder.GetInsertPoint() == BB->end()) {
1635 // TODO: This branch will not be needed once we moved to the
1636 // OpenMPIRBuilder codegen completely.
1637 NonCancellationBlock = BasicBlock::Create(
1638 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1639 } else {
1640 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1642 Builder.SetInsertPoint(BB);
1643 }
1644 BasicBlock *CancellationBlock = BasicBlock::Create(
1645 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1646
1647 // Jump to them based on the return value.
1648 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1649 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1650 /* TODO weight */ nullptr, nullptr);
1651
1652 // From the cancellation block we finalize all variables and go to the
1653 // post finalization block that is known to the FiniCB callback.
1654 auto &FI = FinalizationStack.back();
1655 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1656 if (!FiniBBOrErr)
1657 return FiniBBOrErr.takeError();
1658 Builder.SetInsertPoint(CancellationBlock);
1659 Builder.CreateBr(*FiniBBOrErr);
1660
1661 // The continuation block is where code generation continues.
1662 Builder.SetInsertPoint(NonCancellationBlock->begin());
1663 return Error::success();
1664}
1665
1666/// Create wrapper function used to gather the outlined function's argument
1667/// structure from a shared buffer and to forward them to it when running in
1668/// Generic mode.
1669///
1670/// The outlined function is expected to receive 2 integer arguments followed by
1671/// an optional pointer argument to an argument structure holding the rest.
1673 Function &OutlinedFn) {
1674 size_t NumArgs = OutlinedFn.arg_size();
1675 assert((NumArgs == 2 || NumArgs == 3) &&
1676 "expected a 2-3 argument parallel outlined function");
1677 bool UseArgStruct = NumArgs == 3;
1678
1679 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1680 IRBuilder<>::InsertPointGuard IPG(Builder);
1681 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1682 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1683 /*isVarArg=*/false);
1684 auto *WrapperFn =
1686 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1687
1688 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1689 WrapperFn->addParamAttr(0, Attribute::ZExt);
1690 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1691
1692 BasicBlock *EntryBB =
1693 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1694 Builder.SetInsertPoint(EntryBB);
1695
1696 // Allocation.
1697 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1698 /*ArraySize=*/nullptr, "addr");
1699 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1700 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1701 AddrAlloca->getName() + ".ascast");
1702
1703 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1704 /*ArraySize=*/nullptr, "zero");
1705 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1706 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1707 ZeroAlloca->getName() + ".ascast");
1708
1709 Value *ArgsAlloca = nullptr;
1710 if (UseArgStruct) {
1711 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1712 /*ArraySize=*/nullptr, "global_args");
1713 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1714 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1715 ArgsAlloca->getName() + ".ascast");
1716 }
1717
1718 // Initialization.
1719 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1720 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1721 if (UseArgStruct) {
1722 Builder.CreateCall(
1723 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1724 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1725 {ArgsAlloca});
1726 }
1727
1728 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1729
1730 // Load structArg from global_args.
1731 if (UseArgStruct) {
1732 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1733 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1734 {Builder.getInt64(0)});
1735 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1736 Args.push_back(StructArg);
1737 }
1738
1739 // Call the outlined function holding the parallel body.
1740 Builder.CreateCall(&OutlinedFn, Args);
1741 Builder.CreateRetVoid();
1742
1743 return WrapperFn;
1744}
1745
1746// Callback used to create OpenMP runtime calls to support
1747// omp parallel clause for the device.
1748// We need to use this callback to replace call to the OutlinedFn in OuterFn
1749// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1751 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1752 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1753 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1754 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1755 assert(OutlinedFn.arg_size() >= 2 &&
1756 "Expected at least tid and bounded tid as arguments");
1757 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1758
1759 // Add some known attributes.
1760 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1761 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1762 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1763 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1764 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1765 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1766
1767 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1768 assert(CI && "Expected call instruction to outlined function");
1769 CI->getParent()->setName("omp_parallel");
1770
1771 Builder.SetInsertPoint(CI);
1772 Type *PtrTy = OMPIRBuilder->VoidPtr;
1773
1774 // Add alloca for kernel args
1775 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1776 Builder.SetInsertPoint(OuterAllocaBB->getFirstInsertionPt());
1777 AllocaInst *ArgsAlloca =
1778 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1779 Value *Args = ArgsAlloca;
1780 // Add address space cast if array for storing arguments is not allocated
1781 // in address space 0
1782 if (ArgsAlloca->getAddressSpace())
1783 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1784 Builder.restoreIP(CurrentIP);
1785
1786 // Store captured vars which are used by kmpc_parallel_60
1787 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1788 Value *V = *(CI->arg_begin() + 2 + Idx);
1789 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1790 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1791 Builder.CreateStore(V, StoreAddress);
1792 }
1793
1794 Value *Cond =
1795 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1796 : Builder.getInt32(1);
1797 Value *NumThreadsArg =
1798 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1799 : Builder.getInt32(-1);
1800
1801 // If this is not a Generic kernel, we can skip generating the wrapper.
1802 Value *WrapperFn;
1803 if (isGenericKernel(*OuterFn))
1804 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1805 else
1806 WrapperFn = Constant::getNullValue(PtrTy);
1807
1808 // Build kmpc_parallel_60 call
1809 Value *Parallel60CallArgs[] = {
1810 /* identifier*/ Ident,
1811 /* global thread num*/ ThreadID,
1812 /* if expression */ Cond,
1813 /* number of threads */ NumThreadsArg,
1814 /* Proc bind */ Builder.getInt32(-1),
1815 /* outlined function */ &OutlinedFn,
1816 /* wrapper function */ WrapperFn,
1817 /* arguments of the outlined funciton*/ Args,
1818 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1819 /* strict for number of threads */ Builder.getInt32(0)};
1820
1821 FunctionCallee RTLFn =
1822 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1823
1824 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1825
1826 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1827 << *Builder.GetInsertBlock()->getParent() << "\n");
1828
1829 // Initialize the local TID stack location with the argument value.
1830 Builder.SetInsertPoint(PrivTID);
1831 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1832 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1833 PrivTIDAddr);
1834
1835 // Remove redundant call to the outlined function.
1836 CI->eraseFromParent();
1837
1838 for (Instruction *I : ToBeDeleted) {
1839 I->eraseFromParent();
1840 }
1841}
1842
1843// Callback used to create OpenMP runtime calls to support
1844// omp parallel clause for the host.
1845// We need to use this callback to replace call to the OutlinedFn in OuterFn
1846// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1847static void
1849 Function *OuterFn, Value *Ident, Value *IfCondition,
1850 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1851 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1852 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1853 FunctionCallee RTLFn;
1854 if (IfCondition) {
1855 RTLFn =
1856 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1857 } else {
1858 RTLFn =
1859 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1860 }
1861 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1862 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1863 LLVMContext &Ctx = F->getContext();
1864 MDBuilder MDB(Ctx);
1865 // Annotate the callback behavior of the __kmpc_fork_call:
1866 // - The callback callee is argument number 2 (microtask).
1867 // - The first two arguments of the callback callee are unknown (-1).
1868 // - All variadic arguments to the __kmpc_fork_call are passed to the
1869 // callback callee.
1870 F->addMetadata(LLVMContext::MD_callback,
1872 2, {-1, -1},
1873 /* VarArgsArePassed */ true)}));
1874 }
1875 }
1876 // Add some known attributes.
1877 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1878 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1879 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1880
1881 assert(OutlinedFn.arg_size() >= 2 &&
1882 "Expected at least tid and bounded tid as arguments");
1883 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1884
1885 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1886
1887 // CodeExtractor creates an alloca aggregate struct containing pointers to all
1888 // the captured values. The address of this struct is then passed as a
1889 // parameter to the outlined function. This parameter of the outlined function
1890 // can be marked noalias because CodeExractor + this host OpenMP lowering
1891 // maintains the following invariants:
1892 // - The caller initializes the fields before starting any callbacks.
1893 // - Neither the caller, OpenMP runtime, nor callbacks modify the structure
1894 // while callbacks are executing.
1895 // - The callbacks only load captured values from the structure; they do not
1896 // use it to write results back to the caller. This is generated code; the
1897 // user has no way to modify the structure directly.
1898 //
1899 // Multiple callbacks may read the same structure. This is allowed by noalias,
1900 // which permits aliasing accesses to memory that is not modified during the
1901 // call.
1902 //
1903 // The synchronous fork keeps the structure alive until all callbacks return,
1904 // which justifies nofreeobj. Neither attribute extends to the objects pointed
1905 // to by the captured fields.
1906 if (NumCapturedVars) {
1907 assert(NumCapturedVars == 1 && "Expected an aggregate capture argument");
1910 ->getAllocatedType()
1911 ->isStructTy() &&
1912 "Expected a stack-allocated capture structure");
1913
1914 OutlinedFn.addParamAttr(2, Attribute::NoAlias);
1915 OutlinedFn.addParamAttr(2, Attribute::NoFreeObj);
1916 }
1917
1918 CI->getParent()->setName("omp_parallel");
1919 Builder.SetInsertPoint(CI);
1920
1921 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1922 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1923 &OutlinedFn};
1924
1925 SmallVector<Value *, 16> RealArgs;
1926 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1927 if (IfCondition) {
1928 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1929 RealArgs.push_back(Cond);
1930 }
1931 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1932
1933 // __kmpc_fork_call_if always expects a void ptr as the last argument
1934 // If there are no arguments, pass a null pointer.
1935 auto PtrTy = OMPIRBuilder->VoidPtr;
1936 if (IfCondition && NumCapturedVars == 0) {
1937 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1938 RealArgs.push_back(NullPtrValue);
1939 }
1940
1941 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1942
1943 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1944 << *Builder.GetInsertBlock()->getParent() << "\n");
1945
1946 // Initialize the local TID stack location with the argument value.
1947 Builder.SetInsertPoint(PrivTID);
1948 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1949 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1950 PrivTIDAddr);
1951
1952 // Remove redundant call to the outlined function.
1953 CI->eraseFromParent();
1954
1955 for (Instruction *I : ToBeDeleted) {
1956 I->eraseFromParent();
1957 }
1958}
1959
1961 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1962 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1963 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1964 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1965 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1966
1967 if (!updateToLocation(Loc))
1968 return Loc.IP;
1969
1970 uint32_t SrcLocStrSize;
1971 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1972 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1973 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1974 (ProcBind != OMP_PROC_BIND_default);
1975 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1976 // If we generate code for the target device, we need to allocate
1977 // struct for aggregate params in the device default alloca address space.
1978 // OpenMP runtime requires that the params of the extracted functions are
1979 // passed as zero address space pointers. This flag ensures that extracted
1980 // function arguments are declared in zero address space
1981 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1982
1983 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1984 // only if we compile for host side.
1985 if (NumThreads && !Config.isTargetDevice()) {
1986 Value *Args[] = {
1987 Ident, ThreadID,
1988 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1990 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1991 }
1992
1993 if (ProcBind != OMP_PROC_BIND_default) {
1994 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1995 Value *Args[] = {
1996 Ident, ThreadID,
1997 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1999 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
2000 }
2001
2002 BasicBlock *InsertBB = Builder.GetInsertBlock();
2003 Function *OuterFn = InsertBB->getParent();
2004
2005 // Save the outer alloca block because the insertion iterator may get
2006 // invalidated and we still need this later.
2007 BasicBlock *OuterAllocaBlock = OuterAllocIP.getNodeParent();
2008
2009 // Vector to remember instructions we used only during the modeling but which
2010 // we want to delete at the end.
2012
2013 // Change the location to the outer alloca insertion point to create and
2014 // initialize the allocas we pass into the parallel region.
2015 InsertPointTy NewOuter(OuterAllocaBlock->begin());
2016 Builder.restoreIP(NewOuter);
2017 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
2018 AllocaInst *ZeroAddrAlloca =
2019 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
2020 Instruction *TIDAddr = TIDAddrAlloca;
2021 Instruction *ZeroAddr = ZeroAddrAlloca;
2022 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
2023 // Add additional casts to enforce pointers in zero address space
2024 TIDAddr = new AddrSpaceCastInst(
2025 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2026 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2027 ToBeDeleted.push_back(TIDAddr);
2028 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2029 PointerType ::get(M.getContext(), 0),
2030 "zero.addr.ascast");
2031 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2032 ToBeDeleted.push_back(ZeroAddr);
2033 }
2034
2035 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2036 // associated arguments in the outlined function, so we delete them later.
2037 ToBeDeleted.push_back(TIDAddrAlloca);
2038 ToBeDeleted.push_back(ZeroAddrAlloca);
2039
2040 // Create an artificial insertion point that will also ensure the blocks we
2041 // are about to split are not degenerated.
2042 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2043 UI->setDebugLoc(Loc.DL);
2044
2045 BasicBlock *EntryBB = UI->getParent();
2046 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2047 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2048 BasicBlock *PRegPreFiniBB =
2049 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2050 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2051
2052 auto FiniCBWrapper = [&](InsertPointTy IP) {
2053 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2054 // target to the region exit block.
2055 if (IP == IP.getNodeParent()->end()) {
2057 Builder.restoreIP(IP);
2058 Instruction *I = Builder.CreateBr(PRegExitBB);
2059 IP = I->getIterator();
2060 }
2061 assert(IP.getNodeParent()->getTerminator()->getNumSuccessors() == 1 &&
2062 IP.getNodeParent()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2063 "Unexpected insertion point for finalization call!");
2064 return FiniCB(IP);
2065 };
2066
2067 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2068
2069 // Generate the privatization allocas in the block that will become the entry
2070 // of the outlined function.
2071 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2072 InsertPointTy InnerAllocaIP = Builder.saveIP();
2073
2074 AllocaInst *PrivTIDAddr =
2075 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2076 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2077
2078 // Add some fake uses for OpenMP provided arguments.
2079 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2080 Instruction *ZeroAddrUse =
2081 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2082 ToBeDeleted.push_back(ZeroAddrUse);
2083
2084 // EntryBB
2085 // |
2086 // V
2087 // PRegionEntryBB <- Privatization allocas are placed here.
2088 // |
2089 // V
2090 // PRegionBodyBB <- BodeGen is invoked here.
2091 // |
2092 // V
2093 // PRegPreFiniBB <- The block we will start finalization from.
2094 // |
2095 // V
2096 // PRegionExitBB <- A common exit to simplify block collection.
2097 //
2098
2099 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2100
2101 // Let the caller create the body.
2102 assert(BodyGenCB && "Expected body generation callback!");
2103 InsertPointTy CodeGenIP(PRegBodyBB->begin());
2104 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2105 return Err;
2106
2107 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2108
2109 // If OuterFn is a Generic kernel, we need to use device shared memory to
2110 // allocate argument structures. Otherwise, we use stack allocations as usual.
2111 bool UsesDeviceSharedMemory =
2112 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2113 std::unique_ptr<OutlineInfo> OI =
2114 UsesDeviceSharedMemory
2115 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2116 : std::make_unique<OutlineInfo>();
2117
2118 if (Config.isTargetDevice()) {
2119 // Generate OpenMP target specific runtime call
2120 OI->PostOutlineCB = [=, ToBeDeletedVec =
2121 std::move(ToBeDeleted)](Function &OutlinedFn) {
2122 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2123 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2124 ThreadID, ToBeDeletedVec);
2125 };
2126 } else {
2127 // Generate OpenMP host runtime call
2128 OI->PostOutlineCB = [=, ToBeDeletedVec =
2129 std::move(ToBeDeleted)](Function &OutlinedFn) {
2130 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2131 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2132 };
2133 }
2134
2135 OI->FixUpNonEntryAllocas = true;
2136 OI->OuterAllocBB = OuterAllocaBlock;
2137 OI->EntryBB = PRegEntryBB;
2138 OI->ExitBB = PRegExitBB;
2139 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2140 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2141
2142 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2144 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2145
2146 CodeExtractorAnalysisCache CEAC(*OuterFn);
2147 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2148 /* AggregateArgs */ false,
2149 /* BlockFrequencyInfo */ nullptr,
2150 /* BranchProbabilityInfo */ nullptr,
2151 /* AssumptionCache */ nullptr,
2152 /* AllowVarArgs */ true,
2153 /* AllowAlloca */ true,
2154 /* AllocationBlock */ OuterAllocaBlock,
2155 /* DeallocationBlocks */ {},
2156 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2157
2158 // Find inputs to, outputs from the code region.
2159 BasicBlock *CommonExit = nullptr;
2160 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2161 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2162
2163 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2164 /*CollectGlobalInputs=*/true);
2165
2166 Inputs.remove_if([&](Value *I) {
2168 return GV->getValueType() == OpenMPIRBuilder::Ident;
2169
2170 return false;
2171 });
2172
2173 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2174
2175 FunctionCallee TIDRTLFn =
2176 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2177
2178 auto PrivHelper = [&](Value &V) -> Error {
2179 if (&V == TIDAddr || &V == ZeroAddr) {
2180 OI->ExcludeArgsFromAggregate.push_back(&V);
2181 return Error::success();
2182 }
2183
2185 for (Use &U : V.uses())
2186 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2187 if (ParallelRegionBlockSet.count(UserI->getParent()))
2188 Uses.insert(&U);
2189
2190 // __kmpc_fork_call expects extra arguments as pointers. If the input
2191 // already has a pointer type, everything is fine. Otherwise, store the
2192 // value onto stack and load it back inside the to-be-outlined region. This
2193 // will ensure only the pointer will be passed to the function.
2194 // FIXME: if there are more than 15 trailing arguments, they must be
2195 // additionally packed in a struct.
2196 Value *Inner = &V;
2197 if (!V.getType()->isPointerTy()) {
2199 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2200
2201 Builder.restoreIP(OuterAllocIP);
2202 Value *Ptr;
2203 if (UsesDeviceSharedMemory) {
2204 // Use device shared memory instead, if needed.
2205 Ptr = createOMPAllocShared(Builder, V.getType(),
2206 V.getName() + ".reloaded");
2207 for (BasicBlock *DeallocBlock : OuterDeallocBlocks) {
2208 assert(DeallocBlock->getParent() ==
2209 OuterAllocIP.getNodeParent()->getParent() &&
2210 "Dealloc block must be in the allocation's function to reuse "
2211 "its debug location");
2212 createOMPFreeShared({DeallocBlock->getFirstInsertionPt(),
2213 Builder.getCurrentDebugLocation()},
2214 Ptr, V.getType());
2215 }
2216 } else {
2217 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2218 V.getName() + ".reloaded");
2219 }
2220
2221 // Store to stack at end of the block that currently branches to the entry
2222 // block of the to-be-outlined region.
2223 Builder.SetInsertPoint(InsertBB->getTerminator()->getIterator());
2224 Builder.CreateStore(&V, Ptr);
2225
2226 // Load back next to allocations in the to-be-outlined region.
2227 Builder.restoreIP(InnerAllocaIP);
2228 Inner = Builder.CreateLoad(V.getType(), Ptr);
2229 }
2230
2231 Value *ReplacementValue = nullptr;
2232 CallInst *CI = dyn_cast<CallInst>(&V);
2233 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2234 ReplacementValue = PrivTID;
2235 } else {
2236 InsertPointOrErrorTy AfterIP =
2237 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2238 if (!AfterIP)
2239 return AfterIP.takeError();
2240 Builder.restoreIP(*AfterIP);
2241 InnerAllocaIP =
2242 InnerAllocaIP.getNodeParent()->getTerminator()->getIterator();
2243
2244 assert(ReplacementValue &&
2245 "Expected copy/create callback to set replacement value!");
2246 if (ReplacementValue == &V)
2247 return Error::success();
2248 }
2249
2250 for (Use *UPtr : Uses)
2251 UPtr->set(ReplacementValue);
2252
2253 return Error::success();
2254 };
2255
2256 // Reset the inner alloca insertion as it will be used for loading the values
2257 // wrapped into pointers before passing them into the to-be-outlined region.
2258 // Configure it to insert immediately after the fake use of zero address so
2259 // that they are available in the generated body and so that the
2260 // OpenMP-related values (thread ID and zero address pointers) remain leading
2261 // in the argument list.
2262 InnerAllocaIP = ZeroAddrUse->getNextNode()->getIterator();
2263
2264 // Reset the outer alloca insertion point to the entry of the relevant block
2265 // in case it was invalidated.
2266 OuterAllocIP = OuterAllocaBlock->getFirstInsertionPt();
2267
2268 for (Value *Input : Inputs) {
2269 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2270 if (Error Err = PrivHelper(*Input))
2271 return Err;
2272 }
2273 LLVM_DEBUG({
2274 for (Value *Output : Outputs)
2275 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2276 });
2277 assert(Outputs.empty() &&
2278 "OpenMP outlining should not produce live-out values!");
2279
2280 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2281 LLVM_DEBUG({
2282 for (auto *BB : Blocks)
2283 dbgs() << " PBR: " << BB->getName() << "\n";
2284 });
2285
2286 // Adjust the finalization stack, verify the adjustment, and call the
2287 // finalize function a last time to finalize values between the pre-fini
2288 // block and the exit block if we left the parallel "the normal way".
2289 auto FiniInfo = FinalizationStack.pop_back_val();
2290 (void)FiniInfo;
2291 assert(FiniInfo.DK == OMPD_parallel &&
2292 "Unexpected finalization stack state!");
2293
2294 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2295
2296 InsertPointTy PreFiniIP(PRegPreFiniTI->getIterator());
2297 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2298 if (!FiniBBOrErr)
2299 return FiniBBOrErr.takeError();
2300 {
2302 Builder.restoreIP(PreFiniIP);
2303 Builder.CreateBr(*FiniBBOrErr);
2304 // There's currently a branch to omp.par.exit. Delete it. We will get there
2305 // via the fini block
2306 Builder.GetInsertBlock()->getTerminator()->eraseFromParent();
2307 }
2308
2309 // Register the outlined info.
2310 addOutlineInfo(std::move(OI));
2311
2312 InsertPointTy AfterIP(UI->getParent()->end());
2313 UI->eraseFromParent();
2314
2315 return AfterIP;
2316}
2317
2319 // Build call void __kmpc_flush(ident_t *loc)
2320 uint32_t SrcLocStrSize;
2321 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2322 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2323
2325 Args);
2326}
2327
2329 if (!updateToLocation(Loc))
2330 return;
2331 emitFlush(Loc);
2332}
2333
2335 Value *Message) {
2336 if (!updateToLocation(Loc))
2337 return;
2338
2339 // Build call void __kmpc_error(ident_t *loc, int severity,
2340 // const char *message)
2341 uint32_t SrcLocStrSize;
2342 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2343 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2344 // Severity: 1 = warning, 2 = fatal.
2345 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2346 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2347 Value *Args[] = {Ident, Severity, MessageArg};
2348
2350 Args);
2351}
2352
2354 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2355 uint32_t SrcLocStrSize;
2356 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2357 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2358 Constant *I32Null = ConstantInt::getNullValue(Int32);
2359 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2360
2362 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2363}
2364
2370
2372 const DependData &Dep) {
2373 // Store the pointer to the variable
2374 Value *Addr = Builder.CreateStructGEP(
2375 DependInfo, Entry,
2376 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2377 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2378 Builder.CreateStore(DepValPtr, Addr);
2379 // Store the size of the variable
2380 Value *Size = Builder.CreateStructGEP(
2381 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2382 Builder.CreateStore(
2383 ConstantInt::get(SizeTy,
2384 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2385 Size);
2386 // Store the dependency kind
2387 Value *Flags = Builder.CreateStructGEP(
2388 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2389 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2390 static_cast<unsigned int>(Dep.DepKind)),
2391 Flags);
2392}
2393
2394// Processes the dependencies in Dependencies and does the following
2395// - Allocates space on the stack of an array of DependInfo objects
2396// - Populates each DependInfo object with relevant information of
2397// the corresponding dependence.
2398// - All code is inserted in the entry block of the current function.
2400 OpenMPIRBuilder &OMPBuilder,
2402 // Early return if we have no dependencies to process
2403 if (Dependencies.empty())
2404 return nullptr;
2405
2406 // Given a vector of DependData objects, in this function we create an
2407 // array on the stack that holds kmp_depend_info objects corresponding
2408 // to each dependency. This is then passed to the OpenMP runtime.
2409 // For example, if there are 'n' dependencies then the following psedo
2410 // code is generated. Assume the first dependence is on a variable 'a'
2411 //
2412 // \code{c}
2413 // DepArray = alloc(n x sizeof(kmp_depend_info);
2414 // idx = 0;
2415 // DepArray[idx].base_addr = ptrtoint(&a);
2416 // DepArray[idx].len = 8;
2417 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2418 // ++idx;
2419 // DepArray[idx].base_addr = ...;
2420 // \endcode
2421
2422 IRBuilderBase &Builder = OMPBuilder.Builder;
2423 Type *DependInfo = OMPBuilder.DependInfo;
2424
2425 Value *DepArray = nullptr;
2426 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2427 {
2428 // Use a InsertPointGuard to restore the location back along with the
2429 // insertion point.
2430 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2431 Builder.SetInsertPoint(
2432 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2433 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2434 }
2435
2436 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2437 Value *Base =
2438 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2439 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2440 }
2441 return DepArray;
2442}
2443
2445 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2446 // global_tid);
2447 uint32_t SrcLocStrSize;
2448 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2449 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2450 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2451
2452 // Ignore return result until untied tasks are supported.
2454 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2455}
2456
2458 DependenciesInfo Dependencies,
2459 bool IsNowait) {
2460 if (!updateToLocation(Loc))
2461 return;
2462
2463 Value *DepArray = nullptr;
2464 Type *DepArrayTy = nullptr;
2465 Value *NumDeps = nullptr;
2466 if (Dependencies.DepArray) {
2467 DepArray = Dependencies.DepArray;
2468 NumDeps = Dependencies.NumDeps;
2469 } else if (!Dependencies.Deps.empty()) {
2470 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2471 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2472 {
2474 BasicBlock &entryBB =
2475 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2476 Builder.SetInsertPoint(entryBB.getFirstInsertionPt());
2477 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2478 }
2479
2480 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2481 Value *Base =
2482 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2483 this->emitTaskDependency(Builder, Base, Dep);
2484 }
2485 }
2486
2487 if (DepArray) {
2488 uint32_t SrcLocStrSize;
2489 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2490 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2491 Value *Args[] = {
2492 Ident,
2493 getOrCreateThreadID(Ident),
2494 NumDeps,
2495 DepArray,
2496 ConstantInt::get(Builder.getInt32Ty(), 0),
2498 ConstantInt::get(Builder.getInt32Ty(), IsNowait)};
2501 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2502 Args);
2503 } else {
2505 }
2506}
2507
2508/// Create the task duplication function passed to kmpc_taskloop.
2509Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2510 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2511 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2512 if (!DupCB)
2514 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2515
2516 // From OpenMP Runtime p_task_dup_t:
2517 // Routine optionally generated by the compiler for setting the lastprivate
2518 // flag and calling needed constructors for private/firstprivate objects (used
2519 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2520 // lastprivate flag.
2521 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2522
2523 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2524
2525 FunctionType *DupFuncTy = FunctionType::get(
2526 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2527 /*isVarArg=*/false);
2528
2529 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2530 "omp_taskloop_dup", M);
2531 Value *DestTaskArg = DupFunction->getArg(0);
2532 Value *SrcTaskArg = DupFunction->getArg(1);
2533 Value *LastprivateFlagArg = DupFunction->getArg(2);
2534 DestTaskArg->setName("dest_task");
2535 SrcTaskArg->setName("src_task");
2536 LastprivateFlagArg->setName("lastprivate_flag");
2537
2538 IRBuilderBase::InsertPointGuard Guard(Builder);
2539 Builder.SetInsertPoint(
2540 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2541
2542 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2543 Type *TaskWithPrivatesTy =
2544 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2545 Value *TaskPrivates = Builder.CreateGEP(
2546 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2547 Value *ContextPtr = Builder.CreateGEP(
2548 PrivatesTy, TaskPrivates,
2549 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2550 return ContextPtr;
2551 };
2552
2553 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2554 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2555
2556 DestTaskContextPtr->setName("destPtr");
2557 SrcTaskContextPtr->setName("srcPtr");
2558
2559 InsertPointTy AllocaIP(DupFunction->getEntryBlock().begin());
2560 InsertPointTy CodeGenIP = Builder.saveIP();
2561 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2562 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2563 if (!AfterIPOrError)
2564 return AfterIPOrError.takeError();
2565 Builder.restoreIP(*AfterIPOrError);
2566
2567 Builder.CreateRetVoid();
2568
2569 return DupFunction;
2570}
2571
2572OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2573 const LocationDescription &Loc, InsertPointTy AllocaIP,
2574 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2575 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2576 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2577 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2578 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2579 Value *TaskContextStructPtrVal, bool FreeAgent) {
2580
2581 if (!updateToLocation(Loc))
2582 return InsertPointTy();
2583
2584 uint32_t SrcLocStrSize;
2585 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2586 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2587
2588 BasicBlock *TaskloopExitBB =
2589 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2590 BasicBlock *TaskloopBodyBB =
2591 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2592 BasicBlock *TaskloopAllocaBB =
2593 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2594
2595 InsertPointTy TaskloopAllocaIP = TaskloopAllocaBB->begin();
2596 InsertPointTy TaskloopBodyIP = TaskloopBodyBB->begin();
2597
2598 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2599 return Err;
2600
2601 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2602 if (!result) {
2603 return result.takeError();
2604 }
2605
2606 llvm::CanonicalLoopInfo *CLI = result.get();
2607 if (CLI->VirtualCollapse)
2608 CLI->reportUnsupportedVirtualCollapse();
2609 auto OI = std::make_unique<OutlineInfo>();
2610 OI->EntryBB = TaskloopAllocaBB;
2611 OI->OuterAllocBB = AllocaIP.getNodeParent();
2612 OI->ExitBB = TaskloopExitBB;
2613 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2614 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2615
2616 // Add the thread ID argument.
2617 SmallVector<Instruction *> ToBeDeleted;
2618 // dummy instruction to be used as a fake argument
2619 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2620 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2621 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2622 TaskloopAllocaIP, "lb", false, true);
2623 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2624 TaskloopAllocaIP, "ub", false, true);
2625 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2626 TaskloopAllocaIP, "step", false, true);
2627 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2628 // aggregate struct
2629 OI->Inputs.insert(FakeLB);
2630 OI->Inputs.insert(FakeUB);
2631 OI->Inputs.insert(FakeStep);
2632 if (TaskContextStructPtrVal)
2633 OI->Inputs.insert(TaskContextStructPtrVal);
2634 assert(((TaskContextStructPtrVal && DupCB) ||
2635 (!TaskContextStructPtrVal && !DupCB)) &&
2636 "Task context struct ptr and duplication callback must be both set "
2637 "or both null");
2638
2639 // It isn't safe to run the duplication bodygen callback inside the post
2640 // outlining callback so this has to be run now before we know the real task
2641 // shareds structure type.
2642 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2643 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2644 Type *FakeSharedsTy = StructType::get(
2645 Builder.getContext(),
2646 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2647 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2648 FakeSharedsTy,
2649 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2650 if (!TaskDupFnOrErr) {
2651 return TaskDupFnOrErr.takeError();
2652 }
2653 Value *TaskDupFn = *TaskDupFnOrErr;
2654
2655 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2656 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2657 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2658 FakeSharedsTy, Final, Mergeable, Priority,
2659 NumOfCollapseLoops,
2660 FreeAgent](Function &OutlinedFn) mutable {
2661 // Replace the Stale CI by appropriate RTL function call.
2662 assert(OutlinedFn.hasOneUse() &&
2663 "there must be a single user for the outlined function");
2664 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2665
2666 /* Create the casting for the Bounds Values that can be used when outlining
2667 * to replace the uses of the fakes with real values */
2668 BasicBlock *CodeReplBB = StaleCI->getParent();
2669 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2670 Value *CastedLBVal =
2671 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2672 Value *CastedUBVal =
2673 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2674 Value *CastedStepVal =
2675 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2676
2677 Builder.SetInsertPoint(StaleCI);
2678
2679 // Gather the arguments for emitting the runtime call for
2680 // @__kmpc_omp_task_alloc
2681 Function *TaskAllocFn =
2682 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2683
2684 Value *ThreadID = getOrCreateThreadID(Ident);
2685
2686 if (!NoGroup) {
2687 // Emit runtime call for @__kmpc_taskgroup
2688 Function *TaskgroupFn =
2689 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2690 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2691 }
2692
2693 // `flags` Argument Configuration
2694 // Task is tied if (Flags & 1) == 1.
2695 // Task is untied if (Flags & 1) == 0.
2696 // Task is final if (Flags & 2) == 2.
2697 // Task is not final if (Flags & 2) == 0.
2698 // Task is mergeable if (Flags & 4) == 4.
2699 // Task is not mergeable if (Flags & 4) == 0.
2700 // Task is priority if (Flags & 32) == 32.
2701 // Task is not priority if (Flags & 32) == 0.
2702 // Task is free-agent eligible if (Flags & 128) == 128.
2703 // Task is not free-agent eligible if (Flags & 128) == 0.
2704 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2705 if (Final)
2706 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2707 if (Mergeable)
2708 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2709 if (Priority)
2710 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2711 if (FreeAgent)
2712 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2713
2714 Value *TaskSize = Builder.getInt64(
2715 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2716
2717 AllocaInst *ArgStructAlloca =
2719 assert(ArgStructAlloca &&
2720 "Unable to find the alloca instruction corresponding to arguments "
2721 "for extracted function");
2722 std::optional<TypeSize> ArgAllocSize =
2723 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2724 assert(ArgAllocSize &&
2725 "Unable to determine size of arguments for extracted function");
2726 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2727
2728 // Emit the @__kmpc_omp_task_alloc runtime call
2729 // The runtime call returns a pointer to an area where the task captured
2730 // variables must be copied before the task is run (TaskData)
2731 CallInst *TaskData = Builder.CreateCall(
2732 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2733 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2734 /*task_func=*/&OutlinedFn});
2735
2736 Value *Shareds = StaleCI->getArgOperand(1);
2737 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2738 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2739 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2740 SharedsSize);
2741 // Get the pointer to loop lb, ub, step from task ptr
2742 // and set up the lowerbound,upperbound and step values
2743 llvm::Value *Lb = Builder.CreateGEP(
2744 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2745
2746 llvm::Value *Ub = Builder.CreateGEP(
2747 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2748
2749 llvm::Value *Step = Builder.CreateGEP(
2750 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2751 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2752
2753 // set up the arguments for emitting kmpc_taskloop runtime call
2754 // setting values for ifval, nogroup, sched, grainsize, task_dup
2755 Value *IfCondVal =
2756 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2757 : Builder.getInt32(1);
2758 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2759 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2760 Value *NoGroupVal = Builder.getInt32(1);
2761 Value *SchedVal = Builder.getInt32(Sched);
2762 Value *GrainSizeVal =
2763 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2764 : Builder.getInt64(0);
2765 Value *TaskDup = TaskDupFn;
2766
2767 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2768 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2769
2770 // taskloop runtime call
2771 Function *TaskloopFn =
2772 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2773 Builder.CreateCall(TaskloopFn, Args);
2774
2775 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2776 // nogroup is not defined
2777 if (!NoGroup) {
2778 Function *EndTaskgroupFn =
2779 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2780 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2781 }
2782
2783 StaleCI->eraseFromParent();
2784
2785 Builder.SetInsertPoint(TaskloopAllocaBB->begin());
2786
2787 LoadInst *SharedsOutlined =
2788 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2789 OutlinedFn.getArg(1)->replaceUsesWithIf(
2790 SharedsOutlined,
2791 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2792
2793 Value *IV = CLI->getIndVar();
2794 Type *IVTy = IV->getType();
2795 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2796
2797 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2798 // UpperBound. These GEP's can be reused for loading the tasks respective
2799 // bounds.
2800 Value *TaskLB = nullptr;
2801 Value *TaskUB = nullptr;
2802 Value *TaskStep = nullptr;
2803 Value *LoadTaskLB = nullptr;
2804 Value *LoadTaskUB = nullptr;
2805 Value *LoadTaskStep = nullptr;
2806 for (Instruction &I : *TaskloopAllocaBB) {
2807 if (I.getOpcode() == Instruction::GetElementPtr) {
2808 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2809 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2810 switch (CI->getZExtValue()) {
2811 case 0:
2812 TaskLB = &I;
2813 break;
2814 case 1:
2815 TaskUB = &I;
2816 break;
2817 case 2:
2818 TaskStep = &I;
2819 break;
2820 }
2821 }
2822 } else if (I.getOpcode() == Instruction::Load) {
2823 LoadInst &Load = cast<LoadInst>(I);
2824 if (Load.getPointerOperand() == TaskLB) {
2825 assert(TaskLB != nullptr && "Expected value for TaskLB");
2826 LoadTaskLB = &I;
2827 } else if (Load.getPointerOperand() == TaskUB) {
2828 assert(TaskUB != nullptr && "Expected value for TaskUB");
2829 LoadTaskUB = &I;
2830 } else if (Load.getPointerOperand() == TaskStep) {
2831 assert(TaskStep != nullptr && "Expected value for TaskStep");
2832 LoadTaskStep = &I;
2833 }
2834 }
2835 }
2836
2837 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2838
2839 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2840 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2841 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2842 Value *TripCountMinusOne = Builder.CreateSDiv(
2843 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2844 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2845 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2846 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2847 // set the trip count in the CLI
2848 CLI->setTripCount(CastedTripCount);
2849
2850 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
2851
2852 if (NumOfCollapseLoops > 1) {
2853 llvm::SmallVector<User *> UsersToReplace;
2854 // When using the collapse clause, the bounds of the loop have to be
2855 // adjusted to properly represent the iterator of the outer loop.
2856 Value *IVPlusTaskLB = Builder.CreateAdd(
2857 CLI->getIndVar(),
2858 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2859 // To ensure every Use is correctly captured, we first want to record
2860 // which users to replace the value in, and then replace the value.
2861 for (auto IVUse = CLI->getIndVar()->uses().begin();
2862 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2863 User *IVUser = IVUse->getUser();
2864 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2865 if (Op->getOpcode() == Instruction::URem ||
2866 Op->getOpcode() == Instruction::UDiv) {
2867 UsersToReplace.push_back(IVUser);
2868 }
2869 }
2870 }
2871 for (User *User : UsersToReplace) {
2872 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2873 }
2874 } else {
2875 // The canonical loop is generated with a fixed lower bound. We need to
2876 // update the index calculation code to use the task's lower bound. The
2877 // generated code looks like this:
2878 // %omp_loop.iv = phi ...
2879 // ...
2880 // %tmp = mul [type] %omp_loop.iv, step
2881 // %user_index = add [type] tmp, lb
2882 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2883 // of the normalised induction variable:
2884 // 1. This one: converting the normalised IV to the user IV
2885 // 2. The increment (add)
2886 // 3. The comparison against the trip count (icmp)
2887 // (1) is the only use that is a mul followed by an add so this cannot
2888 // match other IR.
2889 assert(CLI->getIndVar()->getNumUses() == 3 &&
2890 "Canonical loop should have exactly three uses of the ind var");
2891 for (User *IVUser : CLI->getIndVar()->users()) {
2892 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2893 if (Mul->getOpcode() == Instruction::Mul) {
2894 for (User *MulUser : Mul->users()) {
2895 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2896 if (Add->getOpcode() == Instruction::Add) {
2897 Add->setOperand(1, CastedTaskLB);
2898 }
2899 }
2900 }
2901 }
2902 }
2903 }
2904 }
2905
2906 FakeLB->replaceAllUsesWith(CastedLBVal);
2907 FakeUB->replaceAllUsesWith(CastedUBVal);
2908 FakeStep->replaceAllUsesWith(CastedStepVal);
2909 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2910 I->eraseFromParent();
2911 }
2912 };
2913
2914 addOutlineInfo(std::move(OI));
2915 Builder.SetInsertPoint(TaskloopExitBB->begin());
2916 return Builder.saveIP();
2917}
2918
2921 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2923 llvm::Type::getInt32Ty(M.getContext()));
2924}
2925
2927 const LocationDescription &Loc, InsertPointTy AllocaIP,
2928 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2929 bool Tied, Value *Final, Value *IfCondition,
2930 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2931 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2932
2933 if (!updateToLocation(Loc))
2934 return InsertPointTy();
2935
2936 uint32_t SrcLocStrSize;
2937 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2938 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2939 // The current basic block is split into four basic blocks. After outlining,
2940 // they will be mapped as follows:
2941 // ```
2942 // def current_fn() {
2943 // current_basic_block:
2944 // br label %task.exit
2945 // task.exit:
2946 // ; instructions after task
2947 // }
2948 // def outlined_fn() {
2949 // task.alloca:
2950 // br label %task.body
2951 // task.body:
2952 // ret void
2953 // }
2954 // ```
2955 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2956 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2957 BasicBlock *TaskAllocaBB =
2958 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2959
2960 InsertPointTy TaskAllocaIP = TaskAllocaBB->begin();
2961 InsertPointTy TaskBodyIP = TaskBodyBB->begin();
2962 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2963 return Err;
2964
2965 auto OI = std::make_unique<OutlineInfo>();
2966 OI->EntryBB = TaskAllocaBB;
2967 OI->OuterAllocBB = AllocaIP.getNodeParent();
2968 OI->ExitBB = TaskExitBB;
2969 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2970 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2971
2972 // Add the thread ID argument.
2974 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2975 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2976
2977 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2978 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2979 TaskAllocaBB,
2980 ToBeDeleted](Function &OutlinedFn) mutable {
2981 // Replace the Stale CI by appropriate RTL function call.
2982 assert(OutlinedFn.hasOneUse() &&
2983 "there must be a single user for the outlined function");
2984 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2985
2986 // HasShareds is true if any variables are captured in the outlined region,
2987 // false otherwise.
2988 bool HasShareds = StaleCI->arg_size() > 1;
2989 Builder.SetInsertPoint(StaleCI);
2990
2991 // Gather the arguments for emitting the runtime call for
2992 // @__kmpc_omp_task_alloc
2993 Function *TaskAllocFn =
2994 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2995
2996 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2997 // call.
2998 Value *ThreadID = getOrCreateThreadID(Ident);
2999
3000 // Argument - `flags`
3001 // Task is tied iff (Flags & 1) == 1.
3002 // Task is untied iff (Flags & 1) == 0.
3003 // Task is final iff (Flags & 2) == 2.
3004 // Task is not final iff (Flags & 2) == 0.
3005 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
3006 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
3007 // Task is detachable iff (Flags & 64) == 64.
3008 // Task is not detachable iff (Flags & 64) == 0.
3009 // Task is priority iff (Flags & 32) == 32.
3010 // Task is not priority iff (Flags & 32) == 0.
3011 // Task is free-agent eligible iff (Flags & 128) == 128.
3012 // Task is not free-agent eligible iff (Flags & 128) == 0.
3013 // TODO: Handle the other flags.
3014 Value *Flags = Builder.getInt32(Tied);
3015 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
3016 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
3017 if (Final) {
3018 Value *FinalFlag =
3019 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3020 Flags = Builder.CreateOr(FinalFlag, Flags);
3021 }
3022
3023 if (Mergeable || UseMergedIf0Path)
3024 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3025 if (EventHandle)
3026 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3027 if (Priority)
3028 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3029 if (FreeAgent)
3030 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3031
3032 // Argument - `sizeof_kmp_task_t` (TaskSize)
3033 // Tasksize refers to the size in bytes of kmp_task_t data structure
3034 // including private vars accessed in task.
3035 // TODO: add kmp_task_t_with_privates (privates)
3036 Value *TaskSize = Builder.getInt64(
3037 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3038
3039 // Argument - `sizeof_shareds` (SharedsSize)
3040 // SharedsSize refers to the shareds array size in the kmp_task_t data
3041 // structure.
3042 Value *SharedsSize = Builder.getInt64(0);
3043 if (HasShareds) {
3044 AllocaInst *ArgStructAlloca =
3046 assert(ArgStructAlloca &&
3047 "Unable to find the alloca instruction corresponding to arguments "
3048 "for extracted function");
3049 std::optional<TypeSize> ArgAllocSize =
3050 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3051 assert(ArgAllocSize &&
3052 "Unable to determine size of arguments for extracted function");
3053 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3054 }
3055 // Emit the @__kmpc_omp_task_alloc runtime call
3056 // The runtime call returns a pointer to an area where the task captured
3057 // variables must be copied before the task is run (TaskData)
3059 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3060 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3061 /*task_func=*/&OutlinedFn});
3062
3063 if (Affinities.Count && Affinities.Info) {
3065 OMPRTL___kmpc_omp_reg_task_with_affinity);
3066
3067 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3068 Affinities.Count, Affinities.Info});
3069 }
3070
3071 // Emit detach clause initialization.
3072 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3073 // task_descriptor);
3074 if (EventHandle) {
3076 OMPRTL___kmpc_task_allow_completion_event);
3077 llvm::Value *EventVal =
3078 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3079 llvm::Value *EventHandleAddr =
3080 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3081 Builder.getPtrTy(0));
3082 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3083 Builder.CreateStore(EventVal, EventHandleAddr);
3084 }
3085 // Copy the arguments for outlined function
3086 if (HasShareds) {
3087 Value *Shareds = StaleCI->getArgOperand(1);
3088 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3089 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3090 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3091 SharedsSize);
3092 }
3093
3094 if (Priority) {
3095 //
3096 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3097 // we populate the priority information into the "kmp_task_t" here
3098 //
3099 // The struct "kmp_task_t" definition is available in kmp.h
3100 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3101 // data2 is used for priority
3102 //
3103 Type *Int32Ty = Builder.getInt32Ty();
3104 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3105 // kmp_task_t* => { ptr }
3106 Type *TaskPtr = StructType::get(VoidPtr);
3107 Value *TaskGEP =
3108 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3109 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3110 Type *TaskStructType = StructType::get(
3111 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3112 Value *PriorityData = Builder.CreateInBoundsGEP(
3113 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3114 // kmp_cmplrdata_t => { ptr, ptr }
3115 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3116 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3117 PriorityData, {Zero, Zero});
3118 Builder.CreateStore(Priority, CmplrData);
3119 }
3120
3121 Value *DepArray = nullptr;
3122 Value *NumDeps = nullptr;
3123 if (Dependencies.DepArray) {
3124 DepArray = Dependencies.DepArray;
3125 NumDeps = Dependencies.NumDeps;
3126 } else if (!Dependencies.Deps.empty()) {
3127 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3128 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3129 }
3130
3131 // In the presence of the `if` clause, the following IR is generated:
3132 // ...
3133 // %data = call @__kmpc_omp_task_alloc(...)
3134 // br i1 %if_condition, label %then, label %else
3135 // then:
3136 // call @__kmpc_omp_task(...)
3137 // br label %exit
3138 // else:
3139 // ;; Wait for resolution of dependencies, if any, before
3140 // ;; beginning the task
3141 // call @__kmpc_omp_wait_deps(...)
3142 // call @__kmpc_omp_task_begin_if0(...)
3143 // call @outlined_fn(...)
3144 // call @__kmpc_omp_task_complete_if0(...)
3145 // br label %exit
3146 // exit:
3147 // ...
3148 if (IfCondition && !UseMergedIf0Path) {
3149 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3150 // terminator.
3151 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3152 Instruction *IfTerminator =
3153 Builder.GetInsertPoint()->getParent()->getTerminator();
3154 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3155 Builder.SetInsertPoint(IfTerminator);
3156 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3157 &ElseTI);
3158 Builder.SetInsertPoint(ElseTI);
3159
3160 if (DepArray) {
3161 Function *TaskWaitFn =
3162 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3164 TaskWaitFn,
3165 {Ident, ThreadID, NumDeps, DepArray,
3166 ConstantInt::get(Builder.getInt32Ty(), 0),
3168 }
3169 Function *TaskBeginFn =
3170 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3171 Function *TaskCompleteFn =
3172 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3173 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3174 CallInst *CI = nullptr;
3175 if (HasShareds)
3176 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3177 else
3178 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3179 CI->setDebugLoc(StaleCI->getDebugLoc());
3180 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3181 Builder.SetInsertPoint(ThenTI);
3182 }
3183
3184 if (DepArray) {
3185 Function *TaskFn =
3186 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3188 TaskFn,
3189 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3190 ConstantInt::get(Builder.getInt32Ty(), 0),
3192
3193 } else {
3194 // Emit the @__kmpc_omp_task runtime call to spawn the task
3195 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3196 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3197 }
3198
3199 StaleCI->eraseFromParent();
3200
3201 Builder.SetInsertPoint(TaskAllocaBB->begin());
3202 if (HasShareds) {
3203 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3204 OutlinedFn.getArg(1)->replaceUsesWithIf(
3205 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3206 }
3207
3208 // The insert point may refer to one of the instructions about to be
3209 // deleted. It is not needed anymore so clear it instead of leaving it
3210 // dangling.
3211 Builder.ClearInsertionPoint();
3212 for (Instruction *I : llvm::reverse(ToBeDeleted))
3213 I->eraseFromParent();
3214 };
3215
3216 addOutlineInfo(std::move(OI));
3217 Builder.SetInsertPoint(TaskExitBB->begin());
3218
3219 return Builder.saveIP();
3220}
3221
3223 const LocationDescription &Loc, InsertPointTy AllocaIP,
3224 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3225 if (!updateToLocation(Loc))
3226 return InsertPointTy();
3227
3228 uint32_t SrcLocStrSize;
3229 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3230 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3231 Value *ThreadID = getOrCreateThreadID(Ident);
3232
3233 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3234 Function *TaskgroupFn =
3235 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3236 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3237
3238 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3239 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3240 return Err;
3241
3242 Builder.SetInsertPoint(TaskgroupExitBB);
3243 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3244 Function *EndTaskgroupFn =
3245 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3246 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3247
3248 return Builder.saveIP();
3249}
3250
3252 const LocationDescription &Loc, InsertPointTy AllocaIP,
3254 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3255 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3256
3257 if (!updateToLocation(Loc))
3258 return Loc.IP;
3259
3260 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3261
3262 // Each section is emitted as a switch case
3263 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3264 // -> OMP.createSection() which generates the IR for each section
3265 // Iterate through all sections and emit a switch construct:
3266 // switch (IV) {
3267 // case 0:
3268 // <SectionStmt[0]>;
3269 // break;
3270 // ...
3271 // case <NumSection> - 1:
3272 // <SectionStmt[<NumSection> - 1]>;
3273 // break;
3274 // }
3275 // ...
3276 // section_loop.after:
3277 // <FiniCB>;
3278 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3279 Builder.restoreIP(CodeGenIP);
3281 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3282 Function *CurFn = Continue->getParent();
3283 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3284
3285 unsigned CaseNumber = 0;
3286 for (auto SectionCB : SectionCBs) {
3288 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3289 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3290 Builder.SetInsertPoint(CaseBB);
3291 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3292 if (Error Err = SectionCB(InsertPointTy(), CaseEndBr->getIterator(), {}))
3293 return Err;
3294 CaseNumber++;
3295 }
3296 // remove the existing terminator from body BB since there can be no
3297 // terminators after switch/case
3298 return Error::success();
3299 };
3300 // Loop body ends here
3301 // LowerBound, UpperBound, and STride for createCanonicalLoop
3302 Type *I32Ty = Type::getInt32Ty(M.getContext());
3303 Value *LB = ConstantInt::get(I32Ty, 0);
3304 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3305 Value *ST = ConstantInt::get(I32Ty, 1);
3307 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3308 if (!LoopInfo)
3309 return LoopInfo.takeError();
3310
3311 InsertPointOrErrorTy WsloopIP =
3312 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3313 WorksharingLoopType::ForStaticLoop, !IsNowait);
3314 if (!WsloopIP)
3315 return WsloopIP.takeError();
3316 InsertPointTy AfterIP = *WsloopIP;
3317
3318 BasicBlock *LoopFini = AfterIP.getNodeParent()->getSinglePredecessor();
3319 assert(LoopFini && "Bad structure of static workshare loop finalization");
3320
3321 // Apply the finalization callback in LoopAfterBB
3322 auto FiniInfo = FinalizationStack.pop_back_val();
3323 assert(FiniInfo.DK == OMPD_sections &&
3324 "Unexpected finalization stack state!");
3325 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3326 return Err;
3327
3328 return AfterIP;
3329}
3330
3333 BodyGenCallbackTy BodyGenCB,
3334 FinalizeCallbackTy FiniCB) {
3335 if (!updateToLocation(Loc))
3336 return Loc.IP;
3337
3338 auto FiniCBWrapper = [&](InsertPointTy IP) {
3339 if (IP != IP.getNodeParent()->end())
3340 return FiniCB(IP);
3341 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3342 // will fail because that function requires the Finalization Basic Block to
3343 // have a terminator, which is already removed by EmitOMPRegionBody.
3344 // IP is currently at cancelation block.
3345 // We need to backtrack to the condition block to fetch
3346 // the exit block and create a branch from cancelation
3347 // to exit block.
3349 Builder.restoreIP(IP);
3350 auto *CaseBB = Loc.IP.getNodeParent();
3351 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3352 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3353 Instruction *I = Builder.CreateBr(ExitBB);
3354 IP = I->getIterator();
3355 return FiniCB(IP);
3356 };
3357
3358 Directive OMPD = Directive::OMPD_sections;
3359 // Since we are using Finalization Callback here, HasFinalize
3360 // and IsCancellable have to be true
3361 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3362 /*Conditional*/ false, /*hasFinalize*/ true,
3363 /*IsCancellable*/ true);
3364}
3365
3371
3372Value *OpenMPIRBuilder::getGPUThreadID() {
3375 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3376 {});
3377}
3378
3379Value *OpenMPIRBuilder::getGPUWarpSize() {
3381 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3382}
3383
3384Value *OpenMPIRBuilder::getNVPTXWarpID() {
3385 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3386 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3387}
3388
3389Value *OpenMPIRBuilder::getNVPTXLaneID() {
3390 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3391 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3392 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3393 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3394 "nvptx_lane_id");
3395}
3396
3397Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3398 Type *ToType) {
3399 Type *FromType = From->getType();
3400 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3401 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3402 assert(FromSize > 0 && "From size must be greater than zero");
3403 assert(ToSize > 0 && "To size must be greater than zero");
3404 if (FromType == ToType)
3405 return From;
3406 if (FromSize == ToSize)
3407 return Builder.CreateBitCast(From, ToType);
3408 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3409 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3410 InsertPointTy SaveIP = Builder.saveIP();
3411 Builder.restoreIP(AllocaIP);
3412 Value *CastItem = Builder.CreateAlloca(ToType);
3413 Builder.restoreIP(SaveIP);
3414
3415 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3416 CastItem, Builder.getPtrTy(0));
3417 Builder.CreateStore(From, ValCastItem);
3418 return Builder.CreateLoad(ToType, CastItem);
3419}
3420
3421Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3422 Value *Element,
3423 Type *ElementType,
3424 Value *Offset) {
3425 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3426 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3427
3428 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3429 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3430 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3431 Value *WarpSize =
3432 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3434 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3435 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3436 Value *WarpSizeCast =
3437 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3438 Value *ShuffleCall =
3439 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3440 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3441 // down to the requested element type, otherwise storing the result would
3442 // write past the end of an element narrower than the shuffle width.
3443 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3444}
3445
3446void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3447 Value *DstAddr, Type *ElemType,
3448 Value *Offset, Type *ReductionArrayTy,
3449 bool IsByRefElem) {
3450 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3451 // Create the loop over the big sized data.
3452 // ptr = (void*)Elem;
3453 // ptrEnd = (void*) Elem + 1;
3454 // Step = 8;
3455 // while (ptr + Step < ptrEnd)
3456 // shuffle((int64_t)*ptr);
3457 // Step = 4;
3458 // while (ptr + Step < ptrEnd)
3459 // shuffle((int32_t)*ptr);
3460 // ...
3461 Type *IndexTy = Builder.getIndexTy(
3462 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3463 Value *ElemPtr = DstAddr;
3464 Value *Ptr = SrcAddr;
3465 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3466 if (Size < IntSize)
3467 continue;
3468 Type *IntType = Builder.getIntNTy(IntSize * 8);
3469 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3470 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3471 Value *SrcAddrGEP =
3472 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3473 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3474 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3475
3476 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3477 if ((Size / IntSize) > 1) {
3478 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3479 SrcAddrGEP, Builder.getPtrTy());
3480 BasicBlock *PreCondBB =
3481 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3482 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3483 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3484 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3485 emitBlock(PreCondBB, CurFunc);
3486 PHINode *PhiSrc =
3487 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3488 PhiSrc->addIncoming(Ptr, CurrentBB);
3489 PHINode *PhiDest =
3490 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3491 PhiDest->addIncoming(ElemPtr, CurrentBB);
3492 Ptr = PhiSrc;
3493 ElemPtr = PhiDest;
3494 Value *PtrDiff = Builder.CreatePtrDiff(
3495 Builder.getInt8Ty(), PtrEnd,
3496 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3497 Builder.CreateCondBr(
3498 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3499 ExitBB);
3500 emitBlock(ThenBB, CurFunc);
3501 Value *Res = createRuntimeShuffleFunction(
3502 AllocaIP,
3503 Builder.CreateAlignedLoad(
3504 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3505 IntType, Offset);
3506 Builder.CreateAlignedStore(Res, ElemPtr,
3507 M.getDataLayout().getPrefTypeAlign(ElemType));
3508 Value *LocalPtr =
3509 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3510 Value *LocalElemPtr =
3511 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3512 PhiSrc->addIncoming(LocalPtr, ThenBB);
3513 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3514 emitBranch(PreCondBB);
3515 emitBlock(ExitBB, CurFunc);
3516 } else {
3517 // The shuffled value comes back as the chunk's integer type, so the
3518 // store covers exactly this chunk regardless of what ElemType is.
3519 Value *Res = createRuntimeShuffleFunction(
3520 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3521 Builder.CreateStore(Res, ElemPtr);
3522 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3523 ElemPtr =
3524 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3525 }
3526 Size = Size % IntSize;
3527 }
3528}
3529
3530Error OpenMPIRBuilder::emitReductionListCopy(
3531 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3532 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3533 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3534 Type *IndexTy = Builder.getIndexTy(
3535 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3536 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3537
3538 // Iterates, element-by-element, through the source Reduce list and
3539 // make a copy.
3540 for (auto En : enumerate(ReductionInfos)) {
3541 const ReductionInfo &RI = En.value();
3542 Value *SrcElementAddr = nullptr;
3543 AllocaInst *DestAlloca = nullptr;
3544 Value *DestElementAddr = nullptr;
3545 Value *DestElementPtrAddr = nullptr;
3546 // Should we shuffle in an element from a remote lane?
3547 bool ShuffleInElement = false;
3548 // Set to true to update the pointer in the dest Reduce list to a
3549 // newly created element.
3550 bool UpdateDestListPtr = false;
3551
3552 // Step 1.1: Get the address for the src element in the Reduce list.
3553 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3554 ReductionArrayTy, SrcBase,
3555 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3556 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3557
3558 // Step 1.2: Create a temporary to store the element in the destination
3559 // Reduce list.
3560 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3561 ReductionArrayTy, DestBase,
3562 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3563 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3564 switch (Action) {
3566 InsertPointTy CurIP = Builder.saveIP();
3567 Builder.restoreIP(AllocaIP);
3568
3569 Type *DestAllocaType =
3570 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3571 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3572 ".omp.reduction.element");
3573 DestAlloca->setAlignment(
3574 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3575 DestElementAddr = DestAlloca;
3576 DestElementAddr =
3577 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3578 DestElementAddr->getName() + ".ascast");
3579 Builder.restoreIP(CurIP);
3580 ShuffleInElement = true;
3581 UpdateDestListPtr = true;
3582 break;
3583 }
3585 DestElementAddr =
3586 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3587 break;
3588 }
3589 }
3590
3591 // Now that all active lanes have read the element in the
3592 // Reduce list, shuffle over the value from the remote lane.
3593 if (ShuffleInElement) {
3594 Type *ShuffleType = RI.ElementType;
3595 Value *ShuffleSrcAddr = SrcElementAddr;
3596 Value *ShuffleDestAddr = DestElementAddr;
3597 AllocaInst *LocalStorage = nullptr;
3598
3599 if (IsByRefElem) {
3600 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3601 assert(RI.ByRefAllocatedType &&
3602 "Expected by-ref allocated type to be set");
3603 // For by-ref reductions, we need to copy from the remote lane the
3604 // actual value of the partial reduction computed by that remote lane;
3605 // rather than, for example, a pointer to that data or, even worse, a
3606 // pointer to the descriptor of the by-ref reduction element.
3607 ShuffleType = RI.ByRefElementType;
3608
3609 if (RI.DataPtrPtrGen) {
3610 // Descriptor-based by-ref: extract data pointer from descriptor.
3611 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3612 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3613
3614 if (!GenResult)
3615 return GenResult.takeError();
3616
3617 ShuffleSrcAddr =
3618 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3619
3620 {
3621 InsertPointTy OldIP = Builder.saveIP();
3622 Builder.restoreIP(AllocaIP);
3623
3624 LocalStorage = Builder.CreateAlloca(ShuffleType);
3625 Builder.restoreIP(OldIP);
3626 ShuffleDestAddr = LocalStorage;
3627 }
3628 } else {
3629 // Non-descriptor by-ref: the pointer already references data
3630 // directly. Shuffle into the destination alloca.
3631 ShuffleDestAddr = DestElementAddr;
3632 }
3633 }
3634
3635 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3636 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3637
3638 if (IsByRefElem && RI.DataPtrPtrGen) {
3639 // Copy descriptor from source and update base_ptr to shuffled data
3640 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3641 DestAlloca, Builder.getPtrTy(), ".ascast");
3642
3643 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3644 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3645 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3646
3647 if (!GenResult)
3648 return GenResult.takeError();
3649 }
3650 } else {
3651 switch (RI.EvaluationKind) {
3652 case EvalKind::Scalar: {
3653 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3654 // Store the source element value to the dest element address.
3655 Builder.CreateStore(Elem, DestElementAddr);
3656 break;
3657 }
3658 case EvalKind::Complex: {
3659 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3660 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3661 Value *SrcReal = Builder.CreateLoad(
3662 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3663 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3664 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3665 Value *SrcImg = Builder.CreateLoad(
3666 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3667
3668 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3669 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3670 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3671 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3672 Builder.CreateStore(SrcReal, DestRealPtr);
3673 Builder.CreateStore(SrcImg, DestImgPtr);
3674 break;
3675 }
3676 case EvalKind::Aggregate: {
3677 Value *SizeVal = Builder.getInt64(
3678 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3679 Builder.CreateMemCpy(
3680 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3681 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3682 SizeVal, false);
3683 break;
3684 }
3685 };
3686 }
3687
3688 // Step 3.1: Modify reference in dest Reduce list as needed.
3689 // Modifying the reference in Reduce list to point to the newly
3690 // created element. The element is live in the current function
3691 // scope and that of functions it invokes (i.e., reduce_function).
3692 // RemoteReduceData[i] = (void*)&RemoteElem
3693 if (UpdateDestListPtr) {
3694 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3695 DestElementAddr, Builder.getPtrTy(),
3696 DestElementAddr->getName() + ".ascast");
3697 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3698 }
3699 }
3700
3701 return Error::success();
3702}
3703
3704Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3705 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3706 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3707 IRBuilder<>::InsertPointGuard IPG(Builder);
3708 LLVMContext &Ctx = M.getContext();
3709 FunctionType *FuncTy = FunctionType::get(
3710 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3711 /* IsVarArg */ false);
3712 Function *WcFunc =
3714 "_omp_reduction_inter_warp_copy_func", &M);
3715 WcFunc->setCallingConv(Config.getRuntimeCC());
3716 WcFunc->setAttributes(FuncAttrs);
3717 WcFunc->addParamAttr(0, Attribute::NoUndef);
3718 WcFunc->addParamAttr(1, Attribute::NoUndef);
3719 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3720 Builder.SetInsertPoint(EntryBB);
3721 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3722
3723 // ReduceList: thread local Reduce list.
3724 // At the stage of the computation when this function is called, partially
3725 // aggregated values reside in the first lane of every active warp.
3726 Argument *ReduceListArg = WcFunc->getArg(0);
3727 // NumWarps: number of warps active in the parallel region. This could
3728 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3729 Argument *NumWarpsArg = WcFunc->getArg(1);
3730
3731 // This array is used as a medium to transfer, one reduce element at a time,
3732 // the data from the first lane of every warp to lanes in the first warp
3733 // in order to perform the final step of a reduction in a parallel region
3734 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3735 // for reduced latency, as well as to have a distinct copy for concurrently
3736 // executing target regions. The array is declared with common linkage so
3737 // as to be shared across compilation units.
3738 StringRef TransferMediumName =
3739 "__openmp_nvptx_data_transfer_temporary_storage";
3740 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3741 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3742 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3743 if (!TransferMedium) {
3744 TransferMedium = new GlobalVariable(
3745 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3746 UndefValue::get(ArrayTy), TransferMediumName,
3747 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3748 /*AddressSpace=*/3);
3749 }
3750
3751 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3752 Value *GPUThreadID = getGPUThreadID();
3753 // nvptx_lane_id = nvptx_id % warpsize
3754 Value *LaneID = getNVPTXLaneID();
3755 // nvptx_warp_id = nvptx_id / warpsize
3756 Value *WarpID = getNVPTXWarpID();
3757
3758 InsertPointTy AllocaIP = Builder.GetInsertBlock()->getFirstInsertionPt();
3759 Type *Arg0Type = ReduceListArg->getType();
3760 Type *Arg1Type = NumWarpsArg->getType();
3761 Builder.restoreIP(AllocaIP);
3762 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3763 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3764 AllocaInst *NumWarpsAlloca =
3765 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3766 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3767 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3768 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3769 NumWarpsAlloca, Builder.getPtrTy(0),
3770 NumWarpsAlloca->getName() + ".ascast");
3771 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3772 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3773 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3774 InsertPointTy CodeGenIP =
3775 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3776 Builder.restoreIP(CodeGenIP);
3777
3778 Value *ReduceList =
3779 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3780
3781 for (auto En : enumerate(ReductionInfos)) {
3782 //
3783 // Warp master copies reduce element to transfer medium in __shared__
3784 // memory.
3785 //
3786 const ReductionInfo &RI = En.value();
3787 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3788 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3789 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3790 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3791 Type *CType = Builder.getIntNTy(TySize * 8);
3792
3793 unsigned NumIters = RealTySize / TySize;
3794 if (NumIters == 0)
3795 continue;
3796 Value *Cnt = nullptr;
3797 Value *CntAddr = nullptr;
3798 BasicBlock *PrecondBB = nullptr;
3799 BasicBlock *ExitBB = nullptr;
3800 if (NumIters > 1) {
3801 CodeGenIP = Builder.saveIP();
3802 Builder.restoreIP(AllocaIP);
3803 CntAddr =
3804 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3805
3806 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3807 CntAddr->getName() + ".ascast");
3808 Builder.restoreIP(CodeGenIP);
3809 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3810 CntAddr,
3811 /*Volatile=*/false);
3812 PrecondBB = BasicBlock::Create(Ctx, "precond");
3813 ExitBB = BasicBlock::Create(Ctx, "exit");
3814 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3815 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3816 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3817 /*Volatile=*/false);
3818 Value *Cmp = Builder.CreateICmpULT(
3819 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3820 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3821 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3822 }
3823
3824 // kmpc_barrier.
3825 InsertPointOrErrorTy BarrierIP1 =
3827 omp::Directive::OMPD_unknown,
3828 /* ForceSimpleCall */ false,
3829 /* CheckCancelFlag */ true);
3830 if (!BarrierIP1)
3831 return BarrierIP1.takeError();
3832 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3833 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3834 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3835
3836 // if (lane_id == 0)
3837 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3838 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3839 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3840
3841 // Reduce element = LocalReduceList[i]
3842 auto *RedListArrayTy =
3843 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3844 Type *IndexTy = Builder.getIndexTy(
3845 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3846 Value *ElemPtrPtr =
3847 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3848 {ConstantInt::get(IndexTy, 0),
3849 ConstantInt::get(IndexTy, En.index())});
3850 // elemptr = ((CopyType*)(elemptrptr)) + I
3851 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3852
3853 if (IsByRefElem && RI.DataPtrPtrGen) {
3854 InsertPointOrErrorTy GenRes =
3855 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3856
3857 if (!GenRes)
3858 return GenRes.takeError();
3859
3860 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3861 }
3862
3863 if (NumIters > 1)
3864 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3865
3866 // Get pointer to location in transfer medium.
3867 // MediumPtr = &medium[warp_id]
3868 Value *MediumPtr = Builder.CreateInBoundsGEP(
3869 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3870 // elem = *elemptr
3871 //*MediumPtr = elem
3872 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3873 // Store the source element value to the dest element address.
3874 Builder.CreateStore(Elem, MediumPtr,
3875 /*IsVolatile*/ true);
3876 Builder.CreateBr(MergeBB);
3877
3878 // else
3879 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3880 Builder.CreateBr(MergeBB);
3881
3882 // endif
3883 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3884 InsertPointOrErrorTy BarrierIP2 =
3886 omp::Directive::OMPD_unknown,
3887 /* ForceSimpleCall */ false,
3888 /* CheckCancelFlag */ true);
3889 if (!BarrierIP2)
3890 return BarrierIP2.takeError();
3891
3892 // Warp 0 copies reduce element from transfer medium
3893 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3894 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3895 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3896
3897 Value *NumWarpsVal =
3898 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3899 // Up to 32 threads in warp 0 are active.
3900 Value *IsActiveThread =
3901 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3902 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3903
3904 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3905
3906 // SecMediumPtr = &medium[tid]
3907 // SrcMediumVal = *SrcMediumPtr
3908 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3909 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3910 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3911 Value *TargetElemPtrPtr =
3912 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3913 {ConstantInt::get(IndexTy, 0),
3914 ConstantInt::get(IndexTy, En.index())});
3915 Value *TargetElemPtrVal =
3916 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3917 Value *TargetElemPtr = TargetElemPtrVal;
3918
3919 if (IsByRefElem && RI.DataPtrPtrGen) {
3920 InsertPointOrErrorTy GenRes =
3921 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3922
3923 if (!GenRes)
3924 return GenRes.takeError();
3925
3926 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3927 }
3928
3929 if (NumIters > 1)
3930 TargetElemPtr =
3931 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3932
3933 // *TargetElemPtr = SrcMediumVal;
3934 Value *SrcMediumValue =
3935 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3936 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3937 Builder.CreateBr(W0MergeBB);
3938
3939 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3940 Builder.CreateBr(W0MergeBB);
3941
3942 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3943
3944 if (NumIters > 1) {
3945 Cnt = Builder.CreateNSWAdd(
3946 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3947 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3948
3949 auto *CurFn = Builder.GetInsertBlock()->getParent();
3950 emitBranch(PrecondBB);
3951 emitBlock(ExitBB, CurFn);
3952 }
3953 RealTySize %= TySize;
3954 }
3955 }
3956
3957 Builder.CreateRetVoid();
3958
3959 return WcFunc;
3960}
3961
3962Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3963 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3964 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3965 LLVMContext &Ctx = M.getContext();
3966 IRBuilder<>::InsertPointGuard IPG(Builder);
3967 FunctionType *FuncTy =
3968 FunctionType::get(Builder.getVoidTy(),
3969 {Builder.getPtrTy(), Builder.getInt16Ty(),
3970 Builder.getInt16Ty(), Builder.getInt16Ty()},
3971 /* IsVarArg */ false);
3972 Function *SarFunc =
3974 "_omp_reduction_shuffle_and_reduce_func", &M);
3975 SarFunc->setCallingConv(Config.getRuntimeCC());
3976 SarFunc->setAttributes(FuncAttrs);
3977 SarFunc->addParamAttr(0, Attribute::NoUndef);
3978 SarFunc->addParamAttr(1, Attribute::NoUndef);
3979 SarFunc->addParamAttr(2, Attribute::NoUndef);
3980 SarFunc->addParamAttr(3, Attribute::NoUndef);
3981 SarFunc->addParamAttr(1, Attribute::SExt);
3982 SarFunc->addParamAttr(2, Attribute::SExt);
3983 SarFunc->addParamAttr(3, Attribute::SExt);
3984 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3985 Builder.SetInsertPoint(EntryBB);
3986 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3987
3988 // Thread local Reduce list used to host the values of data to be reduced.
3989 Argument *ReduceListArg = SarFunc->getArg(0);
3990 // Current lane id; could be logical.
3991 Argument *LaneIDArg = SarFunc->getArg(1);
3992 // Offset of the remote source lane relative to the current lane.
3993 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3994 // Algorithm version. This is expected to be known at compile time.
3995 Argument *AlgoVerArg = SarFunc->getArg(3);
3996
3997 Type *ReduceListArgType = ReduceListArg->getType();
3998 Type *LaneIDArgType = LaneIDArg->getType();
3999 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
4000 Value *ReduceListAlloca = Builder.CreateAlloca(
4001 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
4002 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4003 LaneIDArg->getName() + ".addr");
4004 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
4005 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
4006 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4007 AlgoVerArg->getName() + ".addr");
4008 ArrayType *RedListArrayTy =
4009 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4010
4011 // Create a local thread-private variable to host the Reduce list
4012 // from a remote lane.
4013 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
4014 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
4015
4016 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4017 ReduceListAlloca, ReduceListArgType,
4018 ReduceListAlloca->getName() + ".ascast");
4019 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4020 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4021 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4022 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4023 RemoteLaneOffsetAlloca->getName() + ".ascast");
4024 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4025 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4026 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4027 RemoteReductionListAlloca, Builder.getPtrTy(),
4028 RemoteReductionListAlloca->getName() + ".ascast");
4029
4030 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4031 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4032 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4033 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4034
4035 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4036 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4037 Value *RemoteLaneOffset =
4038 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4039 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4040
4041 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4042
4043 // This loop iterates through the list of reduce elements and copies,
4044 // element by element, from a remote lane in the warp to RemoteReduceList,
4045 // hosted on the thread's stack.
4046 Error EmitRedLsCpRes = emitReductionListCopy(
4047 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4048 ReduceList, RemoteListAddrCast, IsByRef,
4049 {RemoteLaneOffset, nullptr, nullptr});
4050
4051 if (EmitRedLsCpRes)
4052 return EmitRedLsCpRes;
4053
4054 // The actions to be performed on the Remote Reduce list is dependent
4055 // on the algorithm version.
4056 //
4057 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4058 // LaneId % 2 == 0 && Offset > 0):
4059 // do the reduction value aggregation
4060 //
4061 // The thread local variable Reduce list is mutated in place to host the
4062 // reduced data, which is the aggregated value produced from local and
4063 // remote lanes.
4064 //
4065 // Note that AlgoVer is expected to be a constant integer known at compile
4066 // time.
4067 // When AlgoVer==0, the first conjunction evaluates to true, making
4068 // the entire predicate true during compile time.
4069 // When AlgoVer==1, the second conjunction has only the second part to be
4070 // evaluated during runtime. Other conjunctions evaluates to false
4071 // during compile time.
4072 // When AlgoVer==2, the third conjunction has only the second part to be
4073 // evaluated during runtime. Other conjunctions evaluates to false
4074 // during compile time.
4075 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4076 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4077 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4078 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4079 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4080 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4081 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4082 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4083 Value *RemoteOffsetComp =
4084 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4085 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4086 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4087 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4088
4089 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4090 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4091 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4092
4093 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4094 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4095 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4096 ReduceList, Builder.getPtrTy());
4097 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4098 RemoteListAddrCast, Builder.getPtrTy());
4099 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4100 ->addFnAttr(Attribute::NoUnwind);
4101 Builder.CreateBr(MergeBB);
4102
4103 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4104 Builder.CreateBr(MergeBB);
4105
4106 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4107
4108 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4109 // Reduce list.
4110 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4111 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4112 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4113
4114 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4115 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4116 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4117 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4118
4119 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4120
4121 EmitRedLsCpRes = emitReductionListCopy(
4122 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4123 RemoteListAddrCast, ReduceList, IsByRef);
4124
4125 if (EmitRedLsCpRes)
4126 return EmitRedLsCpRes;
4127
4128 Builder.CreateBr(CpyMergeBB);
4129
4130 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4131 Builder.CreateBr(CpyMergeBB);
4132
4133 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4134
4135 Builder.CreateRetVoid();
4136
4137 return SarFunc;
4138}
4139
4141OpenMPIRBuilder::generateReductionDescriptor(
4142 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4143 Type *DescriptorType,
4144 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4145 DataPtrPtrGen) {
4146
4147 // Copy the source descriptor to preserve all metadata (rank, extents,
4148 // strides, etc.)
4149 Value *DescriptorSize =
4150 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4151 Builder.CreateMemCpy(
4152 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4153 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4154 DescriptorSize);
4155
4156 // Update the base pointer field to point to the local shuffled data
4157 Value *DataPtrField;
4158 InsertPointOrErrorTy GenResult =
4159 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4160
4161 if (!GenResult)
4162 return GenResult.takeError();
4163
4164 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4165 DataPtr, Builder.getPtrTy(), ".ascast"),
4166 DataPtrField);
4167
4168 return Builder.saveIP();
4169}
4170
4171Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4172 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4173 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4174 InsertPointTy OldIP = Builder.saveIP();
4175 Builder.restoreIP(AllocaIP);
4176
4177 AllocaInst *DescriptorAlloca =
4178 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4179 DescriptorAlloca->setAlignment(
4180 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4181 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4182 DescriptorAlloca, DescriptorPtrTy,
4183 DescriptorAlloca->getName() + ".ascast");
4184
4185 Builder.restoreIP(OldIP);
4186
4187 InsertPointOrErrorTy GenResult =
4188 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4189 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4190 if (!GenResult)
4191 return GenResult.takeError();
4192
4193 return DescriptorAddr;
4194}
4195
4196Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4197 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4198 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4199 IRBuilder<>::InsertPointGuard IPG(Builder);
4200 LLVMContext &Ctx = M.getContext();
4201 FunctionType *FuncTy = FunctionType::get(
4202 Builder.getVoidTy(),
4203 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4204 /* IsVarArg */ false);
4205 Function *LtGCFunc =
4207 "_omp_reduction_list_to_global_copy_func", &M);
4208 LtGCFunc->setAttributes(FuncAttrs);
4209 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4210 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4211 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4212
4213 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4214 Builder.SetInsertPoint(EntryBlock);
4215 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4216
4217 // Buffer: global reduction buffer.
4218 Argument *BufferArg = LtGCFunc->getArg(0);
4219 // Idx: index of the buffer.
4220 Argument *IdxArg = LtGCFunc->getArg(1);
4221 // ReduceList: thread local Reduce list.
4222 Argument *ReduceListArg = LtGCFunc->getArg(2);
4223
4224 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4225 BufferArg->getName() + ".addr");
4226 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4227 IdxArg->getName() + ".addr");
4228 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4229 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4230 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4231 BufferArgAlloca, Builder.getPtrTy(),
4232 BufferArgAlloca->getName() + ".ascast");
4233 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4234 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4235 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4236 ReduceListArgAlloca, Builder.getPtrTy(),
4237 ReduceListArgAlloca->getName() + ".ascast");
4238
4239 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4240 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4241 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4242
4243 Value *LocalReduceList =
4244 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4245 Value *BufferArgVal =
4246 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4247 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4248 Type *IndexTy = Builder.getIndexTy(
4249 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4250 for (auto En : enumerate(ReductionInfos)) {
4251 const ReductionInfo &RI = En.value();
4252 auto *RedListArrayTy =
4253 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4254 // Reduce element = LocalReduceList[i]
4255 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4256 RedListArrayTy, LocalReduceList,
4257 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4258 // elemptr = ((CopyType*)(elemptrptr)) + I
4259 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4260
4261 // Global = Buffer.VD[Idx];
4262 Value *BufferVD =
4263 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4264 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4265 ReductionsBufferTy, BufferVD, 0, En.index());
4266
4267 switch (RI.EvaluationKind) {
4268 case EvalKind::Scalar: {
4269 Value *TargetElement;
4270
4271 if (IsByRef.empty() || !IsByRef[En.index()]) {
4272 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4273 } else {
4274 if (RI.DataPtrPtrGen) {
4275 InsertPointOrErrorTy GenResult =
4276 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4277
4278 if (!GenResult)
4279 return GenResult.takeError();
4280
4281 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4282 }
4283 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4284 }
4285
4286 Builder.CreateStore(TargetElement, GlobVal);
4287 break;
4288 }
4289 case EvalKind::Complex: {
4290 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4291 RI.ElementType, ElemPtr, 0, 0, ".realp");
4292 Value *SrcReal = Builder.CreateLoad(
4293 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4294 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4295 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4296 Value *SrcImg = Builder.CreateLoad(
4297 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4298
4299 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4300 RI.ElementType, GlobVal, 0, 0, ".realp");
4301 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4302 RI.ElementType, GlobVal, 0, 1, ".imagp");
4303 Builder.CreateStore(SrcReal, DestRealPtr);
4304 Builder.CreateStore(SrcImg, DestImgPtr);
4305 break;
4306 }
4307 case EvalKind::Aggregate: {
4308 Value *SizeVal =
4309 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4310 Builder.CreateMemCpy(
4311 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4312 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4313 break;
4314 }
4315 }
4316 }
4317
4318 Builder.CreateRetVoid();
4319 return LtGCFunc;
4320}
4321
4322Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4323 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4324 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4325 IRBuilder<>::InsertPointGuard IPG(Builder);
4326 LLVMContext &Ctx = M.getContext();
4327 FunctionType *FuncTy = FunctionType::get(
4328 Builder.getVoidTy(),
4329 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4330 /* IsVarArg */ false);
4331 Function *LtGRFunc =
4333 "_omp_reduction_list_to_global_reduce_func", &M);
4334 LtGRFunc->setAttributes(FuncAttrs);
4335 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4336 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4337 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4338
4339 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4340 Builder.SetInsertPoint(EntryBlock);
4341 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4342
4343 // Buffer: global reduction buffer.
4344 Argument *BufferArg = LtGRFunc->getArg(0);
4345 // Idx: index of the buffer.
4346 Argument *IdxArg = LtGRFunc->getArg(1);
4347 // ReduceList: thread local Reduce list.
4348 Argument *ReduceListArg = LtGRFunc->getArg(2);
4349
4350 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4351 BufferArg->getName() + ".addr");
4352 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4353 IdxArg->getName() + ".addr");
4354 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4355 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4356 auto *RedListArrayTy =
4357 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4358
4359 // 1. Build a list of reduction variables.
4360 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4361 Value *LocalReduceList =
4362 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4363
4364 InsertPointTy AllocaIP(EntryBlock->begin());
4365
4366 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4367 BufferArgAlloca, Builder.getPtrTy(),
4368 BufferArgAlloca->getName() + ".ascast");
4369 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4370 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4371 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4372 ReduceListArgAlloca, Builder.getPtrTy(),
4373 ReduceListArgAlloca->getName() + ".ascast");
4374 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4375 LocalReduceList, Builder.getPtrTy(),
4376 LocalReduceList->getName() + ".ascast");
4377
4378 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4379 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4380 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4381
4382 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4383 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4384 Type *IndexTy = Builder.getIndexTy(
4385 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4386 for (auto En : enumerate(ReductionInfos)) {
4387 const ReductionInfo &RI = En.value();
4388
4389 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4390 RedListArrayTy, LocalReduceListAddrCast,
4391 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4392 Value *BufferVD =
4393 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4394 // Global = Buffer.VD[Idx];
4395 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4396 ReductionsBufferTy, BufferVD, 0, En.index());
4397
4398 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4399 // Get source descriptor from the reduce list argument
4400 Value *ReduceList =
4401 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4402 Value *SrcElementPtrPtr =
4403 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4404 {ConstantInt::get(IndexTy, 0),
4405 ConstantInt::get(IndexTy, En.index())});
4406 Value *SrcDescriptorAddr =
4407 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4408
4409 // Copy descriptor from source and update base_ptr to global buffer data
4410 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4411 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4412 if (!ByRefAlloc)
4413 return ByRefAlloc.takeError();
4414
4415 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4416 } else {
4417 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4418 }
4419 }
4420
4421 // Call reduce_function(GlobalReduceList, ReduceList)
4422 Value *ReduceList =
4423 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4424 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4425 ->addFnAttr(Attribute::NoUnwind);
4426 Builder.CreateRetVoid();
4427 return LtGRFunc;
4428}
4429
4430Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4431 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4432 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4433 IRBuilder<>::InsertPointGuard IPG(Builder);
4434 LLVMContext &Ctx = M.getContext();
4435 FunctionType *FuncTy = FunctionType::get(
4436 Builder.getVoidTy(),
4437 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4438 /* IsVarArg */ false);
4439 Function *GtLCFunc =
4441 "_omp_reduction_global_to_list_copy_func", &M);
4442 GtLCFunc->setAttributes(FuncAttrs);
4443 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4444 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4445 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4446
4447 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4448 Builder.SetInsertPoint(EntryBlock);
4449 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4450
4451 // Buffer: global reduction buffer.
4452 Argument *BufferArg = GtLCFunc->getArg(0);
4453 // Idx: index of the buffer.
4454 Argument *IdxArg = GtLCFunc->getArg(1);
4455 // ReduceList: thread local Reduce list.
4456 Argument *ReduceListArg = GtLCFunc->getArg(2);
4457
4458 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4459 BufferArg->getName() + ".addr");
4460 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4461 IdxArg->getName() + ".addr");
4462 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4463 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4464 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4465 BufferArgAlloca, Builder.getPtrTy(),
4466 BufferArgAlloca->getName() + ".ascast");
4467 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4468 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4469 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4470 ReduceListArgAlloca, Builder.getPtrTy(),
4471 ReduceListArgAlloca->getName() + ".ascast");
4472 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4473 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4474 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4475
4476 Value *LocalReduceList =
4477 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4478 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4479 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4480 Type *IndexTy = Builder.getIndexTy(
4481 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4482 for (auto En : enumerate(ReductionInfos)) {
4483 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4484 auto *RedListArrayTy =
4485 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4486 // Reduce element = LocalReduceList[i]
4487 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4488 RedListArrayTy, LocalReduceList,
4489 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4490 // elemptr = ((CopyType*)(elemptrptr)) + I
4491 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4492 // Global = Buffer.VD[Idx];
4493 Value *BufferVD =
4494 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4495 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4496 ReductionsBufferTy, BufferVD, 0, En.index());
4497
4498 switch (RI.EvaluationKind) {
4499 case EvalKind::Scalar: {
4500 Type *ElemType = RI.ElementType;
4501
4502 if (!IsByRef.empty() && IsByRef[En.index()]) {
4503 ElemType = RI.ByRefElementType;
4504 if (RI.DataPtrPtrGen) {
4505 InsertPointOrErrorTy GenResult =
4506 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4507
4508 if (!GenResult)
4509 return GenResult.takeError();
4510
4511 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4512 }
4513 }
4514
4515 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4516 Builder.CreateStore(TargetElement, ElemPtr);
4517 break;
4518 }
4519 case EvalKind::Complex: {
4520 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4521 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4522 Value *SrcReal = Builder.CreateLoad(
4523 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4524 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4525 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4526 Value *SrcImg = Builder.CreateLoad(
4527 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4528
4529 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4530 RI.ElementType, ElemPtr, 0, 0, ".realp");
4531 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4532 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4533 Builder.CreateStore(SrcReal, DestRealPtr);
4534 Builder.CreateStore(SrcImg, DestImgPtr);
4535 break;
4536 }
4537 case EvalKind::Aggregate: {
4538 Value *SizeVal =
4539 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4540 Builder.CreateMemCpy(
4541 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4542 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4543 SizeVal, false);
4544 break;
4545 }
4546 }
4547 }
4548
4549 Builder.CreateRetVoid();
4550 return GtLCFunc;
4551}
4552
4553Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4554 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4555 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4556 IRBuilder<>::InsertPointGuard IPG(Builder);
4557 LLVMContext &Ctx = M.getContext();
4558 auto *FuncTy = FunctionType::get(
4559 Builder.getVoidTy(),
4560 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4561 /* IsVarArg */ false);
4562 Function *GtLRFunc =
4564 "_omp_reduction_global_to_list_reduce_func", &M);
4565 GtLRFunc->setAttributes(FuncAttrs);
4566 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4567 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4568 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4569
4570 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4571 Builder.SetInsertPoint(EntryBlock);
4572 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4573
4574 // Buffer: global reduction buffer.
4575 Argument *BufferArg = GtLRFunc->getArg(0);
4576 // Idx: index of the buffer.
4577 Argument *IdxArg = GtLRFunc->getArg(1);
4578 // ReduceList: thread local Reduce list.
4579 Argument *ReduceListArg = GtLRFunc->getArg(2);
4580
4581 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4582 BufferArg->getName() + ".addr");
4583 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4584 IdxArg->getName() + ".addr");
4585 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4586 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4587 ArrayType *RedListArrayTy =
4588 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4589
4590 // 1. Build a list of reduction variables.
4591 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4592 Value *LocalReduceList =
4593 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4594
4595 InsertPointTy AllocaIP(EntryBlock->begin());
4596
4597 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4598 BufferArgAlloca, Builder.getPtrTy(),
4599 BufferArgAlloca->getName() + ".ascast");
4600 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4601 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4602 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4603 ReduceListArgAlloca, Builder.getPtrTy(),
4604 ReduceListArgAlloca->getName() + ".ascast");
4605 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4606 LocalReduceList, Builder.getPtrTy(),
4607 LocalReduceList->getName() + ".ascast");
4608
4609 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4610 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4611 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4612
4613 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4614 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4615 Type *IndexTy = Builder.getIndexTy(
4616 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4617 for (auto En : enumerate(ReductionInfos)) {
4618 const ReductionInfo &RI = En.value();
4619
4620 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4621 RedListArrayTy, ReductionList,
4622 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4623 // Global = Buffer.VD[Idx];
4624 Value *BufferVD =
4625 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4626 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4627 ReductionsBufferTy, BufferVD, 0, En.index());
4628
4629 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4630 // Get source descriptor from the reduce list
4631 Value *ReduceListVal =
4632 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4633 Value *SrcElementPtrPtr =
4634 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4635 {ConstantInt::get(IndexTy, 0),
4636 ConstantInt::get(IndexTy, En.index())});
4637 Value *SrcDescriptorAddr =
4638 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4639
4640 // Copy descriptor from source and update base_ptr to global buffer data
4641 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4642 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4643 if (!ByRefAlloc)
4644 return ByRefAlloc.takeError();
4645
4646 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4647 } else {
4648 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4649 }
4650 }
4651
4652 // Call reduce_function(ReduceList, GlobalReduceList)
4653 Value *ReduceList =
4654 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4655 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4656 ->addFnAttr(Attribute::NoUnwind);
4657 Builder.CreateRetVoid();
4658 return GtLRFunc;
4659}
4660
4661std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4662 std::string Suffix =
4663 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4664 return (Name + Suffix).str();
4665}
4666
4667Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4668 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4670 AttributeList FuncAttrs) {
4671 IRBuilder<>::InsertPointGuard IPG(Builder);
4672 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4673 {Builder.getPtrTy(), Builder.getPtrTy()},
4674 /* IsVarArg */ false);
4675 std::string Name = getReductionFuncName(ReducerName);
4676 Function *ReductionFunc =
4678 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4679 ReductionFunc->setAttributes(FuncAttrs);
4680 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4681 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4682 BasicBlock *EntryBB =
4683 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4684 Builder.SetInsertPoint(EntryBB);
4685 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4686
4687 // Need to alloca memory here and deal with the pointers before getting
4688 // LHS/RHS pointers out
4689 Value *LHSArrayPtr = nullptr;
4690 Value *RHSArrayPtr = nullptr;
4691 Argument *Arg0 = ReductionFunc->getArg(0);
4692 Argument *Arg1 = ReductionFunc->getArg(1);
4693 Type *Arg0Type = Arg0->getType();
4694 Type *Arg1Type = Arg1->getType();
4695
4696 Value *LHSAlloca =
4697 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4698 Value *RHSAlloca =
4699 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4700 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4701 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4702 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4703 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4704 Builder.CreateStore(Arg0, LHSAddrCast);
4705 Builder.CreateStore(Arg1, RHSAddrCast);
4706 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4707 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4708
4709 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4710 Type *IndexTy = Builder.getIndexTy(
4711 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4712 SmallVector<Value *> LHSPtrs, RHSPtrs;
4713 for (auto En : enumerate(ReductionInfos)) {
4714 const ReductionInfo &RI = En.value();
4715 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4716 RedArrayTy, RHSArrayPtr,
4717 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4718 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4719 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4720 RHSI8Ptr, RI.PrivateVariable->getType(),
4721 RHSI8Ptr->getName() + ".ascast");
4722
4723 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4724 RedArrayTy, LHSArrayPtr,
4725 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4726 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4727 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4728 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4729
4731 LHSPtrs.emplace_back(LHSPtr);
4732 RHSPtrs.emplace_back(RHSPtr);
4733 } else {
4734 Value *LHS = LHSPtr;
4735 Value *RHS = RHSPtr;
4736
4737 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4738 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4739 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4740 }
4741
4742 Value *Reduced;
4743 InsertPointOrErrorTy AfterIP =
4744 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4745 if (!AfterIP)
4746 return AfterIP.takeError();
4747 if (!Builder.GetInsertBlock())
4748 return ReductionFunc;
4749
4750 Builder.restoreIP(*AfterIP);
4751
4752 if (!IsByRef.empty() && !IsByRef[En.index()])
4753 Builder.CreateStore(Reduced, LHSPtr);
4754 }
4755 }
4756
4758 for (auto En : enumerate(ReductionInfos)) {
4759 unsigned Index = En.index();
4760 const ReductionInfo &RI = En.value();
4761 Value *LHSFixupPtr, *RHSFixupPtr;
4762 Builder.restoreIP(RI.ReductionGenClang(
4763 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4764
4765 // Fix the CallBack code genereated to use the correct Values for the LHS
4766 // and RHS
4767 LHSFixupPtr->replaceUsesWithIf(
4768 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4769 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4770 ReductionFunc;
4771 });
4772 RHSFixupPtr->replaceUsesWithIf(
4773 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4774 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4775 ReductionFunc;
4776 });
4777 }
4778
4779 Builder.CreateRetVoid();
4780 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4781 // to the entry block (this is dones for higher opt levels by later passes in
4782 // the pipeline). This has caused issues because non-entry `alloca`s force the
4783 // function to use dynamic stack allocations and we might run out of scratch
4784 // memory.
4785 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4786
4787 return ReductionFunc;
4788}
4789
4790static void
4792 bool IsGPU) {
4793 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4794 (void)RI;
4795 assert(RI.Variable && "expected non-null variable");
4796 assert(RI.PrivateVariable && "expected non-null private variable");
4797 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4798 "expected non-null reduction generator callback");
4799 if (!IsGPU) {
4800 assert(
4801 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4802 "expected variables and their private equivalents to have the same "
4803 "type");
4804 }
4805 assert(RI.Variable->getType()->isPointerTy() &&
4806 "expected variables to be pointers");
4807 }
4808}
4809
4810// The atomic cross-team reduction fast path applies when every reduction in the
4811// set can be represented by an atomicrmw. Clang only populates it for scalar
4812// reductions with a supported atomic operator.
4815 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4816 return static_cast<bool>(RI.AtomicReductionGen);
4817 });
4818}
4819
4821 const LocationDescription &Loc, InsertPointTy AllocaIP,
4822 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4823 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4824 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4825 Value *SrcLocInfo) {
4826 if (!updateToLocation(Loc))
4827 return InsertPointTy();
4828 Builder.restoreIP(CodeGenIP);
4829 Builder.SetCurrentDebugLocation(Loc.DL);
4830 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4831 LLVMContext &Ctx = M.getContext();
4832
4833 // Source location for the ident struct
4834 if (!SrcLocInfo) {
4835 uint32_t SrcLocStrSize;
4836 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4837 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4838 }
4839
4840 if (ReductionInfos.size() == 0)
4841 return Builder.saveIP();
4842
4843 BasicBlock *ContinuationBlock = nullptr;
4845 // Copied code from createReductions
4846 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
4847 ContinuationBlock = InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
4848 InsertBlock->getTerminator()->eraseFromParent();
4849 Builder.SetInsertPoint(InsertBlock->end());
4850 }
4851
4852 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4853 AttributeList FuncAttrs;
4854 AttrBuilder AttrBldr(Ctx);
4855 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4856 AttrBldr.addAttribute(Attr);
4857 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4858 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4859
4860 Expected<Function *> ReductionResult = createReductionFunction(
4861 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4862 ReductionGenCBKind, FuncAttrs);
4863 if (!ReductionResult)
4864 return ReductionResult.takeError();
4865 Function *ReductionFunc = *ReductionResult;
4866
4867 // Set the grid value in the config needed for lowering later on
4868 if (GridValue.has_value())
4869 Config.setGridValue(GridValue.value());
4870 else
4871 Config.setGridValue(getGridValue(T, ReductionFunc));
4872
4873 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4874 // RedList, shuffle_reduce_func, interwarp_copy_func);
4875 // or
4876 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4877 Value *Res;
4878
4879 // 1. Build a list of reduction variables.
4880 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4881 auto Size = ReductionInfos.size();
4882 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4883 Type *FuncPtrTy =
4884 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4885 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4886 Value *ReductionList;
4887 {
4889 Builder.restoreIP(AllocaIP);
4890 Value *ReductionListAlloca =
4891 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4892 ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4893 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4894 }
4895 Type *IndexTy = Builder.getIndexTy(
4896 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4897 for (auto En : enumerate(ReductionInfos)) {
4898 const ReductionInfo &RI = En.value();
4899 Value *ElemPtr = Builder.CreateInBoundsGEP(
4900 RedArrayTy, ReductionList,
4901 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4902
4903 Value *PrivateVar = RI.PrivateVariable;
4904 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4905 if (IsByRefElem)
4906 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4907
4908 Value *CastElem =
4909 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4910 Builder.CreateStore(CastElem, ElemPtr);
4911 }
4912 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4913 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4914
4915 if (!SarFunc)
4916 return SarFunc.takeError();
4917
4918 Expected<Function *> CopyResult =
4919 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4920 if (!CopyResult)
4921 return CopyResult.takeError();
4922 Function *WcFunc = *CopyResult;
4923
4924 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4925
4926 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4927 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4928 // not currently use it. It is computed here conservatively as max(element
4929 // sizes) * N rather than the exact sum, which over-calculates the size for
4930 // mixed reduction types but is harmless given the argument is unused.
4931 // TODO: Consider dropping this computation if the runtime API is ever revised
4932 // to remove the unused parameter.
4933 unsigned MaxDataSize = 0;
4934 SmallVector<Type *> ReductionTypeArgs;
4935 for (auto En : enumerate(ReductionInfos)) {
4936 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4937 // the actual data size stored in the global reduction buffer, consistent
4938 // with the ReductionsBufferTy struct used for GEP offsets below.
4939 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4940 ? En.value().ByRefElementType
4941 : En.value().ElementType;
4942 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4943 if (Size > MaxDataSize)
4944 MaxDataSize = Size;
4945 ReductionTypeArgs.emplace_back(RedTypeArg);
4946 }
4947 Value *ReductionDataSize =
4948 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4949
4950 // Helper function to copy thread-local data back to the original reduction
4951 // list.
4952 Function *CopyScratchToListFunc = nullptr;
4953 // Thread-local storage for the reduction variables.
4954 Value *ScratchForCopyBack = nullptr;
4955 // RL pointer to which the final value from the per-thread scratch should be
4956 // copied back. (Basically RL, appropriately casted if necessary.)
4957 Value *RLForCopyBack = RL;
4958
4959 bool IsAtomicReduction =
4960 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4961
4962 if (!IsTeamsReduction) {
4963 Value *SarFuncCast =
4964 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4965 Value *WcFuncCast =
4966 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4967 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4968 WcFuncCast};
4970 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4971 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4972 } else if (IsAtomicReduction) {
4973 // Atomic cross-team reduction fast path: determine the team's main thread
4974 // that is later to fold its value atomically into the mapped variable.
4975 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4976 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4977 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4978 } else {
4979 StructType *ReductionsBufferTy = StructType::create(
4980 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4981
4982 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4983 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4984 if (!LtGCFunc)
4985 return LtGCFunc.takeError();
4986
4987 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4988 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4989 if (!GtLCFunc)
4990 return GtLCFunc.takeError();
4991
4992 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4993 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4994 if (!GtLRFunc)
4995 return GtLRFunc.takeError();
4996
4997 // The runtime's cross-team final aggregate uses the storage pointed at by
4998 // its reduce-list argument as per-thread scratch. When the surrounding
4999 // kernel is already in SPMD execution mode, clang emitted each reduction
5000 // private as a per-thread `alloca addrspace(5)`, so the original red_list
5001 // (RL) is already per-thread and nothing else is needed.
5002 //
5003 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
5004 // Generic-mode globalization put the reduction private into team-shared
5005 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
5006 // point all threads of the last team would race on the shared LDS slot.
5007 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
5008 // value in, and hand the per-thread RL to the runtime instead. The writer
5009 // thread copies the final value from that per-thread scratch back to RL
5010 // before running the existing combine path below.
5011
5012 // Thread-local RL (might need localization below before being passed to the
5013 // runtime).
5014 Value *RuntimeRL = RL;
5015
5016 if (!IsSPMD) {
5017 Value *PerThreadScratch;
5018
5019 {
5021 Builder.restoreIP(AllocaIP);
5022 // Allocate thread-local buffer for the reduction variables.
5023 Value *PerThreadScratchAlloca =
5024 Builder.CreateAlloca(ReductionsBufferTy, /*ArraySize=*/nullptr,
5025 ".omp.reduction.scratch");
5026 PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5027 PerThreadScratchAlloca, PtrTy,
5028 PerThreadScratchAlloca->getName() + ".ascast");
5029 // Allocate thread-local buffer for the pointers to the reduction
5030 // variables.
5031 Value *PerThreadRedListAlloca =
5032 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5033 ".omp.reduction.per_thread_red_list");
5034 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5035 PerThreadRedListAlloca, PtrTy,
5036 PerThreadRedListAlloca->getName() + ".ascast");
5037 }
5038
5039 // Iterate over the reduction variables and copy the team-local value to
5040 // the thread-local buffer.
5041 for (auto En : enumerate(ReductionInfos)) {
5042 const ReductionInfo &RI = En.value();
5043 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5044
5045 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5046 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5047 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5048 0, En.index());
5049
5050 Value *RuntimeListEntry = FieldPtr;
5051 if (IsByRefElem && RI.DataPtrPtrGen) {
5052 Value *SrcDescriptor =
5053 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5054 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5055 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5056 if (!Descriptor)
5057 return Descriptor.takeError();
5058 RuntimeListEntry = *Descriptor;
5059 }
5060 Builder.CreateStore(RuntimeListEntry, Slot);
5061 }
5062 // The copy helpers were emitted with default-AS (AS 0) pointer params
5063 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5064 // but PerThreadScratch and RL live in the target's default AS, which
5065 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5066 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5067 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5068 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5069 PerThreadScratch, CopyArg0Ty);
5070 RLForCopyBack =
5071 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5072 // Use index 0 because there is no array of target values to index into,
5073 // there is only one thread-local memory slot.
5074 // restoreIP above left a stale/empty debug location; this inlinable call
5075 // to a debug-info-bearing helper needs one or the verifier rejects the
5076 // module ("!dbg attachment points at wrong subprogram") after inlining.
5077 Builder.SetCurrentDebugLocation(Loc.DL);
5078 Builder.CreateCall(
5079 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5080 CopyScratchToListFunc = *GtLCFunc;
5081 }
5082
5083 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5084 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5085
5086 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5087 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5088 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5089 }
5090
5091 // 5. Build if (res == 1)
5092 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5093 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5094 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5095 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5096
5097 // 6. Build then branch: where we have reduced values in the master
5098 // thread in each team.
5099 // __kmpc_end_reduce{_nowait}(<gtid>);
5100 // break;
5101 emitBlock(ThenBB, CurFunc);
5102
5103 // Copy the writer thread's per-thread scratch result back into the original
5104 // red-list storage before the existing combine path reads RI.PrivateVariable.
5105 // Set a debug location: this inlinable call to a debug-info-bearing helper
5106 // needs one or the verifier rejects the module after inlining.
5107 if (ScratchForCopyBack) {
5108 Builder.SetCurrentDebugLocation(Loc.DL);
5109 Builder.CreateCall(
5110 CopyScratchToListFunc,
5111 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5112 }
5113
5114 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5115 for (auto En : enumerate(ReductionInfos)) {
5116 const ReductionInfo &RI = En.value();
5117
5118 // Atomic cross-team fast path: each team's main thread folds its
5119 // team-reduced value directly into the mapped reduction variable with a
5120 // single atomicrmw.
5121 if (IsAtomicReduction) {
5123 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5124 if (!AfterIP)
5125 return AfterIP.takeError();
5126 Builder.restoreIP(*AfterIP);
5127 continue;
5128 }
5129
5131 Value *RedValue = RI.Variable;
5132
5133 Value *RHS =
5134 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5135
5137 Value *LHSPtr, *RHSPtr;
5138 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5139 &LHSPtr, &RHSPtr, CurFunc));
5140
5141 // Fix the CallBack code genereated to use the correct Values for the LHS
5142 // and RHS. Cast to match types before replacing (necessary to handle
5143 // different address spaces).
5144 if (LHSPtr->getType() != RedValue->getType())
5145 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5146 RedValue, LHSPtr->getType());
5147 if (RHSPtr->getType() != RHS->getType())
5148 RHS =
5149 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5150
5151 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5152 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5153 ReductionFunc;
5154 });
5155 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5156 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5157 ReductionFunc;
5158 });
5159 } else {
5160 if (IsByRef.empty() || !IsByRef[En.index()]) {
5161 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5162 "red.value." + Twine(En.index()));
5163 }
5164 Value *PrivateRedValue = Builder.CreateLoad(
5165 ValueType, RHS, "red.private.value" + Twine(En.index()));
5166 Value *Reduced;
5167 InsertPointOrErrorTy AfterIP =
5168 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5169 if (!AfterIP)
5170 return AfterIP.takeError();
5171 Builder.restoreIP(*AfterIP);
5172
5173 if (!IsByRef.empty() && !IsByRef[En.index()])
5174 Builder.CreateStore(Reduced, RI.Variable);
5175 }
5176 }
5177 emitBlock(ExitBB, CurFunc);
5178 if (ContinuationBlock) {
5179 Builder.CreateBr(ContinuationBlock);
5180 Builder.SetInsertPoint(ContinuationBlock);
5181 }
5182 Config.setEmitLLVMUsed();
5183
5184 return Builder.saveIP();
5185}
5186
5188 Type *VoidTy = Type::getVoidTy(M.getContext());
5189 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5190 auto *FuncTy =
5191 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5193 ".omp.reduction.func", &M);
5194}
5195
5197 Function *ReductionFunc,
5199 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5200 IRBuilder<>::InsertPointGuard IPG(Builder);
5201 Module *Module = ReductionFunc->getParent();
5202 BasicBlock *ReductionFuncBlock =
5203 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5204 Builder.SetInsertPoint(ReductionFuncBlock);
5205 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5206 Value *LHSArrayPtr = nullptr;
5207 Value *RHSArrayPtr = nullptr;
5208 if (IsGPU) {
5209 // Need to alloca memory here and deal with the pointers before getting
5210 // LHS/RHS pointers out
5211 //
5212 Argument *Arg0 = ReductionFunc->getArg(0);
5213 Argument *Arg1 = ReductionFunc->getArg(1);
5214 Type *Arg0Type = Arg0->getType();
5215 Type *Arg1Type = Arg1->getType();
5216
5217 Value *LHSAlloca =
5218 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5219 Value *RHSAlloca =
5220 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5221 Value *LHSAddrCast =
5222 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5223 Value *RHSAddrCast =
5224 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5225 Builder.CreateStore(Arg0, LHSAddrCast);
5226 Builder.CreateStore(Arg1, RHSAddrCast);
5227 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5228 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5229 } else {
5230 LHSArrayPtr = ReductionFunc->getArg(0);
5231 RHSArrayPtr = ReductionFunc->getArg(1);
5232 }
5233
5234 unsigned NumReductions = ReductionInfos.size();
5235 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5236
5237 for (auto En : enumerate(ReductionInfos)) {
5238 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5239 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5240 RedArrayTy, LHSArrayPtr, 0, En.index());
5241 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5242 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5243 LHSI8Ptr, RI.Variable->getType());
5244 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5245 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5246 RedArrayTy, RHSArrayPtr, 0, En.index());
5247 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5248 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5249 RHSI8Ptr, RI.PrivateVariable->getType());
5250 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5251 Value *Reduced;
5253 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5254 if (!AfterIP)
5255 return AfterIP.takeError();
5256
5257 Builder.restoreIP(*AfterIP);
5258 // TODO: Consider flagging an error.
5259 if (!Builder.GetInsertBlock())
5260 return Error::success();
5261
5262 // store is inside of the reduction region when using by-ref
5263 if (!IsByRef[En.index()])
5264 Builder.CreateStore(Reduced, LHSPtr);
5265 }
5266 Builder.CreateRetVoid();
5267 return Error::success();
5268}
5269
5271 const LocationDescription &Loc, InsertPointTy AllocaIP,
5272 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5273 bool IsNoWait, bool IsTeamsReduction) {
5274 assert(ReductionInfos.size() == IsByRef.size());
5275 if (Config.isGPU())
5276 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5277 IsByRef, IsNoWait, IsTeamsReduction);
5278
5279 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5280
5281 if (!updateToLocation(Loc))
5282 return InsertPointTy();
5283
5284 if (ReductionInfos.size() == 0)
5285 return Builder.saveIP();
5286
5287 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
5288 BasicBlock *ContinuationBlock =
5289 InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
5290 InsertBlock->getTerminator()->eraseFromParent();
5291
5292 // Create and populate array of type-erased pointers to private reduction
5293 // values.
5294 unsigned NumReductions = ReductionInfos.size();
5295 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5296 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getTerminator());
5297 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5298
5299 Builder.SetInsertPoint(InsertBlock->end());
5300 // Emitting the alloca moved the insertion point into the alloca block and
5301 // can clear the debug loc. Restore back to Loc.DL.
5302 Builder.SetCurrentDebugLocation(Loc.DL);
5303
5304 for (auto En : enumerate(ReductionInfos)) {
5305 unsigned Index = En.index();
5306 const ReductionInfo &RI = En.value();
5307 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5308 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5309 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5310 }
5311
5312 // Emit a call to the runtime function that orchestrates the reduction.
5313 // Declare the reduction function in the process.
5314 Type *IndexTy = Builder.getIndexTy(
5315 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5316 Function *Func = Builder.GetInsertBlock()->getParent();
5317 Module *Module = Func->getParent();
5318 uint32_t SrcLocStrSize;
5319 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5320 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5321 return RI.AtomicReductionGen;
5322 });
5323 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5324 CanGenerateAtomic
5325 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5326 : IdentFlag(0));
5327 Value *ThreadId = getOrCreateThreadID(Ident);
5328 Constant *NumVariables = Builder.getInt32(NumReductions);
5329 const DataLayout &DL = Module->getDataLayout();
5330 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5331 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5332 Function *ReductionFunc = getFreshReductionFunc(*Module);
5333 Value *Lock = getOMPCriticalRegionLock(".reduction");
5335 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5336 : RuntimeFunction::OMPRTL___kmpc_reduce);
5337 CallInst *ReduceCall =
5338 createRuntimeFunctionCall(ReduceFunc,
5339 {Ident, ThreadId, NumVariables, RedArraySize,
5340 RedArray, ReductionFunc, Lock},
5341 "reduce");
5342
5343 // Create final reduction entry blocks for the atomic and non-atomic case.
5344 // Emit IR that dispatches control flow to one of the blocks based on the
5345 // reduction supporting the atomic mode.
5346 BasicBlock *NonAtomicRedBlock =
5347 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5348 BasicBlock *AtomicRedBlock =
5349 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5350 SwitchInst *Switch =
5351 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5352 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5353 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5354
5355 // Populate the non-atomic reduction using the elementwise reduction function.
5356 // This loads the elements from the global and private variables and reduces
5357 // them before storing back the result to the global variable.
5358 Builder.SetInsertPoint(NonAtomicRedBlock);
5359 for (auto En : enumerate(ReductionInfos)) {
5360 const ReductionInfo &RI = En.value();
5362 // We have one less load for by-ref case because that load is now inside of
5363 // the reduction region
5364 Value *RedValue = RI.Variable;
5365 if (!IsByRef[En.index()]) {
5366 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5367 "red.value." + Twine(En.index()));
5368 }
5369 Value *PrivateRedValue =
5370 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5371 "red.private.value." + Twine(En.index()));
5372 Value *Reduced;
5373 InsertPointOrErrorTy AfterIP =
5374 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5375 if (!AfterIP)
5376 return AfterIP.takeError();
5377 Builder.restoreIP(*AfterIP);
5378
5379 if (!Builder.GetInsertBlock())
5380 return InsertPointTy();
5381 // for by-ref case, the load is inside of the reduction region
5382 if (!IsByRef[En.index()])
5383 Builder.CreateStore(Reduced, RI.Variable);
5384 }
5385 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5386 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5387 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5388 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5389 Builder.CreateBr(ContinuationBlock);
5390
5391 // Populate the atomic reduction using the atomic elementwise reduction
5392 // function. There are no loads/stores here because they will be happening
5393 // inside the atomic elementwise reduction.
5394 Builder.SetInsertPoint(AtomicRedBlock);
5395 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5396 for (const ReductionInfo &RI : ReductionInfos) {
5398 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5399 if (!AfterIP)
5400 return AfterIP.takeError();
5401 Builder.restoreIP(*AfterIP);
5402 if (!Builder.GetInsertBlock())
5403 return InsertPointTy();
5404 }
5405 Builder.CreateBr(ContinuationBlock);
5406 } else {
5407 Builder.CreateUnreachable();
5408 }
5409
5410 // Populate the outlined reduction function using the elementwise reduction
5411 // function. Partial values are extracted from the type-erased array of
5412 // pointers to private variables.
5413 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5414 IsByRef, /*isGPU=*/false);
5415 if (Err)
5416 return Err;
5417
5418 if (!Builder.GetInsertBlock())
5419 return InsertPointTy();
5420
5421 Builder.SetInsertPoint(ContinuationBlock);
5422 return Builder.saveIP();
5423}
5424
5427 BodyGenCallbackTy BodyGenCB,
5428 FinalizeCallbackTy FiniCB) {
5429 if (!updateToLocation(Loc))
5430 return Loc.IP;
5431
5432 Directive OMPD = Directive::OMPD_master;
5433 uint32_t SrcLocStrSize;
5434 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5435 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5436 Value *ThreadId = getOrCreateThreadID(Ident);
5437 Value *Args[] = {Ident, ThreadId};
5438
5439 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5440 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5441
5442 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5443 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5444
5445 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5446 /*Conditional*/ true, /*hasFinalize*/ true);
5447}
5448
5451 BodyGenCallbackTy BodyGenCB,
5452 FinalizeCallbackTy FiniCB, Value *Filter) {
5454 if (!updateToLocation(Loc))
5455 return Loc.IP;
5456
5457 Directive OMPD = Directive::OMPD_masked;
5458 uint32_t SrcLocStrSize;
5459 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5460 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5461 Value *ThreadId = getOrCreateThreadID(Ident);
5462 Value *Args[] = {Ident, ThreadId, Filter};
5463 Value *ArgsEnd[] = {Ident, ThreadId};
5464
5465 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5466 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5467
5468 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5469 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5470
5471 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5472 /*Conditional*/ true, /*hasFinalize*/ true);
5473}
5474
5476 llvm::FunctionCallee Callee,
5478 const llvm::Twine &Name) {
5479 llvm::CallInst *Call = Builder.CreateCall(
5480 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5481 Call->setDoesNotThrow();
5482 return Call;
5483}
5484
5485// Expects input basic block is dominated by BeforeScanBB.
5486// Once Scan directive is encountered, the code after scan directive should be
5487// dominated by AfterScanBB. Scan directive splits the code sequence to
5488// scan and input phase. Based on whether inclusive or exclusive
5489// clause is used in the scan directive and whether input loop or scan loop
5490// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5491// input loop and second is the scan loop. The code generated handles only
5492// inclusive scans now.
5494 const LocationDescription &Loc, InsertPointTy AllocaIP,
5495 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5496 bool IsInclusive, ScanInfo *ScanRedInfo) {
5497 if (ScanRedInfo->OMPFirstScanLoop) {
5498 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5499 ScanVarsType, ScanRedInfo);
5500 if (Err)
5501 return Err;
5502 }
5503 if (!updateToLocation(Loc))
5504 return Loc.IP;
5505
5506 llvm::Value *IV = ScanRedInfo->IV;
5507
5508 if (ScanRedInfo->OMPFirstScanLoop) {
5509 // Emit buffer[i] = red; at the end of the input phase.
5510 for (size_t i = 0; i < ScanVars.size(); i++) {
5511 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5512 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5513 Type *DestTy = ScanVarsType[i];
5514 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5515 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5516
5517 Builder.CreateStore(Src, Val);
5518 }
5519 }
5520 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5521 emitBlock(ScanRedInfo->OMPScanDispatch,
5522 Builder.GetInsertBlock()->getParent());
5523
5524 if (!ScanRedInfo->OMPFirstScanLoop) {
5525 IV = ScanRedInfo->IV;
5526 // Emit red = buffer[i]; at the entrance to the scan phase.
5527 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5528 for (size_t i = 0; i < ScanVars.size(); i++) {
5529 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5530 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5531 Type *DestTy = ScanVarsType[i];
5532 Value *SrcPtr =
5533 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5534 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5535 Builder.CreateStore(Src, ScanVars[i]);
5536 }
5537 }
5538
5539 // TODO: Update it to CreateBr and remove dead blocks
5540 llvm::Value *CmpI = Builder.getInt1(true);
5541 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5542 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5543 ScanRedInfo->OMPAfterScanBlock);
5544 } else {
5545 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5546 ScanRedInfo->OMPBeforeScanBlock);
5547 }
5548 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5549 Builder.GetInsertBlock()->getParent());
5550 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5551 return Builder.saveIP();
5552}
5553
5554Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5555 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5556 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5557
5558 Builder.restoreIP(AllocaIP);
5559 // Create the shared pointer at alloca IP.
5560 for (size_t i = 0; i < ScanVars.size(); i++) {
5561 llvm::Value *BuffPtr =
5562 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5563 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5564 }
5565
5566 // Allocate temporary buffer by master thread
5567 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5568 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5569 Builder.restoreIP(CodeGenIP);
5570 Value *AllocSpan =
5571 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5572 for (size_t i = 0; i < ScanVars.size(); i++) {
5573 Type *IntPtrTy = Builder.getInt32Ty();
5574 Value *Allocsize = Builder.CreateTypeSize(
5575 IntPtrTy, M.getDataLayout().getTypeAllocSize(ScanVarsType[i]));
5576 Value *Buff =
5577 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5578 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5579 }
5580 return Error::success();
5581 };
5582 // TODO: Perform finalization actions for variables. This has to be
5583 // called for variables which have destructors/finalizers.
5584 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5585
5586 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5587 llvm::Value *FilterVal = Builder.getInt32(0);
5589 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5590
5591 if (!AfterIP)
5592 return AfterIP.takeError();
5593 Builder.restoreIP(*AfterIP);
5594 BasicBlock *InputBB = Builder.GetInsertBlock();
5595 if (InputBB->hasTerminator())
5596 Builder.SetInsertPoint(InputBB->getTerminator());
5597 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5598 if (!AfterIP)
5599 return AfterIP.takeError();
5600 Builder.restoreIP(*AfterIP);
5601
5602 return Error::success();
5603}
5604
5605Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5606 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5607 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5608 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5609 Builder.restoreIP(CodeGenIP);
5610 for (ReductionInfo RedInfo : ReductionInfos) {
5611 Value *PrivateVar = RedInfo.PrivateVariable;
5612 Value *OrigVar = RedInfo.Variable;
5613 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5614 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5615
5616 Type *SrcTy = RedInfo.ElementType;
5617 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5618 "arrayOffset");
5619 Value *Src = Builder.CreateLoad(SrcTy, Val);
5620
5621 Builder.CreateStore(Src, OrigVar);
5622 Builder.CreateFree(Buff);
5623 }
5624 return Error::success();
5625 };
5626 // TODO: Perform finalization actions for variables. This has to be
5627 // called for variables which have destructors/finalizers.
5628 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5629
5630 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5631 Builder.SetInsertPoint(TI);
5632 else
5633 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5634
5635 llvm::Value *FilterVal = Builder.getInt32(0);
5637 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5638
5639 if (!AfterIP)
5640 return AfterIP.takeError();
5641 Builder.restoreIP(*AfterIP);
5642 BasicBlock *InputBB = Builder.GetInsertBlock();
5643 if (InputBB->hasTerminator())
5644 Builder.SetInsertPoint(InputBB->getTerminator());
5645 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5646 if (!AfterIP)
5647 return AfterIP.takeError();
5648 Builder.restoreIP(*AfterIP);
5649 return Error::success();
5650}
5651
5653 const LocationDescription &Loc,
5655 ScanInfo *ScanRedInfo) {
5656
5657 if (!updateToLocation(Loc))
5658 return Loc.IP;
5659 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5660 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5661 Builder.restoreIP(CodeGenIP);
5662 Function *CurFn = Builder.GetInsertBlock()->getParent();
5663 // for (int k = 0; k <= ceil(log2(n)); ++k)
5664 llvm::BasicBlock *LoopBB =
5665 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5666 llvm::BasicBlock *ExitBB =
5667 splitBB(Builder, false, "omp.outer.log.scan.exit");
5669 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::log2,
5670 Builder.getDoubleTy());
5671 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5672 llvm::Value *Arg =
5673 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5674 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5676 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::ceil,
5677 Builder.getDoubleTy());
5678 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5679 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5680 llvm::Value *NMin1 = Builder.CreateNUWSub(
5681 ScanRedInfo->Span,
5682 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5683 Builder.SetInsertPoint(InputBB);
5684 Builder.CreateBr(LoopBB);
5685 emitBlock(LoopBB, CurFn);
5686 Builder.SetInsertPoint(LoopBB);
5687
5688 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5689 // size pow2k = 1;
5690 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5691 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5692 InputBB);
5693 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5694 InputBB);
5695 // for (size i = n - 1; i >= 2 ^ k; --i)
5696 // tmp[i] op= tmp[i-pow2k];
5697 llvm::BasicBlock *InnerLoopBB =
5698 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5699 llvm::BasicBlock *InnerExitBB =
5700 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5701 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5702 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5703 emitBlock(InnerLoopBB, CurFn);
5704 Builder.SetInsertPoint(InnerLoopBB);
5705 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5706 IVal->addIncoming(NMin1, LoopBB);
5707 for (ReductionInfo RedInfo : ReductionInfos) {
5708 Value *ReductionVal = RedInfo.PrivateVariable;
5709 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5710 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5711 Type *DestTy = RedInfo.ElementType;
5712 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5713 Value *LHSPtr =
5714 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5715 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5716 Value *RHSPtr =
5717 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5718 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5719 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5720 llvm::Value *Result;
5721 InsertPointOrErrorTy AfterIP =
5722 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5723 if (!AfterIP)
5724 return AfterIP.takeError();
5725 Builder.CreateStore(Result, LHSPtr);
5726 }
5727 llvm::Value *NextIVal = Builder.CreateNUWSub(
5728 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5729 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5730 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5731 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5732 emitBlock(InnerExitBB, CurFn);
5733 llvm::Value *Next = Builder.CreateNUWAdd(
5734 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5735 Counter->addIncoming(Next, Builder.GetInsertBlock());
5736 // pow2k <<= 1;
5737 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5738 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5739 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5740 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5741 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5742 return Error::success();
5743 };
5744
5745 // TODO: Perform finalization actions for variables. This has to be
5746 // called for variables which have destructors/finalizers.
5747 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5748
5749 llvm::Value *FilterVal = Builder.getInt32(0);
5751 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5752
5753 if (!AfterIP)
5754 return AfterIP.takeError();
5755 Builder.restoreIP(*AfterIP);
5756 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5757
5758 if (!AfterIP)
5759 return AfterIP.takeError();
5760 Builder.restoreIP(*AfterIP);
5761 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5762 if (Err)
5763 return Err;
5764
5765 return AfterIP;
5766}
5767
5768Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5769 llvm::function_ref<Error()> InputLoopGen,
5770 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5771 ScanInfo *ScanRedInfo) {
5772
5773 {
5774 // Emit loop with input phase:
5775 // for (i: 0..<num_iters>) {
5776 // <input phase>;
5777 // buffer[i] = red;
5778 // }
5779 ScanRedInfo->OMPFirstScanLoop = true;
5780 Error Err = InputLoopGen();
5781 if (Err)
5782 return Err;
5783 }
5784 {
5785 // Emit loop with scan phase:
5786 // for (i: 0..<num_iters>) {
5787 // red = buffer[i];
5788 // <scan phase>;
5789 // }
5790 ScanRedInfo->OMPFirstScanLoop = false;
5791 Error Err = ScanLoopGen(Builder);
5792 if (Err)
5793 return Err;
5794 }
5795 return Error::success();
5796}
5797
5798void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5799 Function *Fun = Builder.GetInsertBlock()->getParent();
5800 ScanRedInfo->OMPScanDispatch =
5801 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5802 ScanRedInfo->OMPAfterScanBlock =
5803 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5804 ScanRedInfo->OMPBeforeScanBlock =
5805 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5806 ScanRedInfo->OMPScanLoopExit =
5807 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5808}
5810 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5811 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5812 Module *M = F->getParent();
5813 LLVMContext &Ctx = M->getContext();
5814 Type *IndVarTy = TripCount->getType();
5815
5816 // Create the basic block structure.
5817 BasicBlock *Preheader =
5818 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5819 BasicBlock *Header =
5820 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5821 BasicBlock *Cond =
5822 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5823 BasicBlock *Body =
5824 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5825 BasicBlock *Latch =
5826 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5827 BasicBlock *Exit =
5828 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5829 BasicBlock *After =
5830 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5831
5832 // Use specified DebugLoc for new instructions.
5833 Builder.SetCurrentDebugLocation(DL);
5834
5835 Builder.SetInsertPoint(Preheader);
5836 Builder.CreateBr(Header);
5837
5838 Builder.SetInsertPoint(Header);
5839 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5840 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5841 Builder.CreateBr(Cond);
5842
5843 Builder.SetInsertPoint(Cond);
5844 Value *Cmp =
5845 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5846 Builder.CreateCondBr(Cmp, Body, Exit);
5847
5848 Builder.SetInsertPoint(Body);
5849 Builder.CreateBr(Latch);
5850
5851 Builder.SetInsertPoint(Latch);
5852 // Decide whether the induction variable increment can carry nsw.
5853 //
5854 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5855 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5856 // for valid programs 0 <= count <= INT_MAX always holds.
5857 //
5858 // Collapsed loops: the trip count is a product that can overflow i32 even for
5859 // a conforming program, so nsw is kept only when the product is a constant
5860 // that provably fits, dropped otherwise.
5861 bool HasNSW = Config.hasNoSignedWrap();
5862 if (HasNSW) {
5863 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5864 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5866 if (CI->getValue().ugt(SignedMax))
5867 HasNSW = false;
5868 } else if (IsCollapsed) {
5869 HasNSW = false;
5870 }
5871 }
5872 Value *Next =
5873 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5874 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5875 Builder.CreateBr(Header);
5876 IndVarPHI->addIncoming(Next, Latch);
5877
5878 Builder.SetInsertPoint(Exit);
5879 Builder.CreateBr(After);
5880
5881 // Remember and return the canonical control flow.
5882 LoopInfos.emplace_front();
5883 CanonicalLoopInfo *CL = &LoopInfos.front();
5884
5885 CL->Header = Header;
5886 CL->Cond = Cond;
5887 CL->Latch = Latch;
5888 CL->Exit = Exit;
5889
5890#ifndef NDEBUG
5891 CL->assertOK();
5892#endif
5893 return CL;
5894}
5895
5898 LoopBodyGenCallbackTy BodyGenCB,
5899 Value *TripCount, const Twine &Name) {
5900 BasicBlock *BB = Loc.IP.getNodeParent();
5901 BasicBlock *NextBB = BB->getNextNode();
5902
5903 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5904 NextBB, NextBB, Name);
5905 BasicBlock *After = CL->getAfter();
5906
5907 // If location is not set, don't connect the loop.
5908 if (updateToLocation(Loc)) {
5909 // Split the loop at the insertion point: Branch to the preheader and move
5910 // every following instruction to after the loop (the After BB). Also, the
5911 // new successor is the loop's after block.
5912 spliceBB(Builder, After, /*CreateBranch=*/false);
5913 Builder.CreateBr(CL->getPreheader());
5914 }
5915
5916 // Emit the body content. We do it after connecting the loop to the CFG to
5917 // avoid that the callback encounters degenerate BBs.
5918 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5919 return Err;
5920
5921#ifndef NDEBUG
5922 CL->assertOK();
5923#endif
5924 return CL;
5925}
5926
5928 ScanInfos.emplace_front();
5929 ScanInfo *Result = &ScanInfos.front();
5930 return Result;
5931}
5932
5936 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5937 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5938 LocationDescription ComputeLoc =
5939 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5940 updateToLocation(ComputeLoc);
5941
5943
5945 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5946 ScanRedInfo->Span = TripCount;
5947 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5948 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5949
5950 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5951 Builder.restoreIP(CodeGenIP);
5952 ScanRedInfo->IV = IV;
5953 createScanBBs(ScanRedInfo);
5954 BasicBlock *InputBlock = Builder.GetInsertBlock();
5955 Instruction *Terminator = InputBlock->getTerminator();
5956 assert(Terminator->getNumSuccessors() == 1);
5957 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5958 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5959 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5960 Builder.GetInsertBlock()->getParent());
5961 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5962 emitBlock(ScanRedInfo->OMPScanLoopExit,
5963 Builder.GetInsertBlock()->getParent());
5964 Builder.CreateBr(ContinueBlock);
5965 Builder.SetInsertPoint(
5966 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5967 return BodyGenCB(Builder.saveIP(), IV);
5968 };
5969
5970 const auto &&InputLoopGen = [&]() -> Error {
5972 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5973 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5974 if (!LoopInfo)
5975 return LoopInfo.takeError();
5976 Result.push_back(*LoopInfo);
5977 Builder.restoreIP((*LoopInfo)->getAfterIP());
5978 return Error::success();
5979 };
5980 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5982 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5983 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5984 if (!LoopInfo)
5985 return LoopInfo.takeError();
5986 Result.push_back(*LoopInfo);
5987 Builder.restoreIP((*LoopInfo)->getAfterIP());
5988 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5989 return Error::success();
5990 };
5991 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5992 if (Err)
5993 return Err;
5994 return Result;
5995}
5996
5998 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5999 bool IsSigned, bool InclusiveStop, const Twine &Name) {
6000
6001 // Consider the following difficulties (assuming 8-bit signed integers):
6002 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
6003 // DO I = 1, 100, 50
6004 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
6005 // DO I = 100, 0, -128
6006
6007 // Start, Stop and Step must be of the same integer type.
6008 auto *IndVarTy = cast<IntegerType>(Start->getType());
6009 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
6010 assert(IndVarTy == Step->getType() && "Step type mismatch");
6011
6013
6014 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
6015 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
6016
6017 // Like Step, but always positive.
6018 Value *Incr = Step;
6019
6020 // Distance between Start and Stop; always positive.
6021 Value *Span;
6022
6023 // Condition whether there are no iterations are executed at all, e.g. because
6024 // UB < LB.
6025 Value *ZeroCmp;
6026
6027 if (IsSigned) {
6028 // Ensure that increment is positive. If not, negate and invert LB and UB.
6029 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6030 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6031 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6032 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6033 Span = Builder.CreateSub(UB, LB, "", false, true);
6034 ZeroCmp = Builder.CreateICmp(
6035 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6036 } else {
6037 Span = Builder.CreateSub(Stop, Start, "", true);
6038 ZeroCmp = Builder.CreateICmp(
6039 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6040 }
6041
6042 Value *CountIfLooping;
6043 if (InclusiveStop) {
6044 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6045 } else {
6046 // Avoid incrementing past stop since it could overflow.
6047 Value *CountIfTwo = Builder.CreateAdd(
6048 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6049 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6050 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6051 }
6052
6053 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6054 "omp_" + Name + ".tripcount");
6055}
6056
6059 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6060 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6061 ScanInfo *ScanRedInfo) {
6062 LocationDescription ComputeLoc =
6063 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6064
6066 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6067
6068 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6069 Builder.restoreIP(CodeGenIP);
6070 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6071 /*HasNSW=*/Config.hasNoSignedWrap());
6072 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6073 /*HasNSW=*/Config.hasNoSignedWrap());
6074 if (InScan)
6075 ScanRedInfo->IV = IndVar;
6076 return BodyGenCB(Builder.saveIP(), IndVar);
6077 };
6078 LocationDescription LoopLoc =
6079 ComputeIP.isValid()
6080 ? Loc
6081 : LocationDescription(Builder.saveIP(),
6082 Builder.getCurrentDebugLocation());
6083 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6084}
6085
6086// Returns an LLVM function to call for initializing loop bounds using OpenMP
6087// static scheduling for composite `distribute parallel for` depending on
6088// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6089// integers as unsigned similarly to CanonicalLoopInfo.
6090static FunctionCallee
6092 OpenMPIRBuilder &OMPBuilder) {
6093 unsigned Bitwidth = Ty->getIntegerBitWidth();
6094 if (Bitwidth == 32)
6095 return OMPBuilder.getOrCreateRuntimeFunction(
6096 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6097 if (Bitwidth == 64)
6098 return OMPBuilder.getOrCreateRuntimeFunction(
6099 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6100 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6101}
6102
6103// Returns an LLVM function to call for initializing loop bounds using OpenMP
6104// static scheduling depending on `type`. Only i32 and i64 are supported by the
6105// runtime. Always interpret integers as unsigned similarly to
6106// CanonicalLoopInfo.
6108 OpenMPIRBuilder &OMPBuilder) {
6109 unsigned Bitwidth = Ty->getIntegerBitWidth();
6110 if (Bitwidth == 32)
6111 return OMPBuilder.getOrCreateRuntimeFunction(
6112 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6113 if (Bitwidth == 64)
6114 return OMPBuilder.getOrCreateRuntimeFunction(
6115 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6116 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6117}
6118
6119OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6120 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6121 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6122 OMPScheduleType DistScheduleSchedType) {
6123 assert(CLI->isValid() && "Requires a valid canonical loop");
6124 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6125 "Require dedicated allocate IP");
6126
6127 // Set up the source location value for OpenMP runtime.
6128 Builder.restoreIP(CLI->getPreheaderIP());
6129 Builder.SetCurrentDebugLocation(DL);
6130
6131 uint32_t SrcLocStrSize;
6132 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6134 switch (LoopType) {
6135 case WorksharingLoopType::ForStaticLoop:
6136 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6137 break;
6138 case WorksharingLoopType::DistributeStaticLoop:
6139 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6140 break;
6141 case WorksharingLoopType::DistributeForStaticLoop:
6142 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6143 break;
6144 }
6145 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6146
6147 // Declare useful OpenMP runtime functions.
6148 Type *IVTy = CLI->getIndVarType();
6149 FunctionCallee StaticInit =
6150 LoopType == WorksharingLoopType::DistributeForStaticLoop
6151 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6152 : getKmpcForStaticInitForType(IVTy, M, *this);
6153 FunctionCallee StaticFini =
6154 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6155
6156 // Allocate space for computed loop bounds as expected by the "init" function.
6157 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getFirstNonPHIOrAlloca());
6158
6159 Type *I32Type = Type::getInt32Ty(M.getContext());
6160 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6161 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6162 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6163 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6164 CLI->setLastIter(PLastIter);
6165
6166 // At the end of the preheader, prepare for calling the "init" function by
6167 // storing the current loop bounds into the allocated space. A canonical loop
6168 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6169 // and produces an inclusive upper bound.
6170 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6171 Constant *Zero = ConstantInt::get(IVTy, 0);
6172 Constant *One = ConstantInt::get(IVTy, 1);
6173 Builder.CreateStore(Zero, PLowerBound);
6174 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6175 Builder.CreateStore(UpperBound, PUpperBound);
6176 Builder.CreateStore(One, PStride);
6177
6178 Value *ThreadNum =
6179 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6180
6181 OMPScheduleType SchedType =
6182 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6183 ? OMPScheduleType::OrderedDistribute
6185 Constant *SchedulingType =
6186 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6187
6188 // Call the "init" function and update the trip count of the loop with the
6189 // value it produced.
6190 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6191 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6192 this](Value *SchedulingType, auto &Builder) {
6193 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6194 PLowerBound, PUpperBound});
6195 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6196 Value *PDistUpperBound =
6197 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6198 Args.push_back(PDistUpperBound);
6199 }
6200 Args.append({PStride, One, Zero});
6201 createRuntimeFunctionCall(StaticInit, Args);
6202 };
6203 BuildInitCall(SchedulingType, Builder);
6204 if (HasDistSchedule &&
6205 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6206 Constant *DistScheduleSchedType = ConstantInt::get(
6207 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6208 // We want to emit a second init function call for the dist_schedule clause
6209 // to the Distribute construct. This should only be done however if a
6210 // Workshare Loop is nested within a Distribute Construct
6211 BuildInitCall(DistScheduleSchedType, Builder);
6212 }
6213 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6214 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6215 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6216 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6217 if (CLI->VirtualCollapse) {
6218 bindCollapsedRange(CLI, LowerBound, TripCount);
6219#ifndef NDEBUG
6220 CLI->assertOK();
6221#endif
6222 } else {
6223 CLI->setTripCount(TripCount);
6224 }
6225
6226 // Update all uses of the induction variable except the one in the condition
6227 // block that compares it with the actual upper bound, and the increment in
6228 // the latch block.
6229
6230 if (!CLI->VirtualCollapse)
6231 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6232 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6233 Builder.SetCurrentDebugLocation(DL);
6234 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6235 /*HasNSW=*/Config.hasNoSignedWrap());
6236 });
6237
6238 // In the "exit" block, call the "fini" function.
6239 Builder.SetInsertPoint(CLI->getExit()->getTerminator()->getIterator());
6240 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6241
6242 // Add the barrier if requested.
6243 if (NeedsBarrier) {
6244 InsertPointOrErrorTy BarrierIP =
6246 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6247 /* CheckCancelFlag */ false);
6248 if (!BarrierIP)
6249 return BarrierIP.takeError();
6250 }
6251
6252 InsertPointTy AfterIP = CLI->getAfterIP();
6253 // TODO: Removing Virtual's outer bound requires another way to reject idle
6254 // assignments whose raw count wraps. Keep it for correctness and trip-count
6255 // information for analyses. The expected saving is small; investigate the
6256 // effects on downstream optimizations before removing it.
6257 CLI->invalidate();
6258
6259 return AfterIP;
6260}
6261
6262static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6263 LoopInfo &LI);
6264static void addLoopMetadata(CanonicalLoopInfo *Loop,
6266
6268 LLVMContext &Ctx, Loop *Loop,
6270 SmallVector<Metadata *> &LoopMDList) {
6271 SmallSet<BasicBlock *, 8> Reachable;
6272
6273 // Get the basic blocks from the loop in which memref instructions
6274 // can be found.
6275 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6276 // preferably without running any passes.
6277 for (BasicBlock *Block : Loop->getBlocks()) {
6278 if (Block == CLI->getCond() || Block == CLI->getHeader())
6279 continue;
6280 Reachable.insert(Block);
6281 }
6282
6283 // Add access group metadata to memory-access instructions.
6285 for (BasicBlock *BB : Reachable)
6287 // TODO: If the loop has existing parallel access metadata, have
6288 // to combine two lists.
6289 LoopMDList.push_back(MDNode::get(
6290 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6291}
6292
6294OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6295 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6296 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6297 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6298 assert(CLI->isValid() && "Requires a valid canonical loop");
6299 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6300
6301 LLVMContext &Ctx = CLI->getFunction()->getContext();
6302 Value *IV = CLI->getIndVar();
6303 Value *OrigTripCount = CLI->getTripCount();
6304 Type *IVTy = IV->getType();
6305 assert(IVTy->getIntegerBitWidth() <= 64 &&
6306 "Max supported tripcount bitwidth is 64 bits");
6307 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6308 : Type::getInt64Ty(Ctx);
6309 Type *I32Type = Type::getInt32Ty(M.getContext());
6310 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6311 Constant *One = ConstantInt::get(InternalIVTy, 1);
6312
6313 Function *F = CLI->getFunction();
6314 // Blocks must have terminators.
6315 // FIXME: Don't run analyses on incomplete/invalid IR.
6316 SmallVector<Instruction *> UIs;
6317 for (BasicBlock &BB : *F)
6318 if (!BB.hasTerminator())
6319 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6321 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6322 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6323 LoopAnalysis LIA;
6324 LoopInfo &&LI = LIA.run(*F, FAM);
6325 for (Instruction *I : UIs)
6326 I->eraseFromParent();
6327 Loop *L = LI.getLoopFor(CLI->getHeader());
6328 SmallVector<Metadata *> LoopMDList;
6329 if (ChunkSize || DistScheduleChunkSize)
6330 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6331 addLoopMetadata(CLI, LoopMDList);
6332
6333 // Declare useful OpenMP runtime functions.
6334 FunctionCallee StaticInit =
6335 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6336 FunctionCallee StaticFini =
6337 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6338
6339 // Allocate space for computed loop bounds as expected by the "init" function.
6340 Builder.restoreIP(AllocaIP);
6341 Builder.SetCurrentDebugLocation(DL);
6342 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6343 Value *PLowerBound =
6344 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6345 Value *PUpperBound =
6346 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6347 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6348 CLI->setLastIter(PLastIter);
6349
6350 // Set up the source location value for the OpenMP runtime.
6351 Builder.restoreIP(CLI->getPreheaderIP());
6352 Builder.SetCurrentDebugLocation(DL);
6353
6354 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6355 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6356 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6357 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6358 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6359 "distschedulechunksize");
6360 Value *CastedTripCount =
6361 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6362
6363 Constant *SchedulingType =
6364 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6365 Constant *DistSchedulingType =
6366 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6367 Builder.CreateStore(Zero, PLowerBound);
6368 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6369 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6370 Value *UpperBound =
6371 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6372 Builder.CreateStore(UpperBound, PUpperBound);
6373 Builder.CreateStore(One, PStride);
6374
6375 // Call the "init" function and update the trip count of the loop with the
6376 // value it produced.
6377 uint32_t SrcLocStrSize;
6378 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6379 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6380 if (DistScheduleSchedType != OMPScheduleType::None) {
6381 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6382 }
6383 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6384 Value *ThreadNum =
6385 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6386 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6387 PUpperBound, PStride, One,
6388 this](Value *SchedulingType, Value *ChunkSize,
6389 auto &Builder) {
6391 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6392 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6393 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6394 /*pstride=*/PStride, /*incr=*/One,
6395 /*chunk=*/ChunkSize});
6396 };
6397 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6398 if (DistScheduleSchedType != OMPScheduleType::None &&
6399 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6400 SchedType != OMPScheduleType::OrderedDistribute) {
6401 // We want to emit a second init function call for the dist_schedule clause
6402 // to the Distribute construct. This should only be done however if a
6403 // Workshare Loop is nested within a Distribute Construct
6404 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6405 }
6406
6407 // Load values written by the "init" function.
6408 Value *FirstChunkStart =
6409 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6410 Value *FirstChunkStop =
6411 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6412 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6413 Value *ChunkRange =
6414 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6415 Value *NextChunkStride =
6416 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6417
6418 // Create outer "dispatch" loop for enumerating the chunks.
6419 BasicBlock *DispatchEnter = splitBB(Builder, true);
6420 Value *DispatchCounter;
6421
6422 // It is safe to assume this didn't return an error because the callback
6423 // passed into createCanonicalLoop is the only possible error source, and it
6424 // always returns success.
6425 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6426 {Builder.saveIP(), DL},
6427 [&](InsertPointTy BodyIP, Value *Counter) {
6428 DispatchCounter = Counter;
6429 return Error::success();
6430 },
6431 FirstChunkStart, CastedTripCount, NextChunkStride,
6432 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6433 "dispatch"));
6434
6435 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6436 // not have to preserve the canonical invariant.
6437 BasicBlock *DispatchBody = DispatchCLI->getBody();
6438 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6439 BasicBlock *DispatchExit = DispatchCLI->getExit();
6440 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6441 DispatchCLI->invalidate();
6442
6443 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6444 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6445 redirectTo(CLI->getExit(), DispatchLatch, DL);
6446 redirectTo(DispatchBody, DispatchEnter, DL);
6447
6448 // Prepare the prolog of the chunk loop.
6449 Builder.restoreIP(CLI->getPreheaderIP());
6450 Builder.SetCurrentDebugLocation(DL);
6451
6452 // Compute the number of iterations of the chunk loop.
6453 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6454 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6455 Value *IsLastChunk =
6456 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6457 Value *CountUntilOrigTripCount =
6458 Builder.CreateSub(CastedTripCount, DispatchCounter);
6459 Value *ChunkTripCount = Builder.CreateSelect(
6460 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6461 Value *BackcastedChunkTC =
6462 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6463 CLI->setTripCount(BackcastedChunkTC);
6464
6465 // Update all uses of the induction variable except the one in the condition
6466 // block that compares it with the actual upper bound, and the increment in
6467 // the latch block.
6468 Value *BackcastedDispatchCounter =
6469 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6470 CLI->mapIndVar([&](Instruction *) -> Value * {
6471 Builder.restoreIP(CLI->getBodyIP());
6472 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6473 });
6474
6475 // In the "exit" block, call the "fini" function.
6476 Builder.SetInsertPoint(DispatchExit->getFirstInsertionPt());
6477 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6478
6479 // Add the barrier if requested.
6480 if (NeedsBarrier) {
6481 InsertPointOrErrorTy AfterIP =
6482 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6483 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6484 if (!AfterIP)
6485 return AfterIP.takeError();
6486 }
6487
6488#ifndef NDEBUG
6489 // Even though we currently do not support applying additional methods to it,
6490 // the chunk loop should remain a canonical loop.
6491 CLI->assertOK();
6492#endif
6493
6494 return DispatchAfter->getFirstInsertionPt();
6495}
6496
6497// Returns an LLVM function to call for executing an OpenMP static worksharing
6498// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6499// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6500static FunctionCallee
6502 WorksharingLoopType LoopType) {
6503 unsigned Bitwidth = Ty->getIntegerBitWidth();
6504 Module &M = OMPBuilder->M;
6505 switch (LoopType) {
6506 case WorksharingLoopType::ForStaticLoop:
6507 if (Bitwidth == 32)
6508 return OMPBuilder->getOrCreateRuntimeFunction(
6509 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6510 if (Bitwidth == 64)
6511 return OMPBuilder->getOrCreateRuntimeFunction(
6512 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6513 break;
6514 case WorksharingLoopType::DistributeStaticLoop:
6515 if (Bitwidth == 32)
6516 return OMPBuilder->getOrCreateRuntimeFunction(
6517 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6518 if (Bitwidth == 64)
6519 return OMPBuilder->getOrCreateRuntimeFunction(
6520 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6521 break;
6522 case WorksharingLoopType::DistributeForStaticLoop:
6523 if (Bitwidth == 32)
6524 return OMPBuilder->getOrCreateRuntimeFunction(
6525 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6526 if (Bitwidth == 64)
6527 return OMPBuilder->getOrCreateRuntimeFunction(
6528 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6529 break;
6530 }
6531 if (Bitwidth != 32 && Bitwidth != 64) {
6532 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6533 }
6534 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6535}
6536
6537// Inserts a call to proper OpenMP Device RTL function which handles
6538// loop worksharing.
6540 WorksharingLoopType LoopType,
6541 BasicBlock *InsertBlock, Value *Ident,
6542 Value *LoopBodyArg, Value *TripCount,
6543 Function &LoopBodyFn, bool NoLoop) {
6544 Type *TripCountTy = TripCount->getType();
6545 Module &M = OMPBuilder->M;
6546 IRBuilder<> &Builder = OMPBuilder->Builder;
6547 FunctionCallee RTLFn =
6548 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6549 SmallVector<Value *, 8> RealArgs;
6550 RealArgs.push_back(Ident);
6551 RealArgs.push_back(&LoopBodyFn);
6552 RealArgs.push_back(LoopBodyArg);
6553 RealArgs.push_back(TripCount);
6554 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6555 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6556 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6557 Builder.restoreIP(std::prev(InsertBlock->end()));
6558 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6559 return;
6560 }
6561 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6562 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6563 Builder.restoreIP(std::prev(InsertBlock->end()));
6564 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6565
6566 RealArgs.push_back(
6567 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6568 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6569 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6570 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6571 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6572 } else {
6573 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6574 }
6575
6576 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6577}
6578
6580 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6581 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6582 WorksharingLoopType LoopType, bool NoLoop) {
6583 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6584 BasicBlock *Preheader = CLI->getPreheader();
6585 Value *TripCount = CLI->getTripCount();
6586
6587 // After loop body outling, the loop body contains only set up
6588 // of loop body argument structure and the call to the outlined
6589 // loop body function. Firstly, we need to move setup of loop body args
6590 // into loop preheader.
6591 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6592 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6593
6594 // The next step is to remove the whole loop. We do not it need anymore.
6595 // That's why make an unconditional branch from loop preheader to loop
6596 // exit block
6597 Builder.restoreIP(Preheader->end());
6598 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6599 Preheader->getTerminator()->eraseFromParent();
6600 Builder.CreateBr(CLI->getExit());
6601
6602 // Delete dead loop blocks
6603 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6604 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6605 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6606 CleanUpInfo.EntryBB = CLI->getHeader();
6607 CleanUpInfo.ExitBB = CLI->getExit();
6608 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6609 DeleteDeadBlocks(BlocksToBeRemoved);
6610
6611 // Find the instruction which corresponds to loop body argument structure
6612 // and remove the call to loop body function instruction.
6613 Value *LoopBodyArg;
6614 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6615 assert(OutlinedFnUser &&
6616 "Expected unique undroppable user of outlined function");
6617 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6618 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6619 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6620 "Expected outlined function call to be located in loop preheader");
6621 // Check in case no argument structure has been passed.
6622 if (OutlinedFnCallInstruction->arg_size() > 1)
6623 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6624 else
6625 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6626 OutlinedFnCallInstruction->eraseFromParent();
6627
6628 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6629 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6630
6631 for (auto &ToBeDeletedItem : ToBeDeleted)
6632 ToBeDeletedItem->eraseFromParent();
6633 CLI->invalidate();
6634}
6635
6636OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6637 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6638 WorksharingLoopType LoopType, bool NeedsBarrier, bool NoLoop,
6639 bool NeedsLastIter) {
6640 uint32_t SrcLocStrSize;
6641 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6642
6643 // Mirrors host runtime reporting of last iteration by in-body computation.
6644 if (NeedsLastIter) {
6645 Type *I32Type = Type::getInt32Ty(M.getContext());
6646 Builder.restoreIP(AllocaIP);
6647 AllocaInst *PLastIter =
6648 Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6649 CLI->setLastIter(PLastIter);
6650
6651 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6652 Builder.CreateStore(ConstantInt::get(I32Type, 0), PLastIter);
6653
6654 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6655 Value *TripCount = CLI->getTripCount();
6656 Value *LastIter =
6657 Builder.CreateSub(TripCount, ConstantInt::get(TripCount->getType(), 1));
6658 Value *IsLast =
6659 Builder.CreateICmpEQ(CLI->getIndVar(), LastIter, "omp.is_last_iter");
6660 Builder.CreateStore(Builder.CreateZExt(IsLast, I32Type), PLastIter);
6661 }
6662
6664 switch (LoopType) {
6665 case WorksharingLoopType::ForStaticLoop:
6666 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6667 break;
6668 case WorksharingLoopType::DistributeStaticLoop:
6669 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6670 break;
6671 case WorksharingLoopType::DistributeForStaticLoop:
6672 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6673 break;
6674 }
6675 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6676
6677 auto OI = std::make_unique<OutlineInfo>();
6678 OI->OuterAllocBB = CLI->getPreheader();
6679 Function *OuterFn = CLI->getPreheader()->getParent();
6680
6681 // Instructions which need to be deleted at the end of code generation
6682 SmallVector<Instruction *, 4> ToBeDeleted;
6683
6684 OI->OuterAllocBB = AllocaIP.getNodeParent();
6685
6686 // Mark the body loop as region which needs to be extracted
6687 OI->EntryBB = CLI->getBody();
6688 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6689 "omp.prelatch");
6690
6691 // Prepare loop body for extraction
6692 Builder.restoreIP(CLI->getPreheader()->begin());
6693
6694 // Insert new loop counter variable which will be used only in loop
6695 // body.
6696 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6697 Instruction *NewLoopCntLoad =
6698 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6699 // New loop counter instructions are redundant in the loop preheader when
6700 // code generation for workshare loop is finshed. That's why mark them as
6701 // ready for deletion.
6702 ToBeDeleted.push_back(NewLoopCntLoad);
6703 ToBeDeleted.push_back(NewLoopCnt);
6704
6705 // Analyse loop body region. Find all input variables which are used inside
6706 // loop body region.
6707 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6709 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6710
6711 CodeExtractorAnalysisCache CEAC(*OuterFn);
6712 CodeExtractor Extractor(Blocks,
6713 /* DominatorTree */ nullptr,
6714 /* AggregateArgs */ true,
6715 /* BlockFrequencyInfo */ nullptr,
6716 /* BranchProbabilityInfo */ nullptr,
6717 /* AssumptionCache */ nullptr,
6718 /* AllowVarArgs */ true,
6719 /* AllowAlloca */ true,
6720 /* AllocationBlock */ CLI->getPreheader(),
6721 /* DeallocationBlocks */ {},
6722 /* Suffix */ ".omp_wsloop",
6723 /* AggrArgsIn0AddrSpace */ true);
6724
6725 BasicBlock *CommonExit = nullptr;
6726 SetVector<Value *> SinkingCands, HoistingCands;
6727
6728 // Find allocas outside the loop body region which are used inside loop
6729 // body
6730 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6731
6732 // We need to model loop body region as the function f(cnt, loop_arg).
6733 // That's why we replace loop induction variable by the new counter
6734 // which will be one of loop body function argument
6736 CLI->getIndVar()->user_end());
6737 for (auto Use : Users) {
6738 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6739 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6740 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6741 }
6742 }
6743 }
6744 // Make sure that loop counter variable is not merged into loop body
6745 // function argument structure and it is passed as separate variable
6746 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6747
6748 // PostOutline CB is invoked when loop body function is outlined and
6749 // loop body is replaced by call to outlined function. We need to add
6750 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6751 // function will handle loop control logic.
6752 //
6753 OI->PostOutlineCB = [=, ToBeDeletedVec =
6754 std::move(ToBeDeleted)](Function &OutlinedFn) {
6755 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6756 LoopType, NoLoop);
6757 };
6758 addOutlineInfo(std::move(OI));
6759
6760 // Keep the barrier outside the outlined loop body so that every thread
6761 // encounters it, including threads that execute no iterations.
6762 if (NeedsBarrier) {
6763 Builder.SetInsertPoint(CLI->getExit()->getTerminator());
6764 // Standalone distribute loops never request a barrier. For both regular
6765 // worksharing loops and combined distribute/for loops, the barrier is
6766 // associated with the worksharing loop, hence OMPD_for.
6767 InsertPointOrErrorTy BarrierIP =
6768 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6769 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6770 if (!BarrierIP)
6771 return BarrierIP.takeError();
6772 }
6773 return CLI->getAfterIP();
6774}
6775
6778 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6779 bool HasSimdModifier, bool HasMonotonicModifier,
6780 bool HasNonmonotonicModifier, bool HasOrderedClause,
6781 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6782 Value *DistScheduleChunkSize, bool NeedsLastIter) {
6783 if (CLI->VirtualCollapse &&
6784 (Config.isTargetDevice() ||
6785 (SchedKind != omp::OMP_SCHEDULE_Static &&
6786 SchedKind != omp::OMP_SCHEDULE_Default) ||
6787 ChunkSize || HasOrderedClause || HasDistSchedule ||
6788 DistScheduleChunkSize || LoopType != WorksharingLoopType::ForStaticLoop))
6790 "Virtual collapse requires unchunked static worksharing");
6791 if (Config.isTargetDevice())
6792 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6793 NoLoop, NeedsLastIter);
6794 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6795 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6796 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6797
6798 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6799 OMPScheduleType::ModifierOrdered;
6800 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6801 if (HasDistSchedule) {
6802 DistScheduleSchedType = DistScheduleChunkSize
6803 ? OMPScheduleType::OrderedDistributeChunked
6804 : OMPScheduleType::OrderedDistribute;
6805 }
6806 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6807 case OMPScheduleType::BaseStatic:
6808 case OMPScheduleType::BaseDistribute:
6809 assert((!ChunkSize || !DistScheduleChunkSize) &&
6810 "No chunk size with static-chunked schedule");
6811 if (IsOrdered && !HasDistSchedule)
6812 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6813 NeedsBarrier, ChunkSize);
6814 // FIXME: Monotonicity ignored?
6815 if (DistScheduleChunkSize)
6816 return applyStaticChunkedWorkshareLoop(
6817 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6818 DistScheduleChunkSize, DistScheduleSchedType);
6819 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6820 HasDistSchedule);
6821
6822 case OMPScheduleType::BaseStaticChunked:
6823 case OMPScheduleType::BaseDistributeChunked:
6824 if (IsOrdered && !HasDistSchedule)
6825 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6826 NeedsBarrier, ChunkSize);
6827 // FIXME: Monotonicity ignored?
6828 return applyStaticChunkedWorkshareLoop(
6829 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6830 DistScheduleChunkSize, DistScheduleSchedType);
6831
6832 case OMPScheduleType::BaseRuntime:
6833 case OMPScheduleType::BaseAuto:
6834 case OMPScheduleType::BaseGreedy:
6835 case OMPScheduleType::BaseBalanced:
6836 case OMPScheduleType::BaseSteal:
6837 case OMPScheduleType::BaseRuntimeSimd:
6838 assert(!ChunkSize &&
6839 "schedule type does not support user-defined chunk sizes");
6840 [[fallthrough]];
6841 case OMPScheduleType::BaseGuidedSimd:
6842 case OMPScheduleType::BaseDynamicChunked:
6843 case OMPScheduleType::BaseGuidedChunked:
6844 case OMPScheduleType::BaseGuidedIterativeChunked:
6845 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6846 case OMPScheduleType::BaseStaticBalancedChunked:
6847 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6848 NeedsBarrier, ChunkSize);
6849
6850 default:
6851 llvm_unreachable("Unknown/unimplemented schedule kind");
6852 }
6853}
6854
6855/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6856/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6857/// the runtime. Always interpret integers as unsigned similarly to
6858/// CanonicalLoopInfo.
6859static FunctionCallee
6861 unsigned Bitwidth = Ty->getIntegerBitWidth();
6862 if (Bitwidth == 32)
6863 return OMPBuilder.getOrCreateRuntimeFunction(
6864 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6865 if (Bitwidth == 64)
6866 return OMPBuilder.getOrCreateRuntimeFunction(
6867 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6868 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6869}
6870
6871/// Returns an LLVM function to call for updating the next loop using OpenMP
6872/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6873/// the runtime. Always interpret integers as unsigned similarly to
6874/// CanonicalLoopInfo.
6875static FunctionCallee
6877 unsigned Bitwidth = Ty->getIntegerBitWidth();
6878 if (Bitwidth == 32)
6879 return OMPBuilder.getOrCreateRuntimeFunction(
6880 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6881 if (Bitwidth == 64)
6882 return OMPBuilder.getOrCreateRuntimeFunction(
6883 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6884 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6885}
6886
6887/// Returns an LLVM function to call for finalizing the dynamic loop using
6888/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6889/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6890static FunctionCallee
6892 unsigned Bitwidth = Ty->getIntegerBitWidth();
6893 if (Bitwidth == 32)
6894 return OMPBuilder.getOrCreateRuntimeFunction(
6895 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6896 if (Bitwidth == 64)
6897 return OMPBuilder.getOrCreateRuntimeFunction(
6898 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6899 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6900}
6901
6903OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6904 InsertPointTy AllocaIP,
6905 OMPScheduleType SchedType,
6906 bool NeedsBarrier, Value *Chunk) {
6907 assert(CLI->isValid() && "Requires a valid canonical loop");
6908 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6909 "Require dedicated allocate IP");
6911 "Require valid schedule type");
6912
6913 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6914 OMPScheduleType::ModifierOrdered;
6915
6916 // Set up the source location value for OpenMP runtime.
6917 Builder.SetCurrentDebugLocation(DL);
6918
6919 uint32_t SrcLocStrSize;
6920 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6921 Value *SrcLoc =
6922 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6923
6924 // Declare useful OpenMP runtime functions.
6925 Value *IV = CLI->getIndVar();
6926 Type *IVTy = IV->getType();
6927 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6928 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6929
6930 // Allocate space for computed loop bounds as expected by the "init" function.
6931 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getFirstNonPHIOrAlloca());
6932 Type *I32Type = Type::getInt32Ty(M.getContext());
6933 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6934 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6935 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6936 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6937 CLI->setLastIter(PLastIter);
6938
6939 // At the end of the preheader, prepare for calling the "init" function by
6940 // storing the current loop bounds into the allocated space. A canonical loop
6941 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6942 // and produces an inclusive upper bound.
6943 BasicBlock *PreHeader = CLI->getPreheader();
6944 Builder.SetInsertPoint(PreHeader->getTerminator());
6945 Constant *One = ConstantInt::get(IVTy, 1);
6946 Builder.CreateStore(One, PLowerBound);
6947 Value *UpperBound = CLI->getTripCount();
6948 Builder.CreateStore(UpperBound, PUpperBound);
6949 Builder.CreateStore(One, PStride);
6950
6951 BasicBlock *Header = CLI->getHeader();
6952 BasicBlock *Exit = CLI->getExit();
6953 BasicBlock *Cond = CLI->getCond();
6954 BasicBlock *Latch = CLI->getLatch();
6955 InsertPointTy AfterIP = CLI->getAfterIP();
6956
6957 // The CLI will be "broken" in the code below, as the loop is no longer
6958 // a valid canonical loop.
6959
6960 if (!Chunk)
6961 Chunk = One;
6962
6963 Value *ThreadNum =
6964 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6965
6966 Constant *SchedulingType =
6967 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6968
6969 // Call the "init" function.
6970 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6971 /* LowerBound */ One, UpperBound,
6972 /* step */ One, Chunk});
6973
6974 // An outer loop around the existing one.
6975 BasicBlock *OuterCond = BasicBlock::Create(
6976 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6977 PreHeader->getParent());
6978 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6979 Builder.SetInsertPoint(OuterCond->getFirstInsertionPt());
6981 DynamicNext,
6982 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6983 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6984 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6985 Value *LowerBound =
6986 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6987 Builder.CreateCondBr(MoreWork, Header, Exit);
6988
6989 // Change PHI-node in loop header to use outer cond rather than preheader,
6990 // and set IV to the LowerBound.
6991 Instruction *Phi = &Header->front();
6992 auto *PI = cast<PHINode>(Phi);
6993 PI->setIncomingBlock(0, OuterCond);
6994 PI->setIncomingValue(0, LowerBound);
6995
6996 // Then set the pre-header to jump to the OuterCond
6997 Instruction *Term = PreHeader->getTerminator();
6998 auto *Br = cast<UncondBrInst>(Term);
6999 Br->setSuccessor(OuterCond);
7000
7001 // Modify the inner condition:
7002 // * Use the UpperBound returned from the DynamicNext call.
7003 // * jump to the loop outer loop when done with one of the inner loops.
7004 Builder.SetInsertPoint(Cond->getFirstInsertionPt());
7005 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
7006 Instruction *Comp = &*Builder.GetInsertPoint();
7007 auto *CI = cast<CmpInst>(Comp);
7008 CI->setOperand(1, UpperBound);
7009 // Redirect the inner exit to branch to outer condition.
7010 Instruction *Branch = &Cond->back();
7011 auto *BI = cast<CondBrInst>(Branch);
7012 assert(BI->getSuccessor(1) == Exit);
7013 BI->setSuccessor(1, OuterCond);
7014
7015 // Call the "fini" function if "ordered" is present in wsloop directive.
7016 if (Ordered) {
7017 Builder.SetInsertPoint(&Latch->back());
7018 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
7019 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
7020 }
7021
7022 // Add the barrier if requested.
7023 if (NeedsBarrier) {
7024 Builder.SetInsertPoint(&Exit->back());
7025 InsertPointOrErrorTy BarrierIP =
7027 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
7028 /* CheckCancelFlag */ false);
7029 if (!BarrierIP)
7030 return BarrierIP.takeError();
7031 }
7032
7033 CLI->invalidate();
7034 return AfterIP;
7035}
7036
7037/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
7038/// after this \p OldTarget will be orphaned.
7040 BasicBlock *NewTarget, DebugLoc DL) {
7042 redirectTo(Pred, NewTarget, DL);
7043}
7044
7046 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
7047 // We add a block to BBsToKeep iff we have proven it has an external use.
7049
7050 while (true) {
7051 bool Changed = false;
7052
7053 for (BasicBlock *BB : BBs) {
7054 if (BBsToKeep.contains(BB))
7055 continue;
7056
7057 for (Use &U : BB->uses()) {
7058 auto *UseInst = dyn_cast<Instruction>(U.getUser());
7059 if (!UseInst)
7060 continue;
7061 BasicBlock *UseBB = UseInst->getParent();
7062 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
7063 BBsToKeep.insert(BB);
7064 Changed = true;
7065 break;
7066 }
7067 }
7068 }
7069
7070 if (!Changed)
7071 break;
7072 }
7073
7075 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7076 DeleteDeadBlocks(BBsToDelete);
7077}
7078
7079/// Physical handles captured before the source descriptors are invalidated.
7086
7088OpenMPIRBuilder::planVirtualRowLoop(ArrayRef<CanonicalLoopInfo *> Loops) {
7089 // The executor inserts a guard before the outer latch. An indirect jump
7090 // by block address could bypass that guard.
7091 if (isa<IndirectBrInst>(Loops.back()->getAfter()->getTerminator()))
7093 "Virtual collapse requires unobserved canonical loop controls");
7094 // Save the original blocks and IVs before the executor changes the CFG.
7095 VirtualRowPlan Plan;
7096 Plan.Latches.push_back(Loops.front()->getLatch());
7097 for (CanonicalLoopInfo *Loop : Loops) {
7098 Plan.IndVars.push_back(cast<PHINode>(Loop->getIndVar()));
7099 Plan.Preheaders.push_back(Loop->getPreheader());
7100 }
7101 // The inner loop must return directly to the latch where the guard is added.
7102 if (Loops.front()->getBody()->getUniqueSuccessor() !=
7103 Plan.Preheaders.back() ||
7104 Loops.back()->getAfter()->getSingleSuccessor() != Plan.Latches.front())
7105 reportFatalInternalError("Virtual collapse requires direct nesting");
7106 Plan.InnerCmp = cast<CmpInst>(&Loops.back()->getCond()->front());
7107 return Plan;
7108}
7109
7110void OpenMPIRBuilder::createVirtualRowExecutor(CanonicalLoopInfo *Result,
7111 const VirtualRowPlan &Plan,
7112 ArrayRef<Value *> TripCounts,
7113 Value *Begin, Value *Count,
7114 BasicBlock *Entry) {
7115 DebugLoc DL = Builder.getCurrentDebugLocation();
7116 BasicBlock *OuterHeader = Plan.IndVars.front()->getParent();
7117 Type *IVType = Begin->getType();
7118 Constant *Zero = ConstantInt::get(IVType, 0);
7119 // A row is one invocation of the inner loop. Decode the first logical
7120 // iteration once, before entering the original loop nest.
7121 Builder.SetInsertPoint(Entry);
7122 Value *InnerBegin =
7123 Builder.CreateURem(Begin, TripCounts.back(), "omp_collapsed.begin.1");
7124 Value *OuterBegin =
7125 Builder.CreateUDiv(Begin, TripCounts.back(), "omp_collapsed.begin.0");
7126 // Include the skipped prefix of the first row so each completed row
7127 // removes one full inner trip count from Remaining.
7128 Value *InitialRemaining =
7129 Builder.CreateAdd(Count, InnerBegin, "omp_collapsed.row.remaining");
7130 Builder.CreateBr(OuterHeader);
7131
7132 Builder.SetInsertPoint(OuterHeader->getFirstNonPHIIt());
7133 Builder.SetCurrentDebugLocation(DL);
7134 PHINode *Remaining =
7135 Builder.CreatePHI(IVType, 2, "omp_collapsed.row.remaining.0");
7136 Remaining->addIncoming(InitialRemaining, Entry);
7137 // Only the first row starts at InnerBegin; subsequent rows start at zero.
7138 // For an inner trip count of one, the existing zero start is sufficient.
7139 if (!cast<ConstantInt>(TripCounts.back())->isOne()) {
7140 PHINode *Start =
7141 Builder.CreatePHI(IVType, 2, "omp_collapsed.row.start.1.0");
7142 Start->addIncoming(InnerBegin, Entry);
7143 Start->addIncoming(Zero, Plan.Latches.front());
7144 Plan.IndVars.back()->setIncomingValue(0, Start);
7145 }
7146 // Saved inner-preheader insertions must not change the IV PHI's entry edge.
7147 // Keep the original branch as their anchor and give it a PHI-free successor.
7148 auto *InnerBr = cast<UncondBrInst>(Plan.Preheaders.back()->getTerminator());
7149 BasicBlock *InnerHeader = Plan.IndVars.back()->getParent();
7150 BasicBlock *InnerEntry =
7151 BasicBlock::Create(M.getContext(), "omp_collapsed.row.preheader",
7152 InnerHeader->getParent(), InnerHeader);
7153 Builder.SetInsertPoint(InnerEntry);
7154 Builder.SetCurrentDebugLocation(DL);
7155 Builder.CreateBr(InnerHeader);
7156 InnerBr->setSuccessor(InnerEntry);
7157 Plan.IndVars.back()->setIncomingBlock(0, InnerEntry);
7158 // The final row ends at Remaining; earlier rows keep their original bound.
7159 Builder.SetInsertPoint(Plan.Preheaders.back()->getTerminator());
7160 Builder.SetCurrentDebugLocation(DL);
7161 Value *IsFinalRow = Builder.CreateICmpULE(Remaining, TripCounts.back(),
7162 "omp_collapsed.row.is_final");
7163 Value *InnerStop = Builder.CreateSelect(
7164 IsFinalRow, Remaining, TripCounts.back(), "omp_collapsed.row.stop");
7165 Plan.InnerCmp->setOperand(1, InnerStop);
7166
7167 // Only non-final rows reach the outer latch, so Remaining is greater than
7168 // the inner trip count and this subtraction cannot underflow.
7169 BasicBlock *Latch = Plan.Latches.front();
7170 Builder.SetInsertPoint(Latch->getTerminator());
7171 Builder.SetCurrentDebugLocation(DL);
7172 Value *RemainingNext = Builder.CreateSub(Remaining, TripCounts.back(),
7173 "omp_collapsed.row.remaining.next",
7174 /*HasNUW=*/true);
7175 Remaining->addIncoming(RemainingNext, Latch);
7176 // Check completion after the inner loop and its continuation, before
7177 // incrementing the outer IV. The final row exits the whole nest.
7178 BasicBlock *Guard =
7179 Latch->splitBasicBlockBefore(Latch->begin(), "omp_collapsed.row.done");
7180 Guard->getTerminator()->eraseFromParent();
7181 Builder.SetInsertPoint(Guard);
7182 Builder.CreateCondBr(IsFinalRow, Result->getExit(), Latch);
7183 Plan.IndVars.front()->setIncomingValue(0, OuterBegin);
7184 Plan.IndVars.front()->setIncomingBlock(0, Entry);
7185}
7186
7187CanonicalLoopInfo *OpenMPIRBuilder::createVirtualCollapsedLoop(
7189 const VirtualRowPlan &Plan, ArrayRef<Value *> TripCounts,
7190 Value *CollapsedTripCount) {
7191 // Reuse the outer loop's control blocks; the physical nest stays in place.
7192 LoopInfos.emplace_front();
7193 CanonicalLoopInfo *Result = &LoopInfos.front();
7194 Result->Header = Loops.front()->getHeader();
7195 Result->Cond = Loops.front()->getCond();
7196 Result->Latch = Loops.front()->getLatch();
7197 Result->Exit = Loops.front()->getExit();
7198 BasicBlock *Preheader = Plan.Preheaders.front();
7199 Function *F = Result->getFunction();
7200 Type *IVType = CollapsedTripCount->getType();
7201 Constant *Zero = ConstantInt::get(IVType, 0);
7202 // Keep initialization separate from decoding so an empty range skips both
7203 // division and the loop nest.
7204 BasicBlock *Init = BasicBlock::Create(M.getContext(), "omp_collapsed.init", F,
7205 Result->getHeader());
7206 BasicBlock *Entry = BasicBlock::Create(M.getContext(), "omp_collapsed.decode",
7207 F, Result->getHeader());
7208 Builder.SetCurrentDebugLocation(DL);
7209 Builder.SetInsertPoint(Init);
7210 // Keep explicit range copies until scheduling replaces operand zero. Create
7211 // the instructions directly so the initial constants are not folded away.
7212 auto *Begin = Builder.Insert(BinaryOperator::CreateAdd(Zero, Zero),
7213 "omp_collapsed.range.begin");
7214 auto *Count =
7215 Builder.Insert(BinaryOperator::CreateAdd(CollapsedTripCount, Zero),
7216 "omp_collapsed.range.count");
7217 Builder.CreateCondBr(Builder.CreateICmpNE(Count, Zero), Entry,
7218 Result->getExit());
7219 Result->VirtualCollapse =
7220 CanonicalLoopInfo::VirtualCollapseInfo{Begin, Count};
7221 // Route the preheader through Init. The executor reconnects the outer IV
7222 // PHI to the decode block.
7223 cast<UncondBrInst>(Preheader->getTerminator())->setSuccessor(Init);
7224 createVirtualRowExecutor(Result, Plan, TripCounts, Begin, Count, Entry);
7225 // The transformed nest is now represented by Result, not the input loops.
7226 for (CanonicalLoopInfo *Loop : Loops)
7227 Loop->invalidate();
7228#ifndef NDEBUG
7229 Result->assertOK();
7230#endif
7231 return Result;
7232}
7233
7234void OpenMPIRBuilder::bindCollapsedRange(CanonicalLoopInfo *Loop, Value *Begin,
7235 Value *Count) {
7236 // Update only the copied values; the range has no PHI predecessor to rebind.
7237 Loop->VirtualCollapse->Begin->setOperand(0, Begin);
7238 Loop->VirtualCollapse->Count->setOperand(0, Count);
7239}
7240
7241CanonicalLoopInfo *
7243 InsertPointTy ComputeIP) {
7244 assert(Loops.size() >= 1 && "At least one loop required");
7245 size_t NumLoops = Loops.size();
7246
7247 // Nothing to do if there is already just one loop.
7248 if (NumLoops == 1)
7249 return Loops.front();
7250
7251 if (Config.CollapseMapping == CollapseIVMapping::Virtual) {
7252 if (Loops.size() != 2)
7254 "Virtual collapse requires two constant-count loops");
7255 SmallVector<Value *, 4> TripCounts;
7256 for (CanonicalLoopInfo *Loop : Loops) {
7257 if (Loop->VirtualCollapse)
7258 Loop->reportUnsupportedVirtualCollapse();
7259 Value *TC = Loop->getTripCount();
7260 if (!isa<ConstantInt>(TC))
7262 "Virtual collapse requires two constant-count loops");
7263 TripCounts.push_back(TC);
7264 }
7265 VirtualRowPlan Plan = planVirtualRowLoop(Loops);
7266 // Constant counts fold the product without emitting instructions.
7267 Value *CollapsedTripCount =
7268 Builder.CreateNUWMul(TripCounts.front(), TripCounts.back());
7269 return createVirtualCollapsedLoop(DL, Loops, Plan, TripCounts,
7270 CollapsedTripCount);
7271 }
7272
7273 CanonicalLoopInfo *Outermost = Loops.front();
7274 CanonicalLoopInfo *Innermost = Loops.back();
7275 BasicBlock *OrigPreheader = Outermost->getPreheader();
7276 BasicBlock *OrigAfter = Outermost->getAfter();
7277 Function *F = OrigPreheader->getParent();
7278
7279 // Loop control blocks that may become orphaned later.
7280 SmallVector<BasicBlock *, 12> OldControlBBs;
7281 OldControlBBs.reserve(6 * Loops.size());
7283 Loop->collectControlBlocks(OldControlBBs);
7284
7285 // Setup the IRBuilder for inserting the trip count computation.
7286 Builder.SetCurrentDebugLocation(DL);
7287 if (ComputeIP.isValid())
7288 Builder.restoreIP(ComputeIP);
7289 else
7290 Builder.restoreIP(Outermost->getPreheaderIP());
7291
7292 // Derive the collapsed' loop trip count.
7293 // TODO: Find common/largest indvar type.
7294 Value *CollapsedTripCount = nullptr;
7295 for (CanonicalLoopInfo *L : Loops) {
7296 assert(L->isValid() &&
7297 "All loops to collapse must be valid canonical loops");
7298 Value *OrigTripCount = L->getTripCount();
7299 if (!CollapsedTripCount) {
7300 CollapsedTripCount = OrigTripCount;
7301 continue;
7302 }
7303
7304 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7305 CollapsedTripCount =
7306 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7307 }
7308
7309 // Create the collapsed loop control flow.
7310 CanonicalLoopInfo *Result =
7311 createLoopSkeleton(DL, CollapsedTripCount, F,
7312 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7313 /*IsCollapsed=*/true);
7314
7315 // Build the collapsed loop body code.
7316 // Start with deriving the input loop induction variables from the collapsed
7317 // one, using a divmod scheme. To preserve the original loops' order, the
7318 // innermost loop use the least significant bits.
7319 Builder.restoreIP(Result->getBodyIP());
7320
7321 Value *Leftover = Result->getIndVar();
7322 SmallVector<Value *> NewIndVars;
7323 NewIndVars.resize(NumLoops);
7324 for (int i = NumLoops - 1; i >= 1; --i) {
7325 Value *OrigTripCount = Loops[i]->getTripCount();
7326
7327 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7328 NewIndVars[i] = NewIndVar;
7329
7330 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7331 }
7332 // Outermost loop gets all the remaining bits.
7333 NewIndVars[0] = Leftover;
7334
7335 // Construct the loop body control flow.
7336 // We progressively construct the branch structure following in direction of
7337 // the control flow, from the leading in-between code, the loop nest body, the
7338 // trailing in-between code, and rejoining the collapsed loop's latch.
7339 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7340 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7341 // its predecessors as sources.
7342 BasicBlock *ContinueBlock = Result->getBody();
7343 BasicBlock *ContinuePred = nullptr;
7344 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7345 BasicBlock *NextSrc) {
7346 if (ContinueBlock)
7347 redirectTo(ContinueBlock, Dest, DL);
7348 else
7349 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7350
7351 ContinueBlock = nullptr;
7352 ContinuePred = NextSrc;
7353 };
7354
7355 // The code before the nested loop of each level.
7356 // Because we are sinking it into the nest, it will be executed more often
7357 // that the original loop. More sophisticated schemes could keep track of what
7358 // the in-between code is and instantiate it only once per thread.
7359 for (size_t i = 0; i < NumLoops - 1; ++i)
7360 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7361
7362 // Connect the loop nest body.
7363 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7364
7365 // The code after the nested loop at each level.
7366 for (size_t i = NumLoops - 1; i > 0; --i)
7367 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7368
7369 // Connect the finished loop to the collapsed loop latch.
7370 ContinueWith(Result->getLatch(), nullptr);
7371
7372 // Replace the input loops with the new collapsed loop.
7373 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7374 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7375
7376 // Replace the input loop indvars with the derived ones.
7377 for (size_t i = 0; i < NumLoops; ++i)
7378 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7379
7380 // Remove unused parts of the input loops.
7381 removeUnusedBlocksFromParent(OldControlBBs);
7382
7383 for (CanonicalLoopInfo *L : Loops)
7384 L->invalidate();
7385
7386#ifndef NDEBUG
7387 Result->assertOK();
7388#endif
7389 return Result;
7390}
7391
7392std::vector<CanonicalLoopInfo *>
7394 ArrayRef<Value *> TileSizes) {
7395 assert(TileSizes.size() == Loops.size() &&
7396 "Must pass as many tile sizes as there are loops");
7397 int NumLoops = Loops.size();
7398 assert(NumLoops >= 1 && "At least one loop to tile required");
7399
7400 CanonicalLoopInfo *OutermostLoop = Loops.front();
7401 CanonicalLoopInfo *InnermostLoop = Loops.back();
7402 Function *F = OutermostLoop->getBody()->getParent();
7403 BasicBlock *InnerEnter = InnermostLoop->getBody();
7404 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7405
7406 // Loop control blocks that may become orphaned later.
7407 SmallVector<BasicBlock *, 12> OldControlBBs;
7408 OldControlBBs.reserve(6 * Loops.size());
7410 Loop->collectControlBlocks(OldControlBBs);
7411
7412 // Collect original trip counts and induction variable to be accessible by
7413 // index. Also, the structure of the original loops is not preserved during
7414 // the construction of the tiled loops, so do it before we scavenge the BBs of
7415 // any original CanonicalLoopInfo.
7416 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7417 for (CanonicalLoopInfo *L : Loops) {
7418 assert(L->isValid() && "All input loops must be valid canonical loops");
7419 OrigTripCounts.push_back(L->getTripCount());
7420 OrigIndVars.push_back(L->getIndVar());
7421 }
7422
7423 // Collect the code between loop headers. These may contain SSA definitions
7424 // that are used in the loop nest body. To be usable with in the innermost
7425 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7426 // these instructions may be executed more often than before the tiling.
7427 // TODO: It would be sufficient to only sink them into body of the
7428 // corresponding tile loop.
7430 for (int i = 0; i < NumLoops - 1; ++i) {
7431 CanonicalLoopInfo *Surrounding = Loops[i];
7432 CanonicalLoopInfo *Nested = Loops[i + 1];
7433
7434 BasicBlock *EnterBB = Surrounding->getBody();
7435 BasicBlock *ExitBB = Nested->getHeader();
7436 InbetweenCode.emplace_back(EnterBB, ExitBB);
7437 }
7438
7439 // Compute the trip counts of the floor loops.
7440 Builder.SetCurrentDebugLocation(DL);
7441 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7442 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7443 for (int i = 0; i < NumLoops; ++i) {
7444 Value *TileSize = TileSizes[i];
7445 Value *OrigTripCount = OrigTripCounts[i];
7446 Type *IVType = OrigTripCount->getType();
7447
7448 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7449 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7450
7451 // 0 if tripcount divides the tilesize, 1 otherwise.
7452 // 1 means we need an additional iteration for a partial tile.
7453 //
7454 // Unfortunately we cannot just use the roundup-formula
7455 // (tripcount + tilesize - 1)/tilesize
7456 // because the summation might overflow. We do not want introduce undefined
7457 // behavior when the untiled loop nest did not.
7458 Value *FloorTripOverflow =
7459 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7460
7461 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7462 Value *FloorTripCount =
7463 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7464 "omp_floor" + Twine(i) + ".tripcount", true);
7465
7466 // Remember some values for later use.
7467 FloorCompleteCount.push_back(FloorCompleteTripCount);
7468 FloorCount.push_back(FloorTripCount);
7469 FloorRems.push_back(FloorTripRem);
7470 }
7471
7472 // Generate the new loop nest, from the outermost to the innermost.
7473 std::vector<CanonicalLoopInfo *> Result;
7474 Result.reserve(NumLoops * 2);
7475
7476 // The basic block of the surrounding loop that enters the nest generated
7477 // loop.
7478 BasicBlock *Enter = OutermostLoop->getPreheader();
7479
7480 // The basic block of the surrounding loop where the inner code should
7481 // continue.
7482 BasicBlock *Continue = OutermostLoop->getAfter();
7483
7484 // Where the next loop basic block should be inserted.
7485 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7486
7487 auto EmbeddNewLoop =
7488 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7489 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7490 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7491 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7492 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7493 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7494
7495 // Setup the position where the next embedded loop connects to this loop.
7496 Enter = EmbeddedLoop->getBody();
7497 Continue = EmbeddedLoop->getLatch();
7498 OutroInsertBefore = EmbeddedLoop->getLatch();
7499 return EmbeddedLoop;
7500 };
7501
7502 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7503 const Twine &NameBase) {
7504 for (auto P : enumerate(TripCounts)) {
7505 CanonicalLoopInfo *EmbeddedLoop =
7506 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7507 Result.push_back(EmbeddedLoop);
7508 }
7509 };
7510
7511 EmbeddNewLoops(FloorCount, "floor");
7512
7513 // Within the innermost floor loop, emit the code that computes the tile
7514 // sizes.
7515 Builder.SetInsertPoint(Enter->getTerminator());
7516 SmallVector<Value *, 4> TileCounts;
7517 for (int i = 0; i < NumLoops; ++i) {
7518 CanonicalLoopInfo *FloorLoop = Result[i];
7519 Value *TileSize = TileSizes[i];
7520
7521 Value *FloorIsEpilogue =
7522 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7523 Value *TileTripCount =
7524 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7525
7526 TileCounts.push_back(TileTripCount);
7527 }
7528
7529 // Create the tile loops.
7530 EmbeddNewLoops(TileCounts, "tile");
7531
7532 // Insert the inbetween code into the body.
7533 BasicBlock *BodyEnter = Enter;
7534 BasicBlock *BodyEntered = nullptr;
7535 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7536 BasicBlock *EnterBB = P.first;
7537 BasicBlock *ExitBB = P.second;
7538
7539 if (BodyEnter)
7540 redirectTo(BodyEnter, EnterBB, DL);
7541 else
7542 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7543
7544 BodyEnter = nullptr;
7545 BodyEntered = ExitBB;
7546 }
7547
7548 // Append the original loop nest body into the generated loop nest body.
7549 if (BodyEnter)
7550 redirectTo(BodyEnter, InnerEnter, DL);
7551 else
7552 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7554
7555 // Replace the original induction variable with an induction variable computed
7556 // from the tile and floor induction variables.
7557 Builder.restoreIP(Result.back()->getBodyIP());
7558 for (int i = 0; i < NumLoops; ++i) {
7559 CanonicalLoopInfo *FloorLoop = Result[i];
7560 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7561 Value *OrigIndVar = OrigIndVars[i];
7562 Value *Size = TileSizes[i];
7563
7564 Value *Scale =
7565 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7566 Value *Shift =
7567 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7568 OrigIndVar->replaceAllUsesWith(Shift);
7569 }
7570
7571 // Remove unused parts of the original loops.
7572 removeUnusedBlocksFromParent(OldControlBBs);
7573
7574 for (CanonicalLoopInfo *L : Loops)
7575 L->invalidate();
7576
7577#ifndef NDEBUG
7578 for (CanonicalLoopInfo *GenL : Result)
7579 GenL->assertOK();
7580#endif
7581 return Result;
7582}
7583
7584/// Attach metadata \p Properties to the basic block described by \p BB. If the
7585/// basic block already has metadata, the basic block properties are appended.
7588 // Nothing to do if no property to attach.
7589 if (Properties.empty())
7590 return;
7591
7592 LLVMContext &Ctx = BB->getContext();
7593 SmallVector<Metadata *> NewProperties;
7594 NewProperties.push_back(nullptr);
7595
7596 // If the basic block already has metadata, prepend it to the new metadata.
7597 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7598 if (Existing)
7599 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7600
7601 append_range(NewProperties, Properties);
7602 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7603 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7604
7605 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7606}
7607
7608/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7609/// loop already has metadata, the loop properties are appended.
7612 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7613
7614 // Attach metadata to the loop's latch
7615 BasicBlock *Latch = Loop->getLatch();
7616 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7618}
7619
7620/// Attach llvm.access.group metadata to the memref instructions of \p Block
7622 LoopInfo &LI) {
7623 for (Instruction &I : *Block) {
7624 if (I.mayReadOrWriteMemory()) {
7625 // TODO: This instruction may already have access group from
7626 // other pragmas e.g. #pragma clang loop vectorize. Append
7627 // so that the existing metadata is not overwritten.
7628 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7629 }
7630 }
7631}
7632
7633CanonicalLoopInfo *
7635 CanonicalLoopInfo *firstLoop = Loops.front();
7636 CanonicalLoopInfo *lastLoop = Loops.back();
7637 Function *F = firstLoop->getPreheader()->getParent();
7638
7639 // Loop control blocks that will become orphaned later
7640 SmallVector<BasicBlock *> oldControlBBs;
7642 Loop->collectControlBlocks(oldControlBBs);
7643
7644 // Collect original trip counts
7645 SmallVector<Value *> origTripCounts;
7646 for (CanonicalLoopInfo *L : Loops) {
7647 assert(L->isValid() && "All input loops must be valid canonical loops");
7648 origTripCounts.push_back(L->getTripCount());
7649 }
7650
7651 Builder.SetCurrentDebugLocation(DL);
7652
7653 // Compute max trip count.
7654 // The fused loop will be from 0 to max(origTripCounts)
7655 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7656 F, firstLoop->getHeader());
7657 Builder.SetInsertPoint(TCBlock);
7658 Value *fusedTripCount = nullptr;
7659 for (CanonicalLoopInfo *L : Loops) {
7660 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7661 Value *origTripCount = L->getTripCount();
7662 if (!fusedTripCount) {
7663 fusedTripCount = origTripCount;
7664 continue;
7665 }
7666 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7667 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7668 ".omp.fuse.tc");
7669 }
7670
7671 // Generate new loop
7672 CanonicalLoopInfo *fused =
7673 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7674 lastLoop->getLatch(), "fused");
7675
7676 // Replace original loops with the fused loop
7677 // Preheader and After are not considered inside the CLI.
7678 // These are used to compute the individual TCs of the loops
7679 // so they have to be put before the resulting fused loop.
7680 // Moving them up for readability.
7681 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7682 Loops[i]->getPreheader()->moveBefore(TCBlock);
7683 Loops[i]->getAfter()->moveBefore(TCBlock);
7684 }
7685 lastLoop->getPreheader()->moveBefore(TCBlock);
7686
7687 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7688 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7689 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7690 }
7691 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7692 redirectTo(TCBlock, fused->getPreheader(), DL);
7693 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7694
7695 // Build the fused body
7696 // Create new Blocks with conditions that jump to the original loop bodies
7698 SmallVector<Value *> condValues;
7699 for (size_t i = 0; i < Loops.size(); ++i) {
7700 BasicBlock *condBlock = BasicBlock::Create(
7701 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7702 Builder.SetInsertPoint(condBlock);
7703 Value *condValue =
7704 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7705 condBBs.push_back(condBlock);
7706 condValues.push_back(condValue);
7707 }
7708 // Join the condition blocks with the bodies of the original loops
7709 redirectTo(fused->getBody(), condBBs[0], DL);
7710 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7711 Builder.SetInsertPoint(condBBs[i]);
7712 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7713 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7714 // Replace the IV with the fused IV
7715 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7716 }
7717 // Last body jumps to the created end body block
7718 Builder.SetInsertPoint(condBBs.back());
7719 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7720 fused->getLatch());
7721 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7722 // Replace the IV with the fused IV
7723 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7724
7725 // The loop latch must have only one predecessor. Currently it is branched to
7726 // from both the last condition block and the last loop body
7727 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7728 "omp.fused.pre_latch");
7729
7730 // Remove unused parts
7731 removeUnusedBlocksFromParent(oldControlBBs);
7732
7733 // Invalidate old CLIs
7734 for (CanonicalLoopInfo *L : Loops)
7735 L->invalidate();
7736
7737#ifndef NDEBUG
7738 fused->assertOK();
7739#endif
7740 return fused;
7741}
7742
7743void OpenMPIRBuilder::addUnrollMetadata(CanonicalLoopInfo *Loop,
7745 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7746 if (Loop->VirtualCollapse)
7747 reportFatalInternalError("unrolling must precede Virtual collapse");
7749}
7750
7752 LLVMContext &Ctx = Builder.getContext();
7753 addUnrollMetadata(
7754 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7755 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7756}
7757
7759 LLVMContext &Ctx = Builder.getContext();
7760 addUnrollMetadata(
7761 Loop, {
7762 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7763 });
7764}
7765
7766void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7767 Value *IfCond, ValueToValueMapTy &VMap,
7768 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7769 const Twine &NamePrefix) {
7770 Function *F = CanonicalLoop->getFunction();
7771
7772 // We can't do
7773 // if (cond) {
7774 // simd_loop;
7775 // } else {
7776 // non_simd_loop;
7777 // }
7778 // because then the CanonicalLoopInfo would only point to one of the loops:
7779 // leading to other constructs operating on the same loop to malfunction.
7780 // Instead generate
7781 // while (...) {
7782 // if (cond) {
7783 // simd_body;
7784 // } else {
7785 // not_simd_body;
7786 // }
7787 // }
7788 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7789 // body at -O3
7790
7791 // Define where if branch should be inserted
7792 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7793
7794 // Create additional blocks for the if statement
7795 BasicBlock *Cond = SplitBeforeIt->getParent();
7796 llvm::LLVMContext &C = Cond->getContext();
7798 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7800 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7801
7802 // Create if condition branch.
7803 Builder.SetInsertPoint(SplitBeforeIt);
7804 Instruction *BrInstr =
7805 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7806 InsertPointTy IP(++BrInstr->getIterator());
7807 // Then block contains branch to omp loop body which needs to be vectorized
7808 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7809 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7810
7811 Builder.SetInsertPoint(ElseBlock);
7812
7813 // Clone loop for the else branch
7815
7816 SmallVector<BasicBlock *, 8> ExistingBlocks;
7817 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7818 ExistingBlocks.push_back(ThenBlock);
7819 ExistingBlocks.append(L->block_begin(), L->block_end());
7820 // Cond is the block that has the if clause condition
7821 // LoopCond is omp_loop.cond
7822 // LoopHeader is omp_loop.header
7823 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7824 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7825 assert(LoopCond && LoopHeader && "Invalid loop structure");
7826 for (BasicBlock *Block : ExistingBlocks) {
7827 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7828 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7829 continue;
7830 }
7831 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7832
7833 // fix name not to be omp.if.then
7834 if (Block == ThenBlock)
7835 NewBB->setName(NamePrefix + ".if.else");
7836
7837 NewBB->moveBefore(CanonicalLoop->getExit());
7838 VMap[Block] = NewBB;
7839 NewBlocks.push_back(NewBB);
7840 }
7841 remapInstructionsInBlocks(NewBlocks, VMap);
7842 Builder.CreateBr(NewBlocks.front());
7843
7844 // The loop latch must have only one predecessor. Currently it is branched to
7845 // from both the 'then' and 'else' branches.
7846 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7847 NamePrefix + ".pre_latch");
7848
7849 // Ensure that the then block is added to the loop so we add the attributes in
7850 // the next step
7851 L->addBasicBlockToLoop(ThenBlock, LI);
7852}
7853
7854unsigned
7856 const StringMap<bool> &Features) {
7857 if (TargetTriple.isX86()) {
7858 if (Features.lookup("avx512f"))
7859 return 512;
7860 else if (Features.lookup("avx"))
7861 return 256;
7862 return 128;
7863 }
7864 if (TargetTriple.isPPC())
7865 return 128;
7866 if (TargetTriple.isWasm())
7867 return 128;
7868 if (TargetTriple.isSystemZ())
7869 return 64;
7870 return 0;
7871}
7872
7874 MapVector<Value *, Value *> AlignedVars,
7875 Value *IfCond, OrderKind Order,
7876 ConstantInt *Simdlen, ConstantInt *Safelen) {
7877 if (CanonicalLoop->VirtualCollapse)
7878 CanonicalLoop->reportUnsupportedVirtualCollapse();
7879 LLVMContext &Ctx = Builder.getContext();
7880
7881 Function *F = CanonicalLoop->getFunction();
7882
7883 // Blocks must have terminators.
7884 // FIXME: Don't run analyses on incomplete/invalid IR.
7886 for (BasicBlock &BB : *F)
7887 if (!BB.hasTerminator())
7888 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7889
7890 // TODO: We should not rely on pass manager. Currently we use pass manager
7891 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7892 // object. We should have a method which returns all blocks between
7893 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7895 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7896 FAM.registerPass([]() { return LoopAnalysis(); });
7897 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7898
7899 LoopAnalysis LIA;
7900 LoopInfo &&LI = LIA.run(*F, FAM);
7901
7902 for (Instruction *I : UIs)
7903 I->eraseFromParent();
7904
7905 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7906 if (AlignedVars.size()) {
7907 InsertPointTy IP = Builder.saveIP();
7908 for (auto &AlignedItem : AlignedVars) {
7909 Value *AlignedPtr = AlignedItem.first;
7910 Value *Alignment = AlignedItem.second;
7911 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7912 Builder.SetInsertPoint(loadInst->getNextNode());
7913 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7914 Alignment);
7915 }
7916 Builder.restoreIP(IP);
7917 }
7918
7919 if (IfCond) {
7920 ValueToValueMapTy VMap;
7921 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7922 }
7923
7925
7926 // Get the basic blocks from the loop in which memref instructions
7927 // can be found.
7928 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7929 // preferably without running any passes.
7930 for (BasicBlock *Block : L->getBlocks()) {
7931 if (Block == CanonicalLoop->getCond() ||
7932 Block == CanonicalLoop->getHeader())
7933 continue;
7934 Reachable.insert(Block);
7935 }
7936
7937 SmallVector<Metadata *> LoopMDList;
7938
7939 // In presence of finite 'safelen', it may be unsafe to mark all
7940 // the memory instructions parallel, because loop-carried
7941 // dependences of 'safelen' iterations are possible.
7942 // If clause order(concurrent) is specified then the memory instructions
7943 // are marked parallel even if 'safelen' is finite.
7944 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7945 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7946
7947 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7948 // versions so we can't add the loop attributes in that case.
7949 if (IfCond) {
7950 // we can still add llvm.loop.parallel_access
7951 addLoopMetadata(CanonicalLoop, LoopMDList);
7952 return;
7953 }
7954
7955 // Use the above access group metadata to create loop level
7956 // metadata, which should be distinct for each loop.
7957 LoopMDList.push_back(
7958 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7959
7960 if (Simdlen || Safelen) {
7961 // If both simdlen and safelen clauses are specified, the value of the
7962 // simdlen parameter must be less than or equal to the value of the safelen
7963 // parameter. Therefore, use safelen only in the absence of simdlen.
7964 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7965 LoopMDList.push_back(
7966 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7967 ConstantAsMetadata::get(VectorizeWidth)}));
7968 }
7969
7970 addLoopMetadata(CanonicalLoop, LoopMDList);
7971}
7972
7974 int32_t Factor,
7975 CanonicalLoopInfo **UnrolledCLI) {
7976 assert(Factor >= 0 && "Unroll factor must not be negative");
7977
7978 Function *F = Loop->getFunction();
7979 LLVMContext &Ctx = F->getContext();
7980
7981 // If the unrolled loop is not used for another loop-associated directive, it
7982 // is sufficient to add metadata for the LoopUnrollPass.
7983 if (!UnrolledCLI) {
7984 SmallVector<Metadata *, 2> LoopMetadata;
7985 LoopMetadata.push_back(
7986 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7987
7988 if (Factor >= 1) {
7990 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7991 LoopMetadata.push_back(MDNode::get(
7992 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7993 }
7994
7995 addUnrollMetadata(Loop, LoopMetadata);
7996 return;
7997 }
7998
7999 if (Factor == 0) {
8000 if (Loop->VirtualCollapse)
8001 Loop->reportUnsupportedVirtualCollapse();
8003 }
8004
8005 // No change required with unroll factor 1.
8006 if (Factor == 1) {
8007 *UnrolledCLI = Loop;
8008 return;
8009 }
8010
8011 assert(Factor >= 2 &&
8012 "unrolling only makes sense with a factor of 2 or larger");
8013
8014 Type *IndVarTy = Loop->getIndVarType();
8015
8016 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
8017 // unroll the inner loop.
8018 Value *FactorVal =
8019 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
8020 /*isSigned=*/false));
8021 std::vector<CanonicalLoopInfo *> LoopNest =
8022 tileLoops(DL, {Loop}, {FactorVal});
8023 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
8024 *UnrolledCLI = LoopNest[0];
8025 CanonicalLoopInfo *InnerLoop = LoopNest[1];
8026
8027 // LoopUnrollPass can only fully unroll loops with constant trip count.
8028 // Unroll by the unroll factor with a fallback epilog for the remainder
8029 // iterations if necessary.
8031 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
8033 InnerLoop,
8034 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
8036 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
8037
8038#ifndef NDEBUG
8039 (*UnrolledCLI)->assertOK();
8040#endif
8041}
8042
8045 llvm::Value *BufSize, llvm::Value *CpyBuf,
8046 llvm::Value *CpyFn, llvm::Value *DidIt) {
8047 if (!updateToLocation(Loc))
8048 return Loc.IP;
8049
8050 uint32_t SrcLocStrSize;
8051 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8052 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8053 Value *ThreadId = getOrCreateThreadID(Ident);
8054
8055 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
8056
8057 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
8058
8059 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
8060 createRuntimeFunctionCall(Fn, Args);
8061
8062 return Builder.saveIP();
8063}
8064
8066 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8067 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
8069
8070 if (!updateToLocation(Loc))
8071 return Loc.IP;
8072
8073 // If needed allocate and initialize `DidIt` with 0.
8074 // DidIt: flag variable: 1=single thread; 0=not single thread.
8075 llvm::Value *DidIt = nullptr;
8076 if (!CPVars.empty()) {
8077 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
8078 Builder.CreateStore(Builder.getInt32(0), DidIt);
8079 }
8080
8081 Directive OMPD = Directive::OMPD_single;
8082 uint32_t SrcLocStrSize;
8083 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8084 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8085 Value *ThreadId = getOrCreateThreadID(Ident);
8086 Value *Args[] = {Ident, ThreadId};
8087
8088 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8089 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8090
8091 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8092 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8093
8094 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8095 if (Error Err = FiniCB(IP))
8096 return Err;
8097
8098 // The thread that executes the single region must set `DidIt` to 1.
8099 // This is used by __kmpc_copyprivate, to know if the caller is the
8100 // single thread or not.
8101 if (DidIt)
8102 Builder.CreateStore(Builder.getInt32(1), DidIt);
8103
8104 return Error::success();
8105 };
8106
8107 // generates the following:
8108 // if (__kmpc_single()) {
8109 // .... single region ...
8110 // __kmpc_end_single
8111 // }
8112 // __kmpc_copyprivate
8113 // __kmpc_barrier
8114
8115 InsertPointOrErrorTy AfterIP =
8116 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8117 /*Conditional*/ true,
8118 /*hasFinalize*/ true);
8119 if (!AfterIP)
8120 return AfterIP.takeError();
8121
8122 if (DidIt) {
8123 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8124 // NOTE BufSize is currently unused, so just pass 0.
8126 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8127 CPFuncs[I], DidIt);
8128 // NOTE __kmpc_copyprivate already inserts a barrier
8129 } else if (!IsNowait) {
8130 InsertPointOrErrorTy AfterIP =
8132 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8133 /* CheckCancelFlag */ false);
8134 if (!AfterIP)
8135 return AfterIP.takeError();
8136 }
8137 return Builder.saveIP();
8138}
8139
8142 BodyGenCallbackTy BodyGenCB,
8143 FinalizeCallbackTy FiniCB, bool IsNowait) {
8144
8145 if (!updateToLocation(Loc))
8146 return Loc.IP;
8147
8148 // All threads execute the scope body — no conditional entry.
8149 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8150 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8151 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8152 /*IsCancellable=*/false);
8153 if (!AfterIP)
8154 return AfterIP.takeError();
8155
8156 Builder.restoreIP(*AfterIP);
8157 if (!IsNowait) {
8158 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8159 omp::Directive::OMPD_unknown,
8160 /*ForceSimpleCall=*/false,
8161 /*CheckCancelFlag=*/false);
8162 if (!AfterIP)
8163 return AfterIP.takeError();
8164 }
8165 return Builder.saveIP();
8166}
8167
8169 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8170 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8171
8172 if (!updateToLocation(Loc))
8173 return Loc.IP;
8174
8175 Directive OMPD = Directive::OMPD_critical;
8176 uint32_t SrcLocStrSize;
8177 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8178 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8179 Value *ThreadId = getOrCreateThreadID(Ident);
8180 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8181 Value *Args[] = {Ident, ThreadId, LockVar};
8182
8183 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8184 Function *RTFn = nullptr;
8185 if (HintInst) {
8186 // Add Hint to entry Args and create call
8187 EnterArgs.push_back(HintInst);
8188 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8189 } else {
8190 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8191 }
8192 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8193
8194 Function *ExitRTLFn =
8195 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8196 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8197
8198 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8199 /*Conditional*/ false, /*hasFinalize*/ true);
8200}
8201
8204 InsertPointTy AllocaIP, unsigned NumLoops,
8205 ArrayRef<llvm::Value *> StoreValues,
8206 const Twine &Name, bool IsDependSource) {
8207 assert(
8208 llvm::all_of(StoreValues,
8209 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8210 "OpenMP runtime requires depend vec with i64 type");
8211
8212 if (!updateToLocation(Loc))
8213 return Loc.IP;
8214
8215 // Allocate space for vector and generate alloc instruction.
8216 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8217 Builder.restoreIP(AllocaIP);
8218 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8219 ArgsBase->setAlignment(Align(8));
8221
8222 // Store the index value with offset in depend vector.
8223 for (unsigned I = 0; I < NumLoops; ++I) {
8224 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8225 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8226 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8227 STInst->setAlignment(Align(8));
8228 }
8229
8230 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8231 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8232
8233 uint32_t SrcLocStrSize;
8234 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8235 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8236 Value *ThreadId = getOrCreateThreadID(Ident);
8237 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8238
8239 Function *RTLFn = nullptr;
8240 if (IsDependSource)
8241 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8242 else
8243 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8244 createRuntimeFunctionCall(RTLFn, Args);
8245
8246 return Builder.saveIP();
8247}
8248
8250 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8251 FinalizeCallbackTy FiniCB, bool IsThreads) {
8252 if (!updateToLocation(Loc))
8253 return Loc.IP;
8254
8255 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8256 Instruction *EntryCall = nullptr;
8257 Instruction *ExitCall = nullptr;
8258
8259 if (IsThreads) {
8260 uint32_t SrcLocStrSize;
8261 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8262 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8263 Value *ThreadId = getOrCreateThreadID(Ident);
8264 Value *Args[] = {Ident, ThreadId};
8265
8266 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8267 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8268
8269 Function *ExitRTLFn =
8270 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8271 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8272 }
8273
8274 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8275 /*Conditional*/ false, /*hasFinalize*/ true);
8276}
8277
8278OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8279 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8280 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8281 bool HasFinalize, bool IsCancellable) {
8282
8283 if (HasFinalize)
8284 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8285
8286 // Create inlined region's entry and body blocks, in preparation
8287 // for conditional creation
8288 BasicBlock *EntryBB = Builder.GetInsertBlock();
8289 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8291 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8292 SplitPos->setDebugLoc(Builder.getCurrentDebugLocation());
8293 }
8294 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8295 BasicBlock *FiniBB =
8296 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8297
8298 Builder.SetInsertPoint(EntryBB->getTerminator());
8299 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8300
8301 // generate body
8302 if (Error Err =
8303 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8304 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8305 return Err;
8306
8307 // emit exit call and do any needed finalization.
8308 auto FinIP = FiniBB->getFirstInsertionPt();
8309 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8310 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8311 "Unexpected control flow graph state!!");
8312 InsertPointOrErrorTy AfterIP =
8313 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8314 if (!AfterIP)
8315 return AfterIP.takeError();
8316
8317 // If we are skipping the region of a non conditional, remove the exit
8318 // block, and clear the builder's insertion point.
8319 assert(SplitPos->getParent() == ExitBB &&
8320 "Unexpected Insertion point location!");
8321 auto merged = MergeBlockIntoPredecessor(ExitBB);
8322 BasicBlock *ExitPredBB = SplitPos->getParent();
8323 auto InsertBB = merged ? ExitPredBB : ExitBB;
8325 SplitPos->eraseFromParent();
8326 Builder.SetInsertPoint(InsertBB);
8327
8328 return Builder.saveIP();
8329}
8330
8331OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8332 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8333 // if nothing to do, Return current insertion point.
8334 if (!Conditional || !EntryCall)
8335 return Builder.saveIP();
8336
8337 BasicBlock *EntryBB = Builder.GetInsertBlock();
8338 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8339 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8340 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8341
8342 // Emit thenBB and set the Builder's insertion point there for
8343 // body generation next. Place the block after the current block.
8344 Function *CurFn = EntryBB->getParent();
8345 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8346
8347 // Move Entry branch to end of ThenBB, and replace with conditional
8348 // branch (If-stmt)
8349 Instruction *EntryBBTI = EntryBB->getTerminator();
8350 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8351 EntryBBTI->removeFromParent();
8352 Builder.SetInsertPoint(UI);
8353 Builder.Insert(EntryBBTI);
8354 UI->eraseFromParent();
8355 Builder.SetInsertPoint(ThenBB->getTerminator());
8356
8357 // return an insertion point to ExitBB.
8358 return ExitBB->getFirstInsertionPt();
8359}
8360
8361OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8362 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8363 bool HasFinalize) {
8364
8365 Builder.restoreIP(FinIP);
8366
8367 // If there is finalization to do, emit it before the exit call
8368 if (HasFinalize) {
8369 assert(!FinalizationStack.empty() &&
8370 "Unexpected finalization stack state!");
8371
8372 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8373 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8374
8375 BasicBlock *FinBB = FinIP.getNodeParent();
8376 if (Error Err = Fi.mergeFiniBB(Builder, FinBB))
8377 return std::move(Err);
8378
8379 // Exit condition: insertion point is before the terminator of the new Fini
8380 // block
8381 Builder.SetInsertPoint(FinBB->getTerminator());
8382 }
8383
8384 if (!ExitCall)
8385 return Builder.saveIP();
8386
8387 // place the Exitcall as last instruction before Finalization block terminator
8388 ExitCall->removeFromParent();
8389 Builder.Insert(ExitCall);
8390
8391 return ExitCall->getIterator();
8392}
8393
8395 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8396 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8397 if (!IP.isValid())
8398 return IP;
8399
8401
8402 // creates the following CFG structure
8403 // OMP_Entry : (MasterAddr != PrivateAddr)?
8404 // F T
8405 // | \
8406 // | copin.not.master
8407 // | /
8408 // v /
8409 // copyin.not.master.end
8410 // |
8411 // v
8412 // OMP.Entry.Next
8413
8414 BasicBlock *OMP_Entry = IP.getNodeParent();
8415 Function *CurFn = OMP_Entry->getParent();
8416 BasicBlock *CopyBegin =
8417 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8418 BasicBlock *CopyEnd = nullptr;
8419
8420 // If entry block is terminated, split to preserve the branch to following
8421 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8423 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8424 "copyin.not.master.end");
8425 OMP_Entry->getTerminator()->eraseFromParent();
8426 } else {
8427 CopyEnd =
8428 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8429 }
8430
8431 Builder.SetInsertPoint(OMP_Entry);
8432 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8433 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8434 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8435 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8436
8437 Builder.SetInsertPoint(CopyBegin);
8438 if (BranchtoEnd)
8439 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8440
8441 return Builder.saveIP();
8442}
8443
8445 Value *Size, Value *Allocator,
8446 std::string Name) {
8448 if (!updateToLocation(Loc))
8449 return nullptr;
8450
8451 uint32_t SrcLocStrSize;
8452 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8453 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8454 Value *ThreadId = getOrCreateThreadID(Ident);
8455 Value *Args[] = {ThreadId, Size, Allocator};
8456
8457 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8458
8459 return createRuntimeFunctionCall(Fn, Args, Name);
8460}
8461
8463 Value *Align, Value *Size,
8464 Value *Allocator,
8465 std::string Name) {
8467 if (!updateToLocation(Loc))
8468 return nullptr;
8469
8470 uint32_t SrcLocStrSize;
8471 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8472 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8473 Value *ThreadId = getOrCreateThreadID(Ident);
8474 Value *Args[] = {ThreadId, Align, Size, Allocator};
8475
8476 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8477
8478 return Builder.CreateCall(Fn, Args, Name);
8479}
8480
8482 Value *Addr, Value *Allocator,
8483 std::string Name) {
8485 if (!updateToLocation(Loc))
8486 return nullptr;
8487
8488 uint32_t SrcLocStrSize;
8489 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8490 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8491 Value *ThreadId = getOrCreateThreadID(Ident);
8492 Value *Args[] = {ThreadId, Addr, Allocator};
8493 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8494 return createRuntimeFunctionCall(Fn, Args, Name);
8495}
8496
8498 Value *Size,
8499 const Twine &Name) {
8502
8503 Value *Args[] = {Size};
8504 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8505 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8507 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8508 return Call;
8509}
8510
8512 Type *VarType,
8513 const Twine &Name) {
8514 return createOMPAllocShared(
8515 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8516}
8517
8519 Value *Addr, Value *Size,
8520 const Twine &Name) {
8523
8524 Value *Args[] = {Addr, Size};
8525 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8526 return Builder.CreateCall(Fn, Args, Name);
8527}
8528
8530 Value *Addr, Type *VarType,
8531 const Twine &Name) {
8532 return createOMPFreeShared(
8533 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8534 Name);
8535}
8536
8538 const LocationDescription &Loc, Value *InteropVar,
8540 Value *DependenceAddress, bool HaveNowaitClause) {
8543
8544 uint32_t SrcLocStrSize;
8545 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8546 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8547 Value *ThreadId = getOrCreateThreadID(Ident);
8548 if (Device == nullptr)
8550 else if (Device->getType() != Int32)
8551 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8552 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8553 if (NumDependences == nullptr) {
8554 NumDependences = ConstantInt::get(Int32, 0);
8555 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8556 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8557 }
8558 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8559 Value *Args[] = {
8560 Ident, ThreadId, InteropVar, InteropTypeVal,
8561 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8562
8563 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8564
8565 return createRuntimeFunctionCall(Fn, Args);
8566}
8567
8569 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8570 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8573
8574 uint32_t SrcLocStrSize;
8575 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8576 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8577 Value *ThreadId = getOrCreateThreadID(Ident);
8578 if (Device == nullptr)
8580 else if (Device->getType() != Int32)
8581 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8582 if (NumDependences == nullptr) {
8583 NumDependences = ConstantInt::get(Int32, 0);
8584 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8585 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8586 }
8587 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8588 Value *Args[] = {
8589 Ident, ThreadId, InteropVar, Device,
8590 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8591
8592 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8593
8594 return createRuntimeFunctionCall(Fn, Args);
8595}
8596
8598 Value *InteropVar, Value *Device,
8599 Value *NumDependences,
8600 Value *DependenceAddress,
8601 bool HaveNowaitClause) {
8604 uint32_t SrcLocStrSize;
8605 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8606 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8607 Value *ThreadId = getOrCreateThreadID(Ident);
8608 if (Device == nullptr)
8610 else if (Device->getType() != Int32)
8611 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8612 if (NumDependences == nullptr) {
8613 NumDependences = ConstantInt::get(Int32, 0);
8614 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8615 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8616 }
8617 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8618 Value *Args[] = {
8619 Ident, ThreadId, InteropVar, Device,
8620 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8621
8622 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8623
8624 return createRuntimeFunctionCall(Fn, Args);
8625}
8626
8629 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8632
8633 uint32_t SrcLocStrSize;
8634 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8635 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8636 Value *ThreadId = getOrCreateThreadID(Ident);
8637 Constant *ThreadPrivateCache =
8638 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8639 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8640
8641 Function *Fn =
8642 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8643
8644 return createRuntimeFunctionCall(Fn, Args);
8645}
8646
8648 const LocationDescription &Loc,
8650 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8651 "expected num_threads and num_teams to be specified");
8652
8653 if (!updateToLocation(Loc))
8654 return nullptr;
8655
8656 uint32_t SrcLocStrSize;
8657 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8658 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8659 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8660 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8661 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8662 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8663 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8664 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8665
8666 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8667 Function *Kernel = DebugKernelWrapper;
8668
8669 // We need to strip the debug prefix to get the correct kernel name.
8670 StringRef KernelName = Kernel->getName();
8671 const std::string DebugPrefix = "_debug__";
8672 if (KernelName.ends_with(DebugPrefix)) {
8673 KernelName = KernelName.drop_back(DebugPrefix.length());
8674 Kernel = M.getFunction(KernelName);
8675 assert(Kernel && "Expected the real kernel to exist");
8676 }
8677
8678 // Manifest the launch configuration in the metadata matching the kernel
8679 // environment.
8680 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8681 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8682 Attrs.MaxTeams.front());
8683
8684 // Don't derive or write thread bounds for Bare kernels.
8685 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8686 if (Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_BARE) {
8687 // If MaxThreads is not set and needs adjustment, select the maximum
8688 // between the default workgroup size and the MinThreads value. This is
8689 // only meaningful for targets with a known grid value (i.e. GPUs); for
8690 // other targets (e.g. host kernels) leave it unset so the runtime falls
8691 // back to its own device-specific default.
8692 if (MaxThreadsVal < 0 && UseDefaultMaxThreads && hasGridValue(T))
8693 MaxThreadsVal =
8694 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8695 Attrs.MinThreads.front());
8696
8697 // Generic mode runs the main thread on a warp of its own, past
8698 // thread_limit. Reserve the widest warp any target has. Not on SPIR-V,
8699 // causes problems with Level Zero.
8700 if (MaxThreadsVal > 0 &&
8701 Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC && hasGridValue(T) &&
8702 !T.isSPIRV())
8703 MaxThreadsVal = int32_t(
8704 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8705 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8706
8707 if (MaxThreadsVal > 0)
8708 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8709 MaxThreadsVal);
8710 }
8711
8712 Constant *MinThreads =
8713 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8714 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8715 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8716 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8717 Constant *ReductionDataSize =
8718 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8719
8720 const DataLayout &DL = M.getDataLayout();
8721
8722 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8723 Constant *DynamicEnvironmentInitializer =
8724 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8725 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8726 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8727 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8728 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8729 DL.getDefaultGlobalsAddressSpace());
8730 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8731
8732 Constant *DynamicEnvironment =
8733 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8734 ? DynamicEnvironmentGV
8735 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8736 DynamicEnvironmentPtr);
8737
8738 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8739 ConfigurationEnvironment, {
8740 UseGenericStateMachineVal,
8741 MayUseNestedParallelismVal,
8742 IsSPMDVal,
8743 MinThreads,
8744 MaxThreads,
8745 MinTeams,
8746 MaxTeams,
8747 ReductionDataSize,
8748 });
8749 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8750 KernelEnvironment, {
8751 ConfigurationEnvironmentInitializer,
8752 Ident,
8753 DynamicEnvironment,
8754 });
8755 std::string KernelEnvironmentName =
8756 (KernelName + "_kernel_environment").str();
8757 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8758 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8759 KernelEnvironmentInitializer, KernelEnvironmentName,
8760 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8761 DL.getDefaultGlobalsAddressSpace());
8762 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8763
8764 return KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8765 ? KernelEnvironmentGV
8766 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8767 KernelEnvironmentPtr);
8768}
8769
8771 const LocationDescription &Loc,
8773 Constant *KernelEnvironment = emitKernelEnvironment(Loc, Attrs);
8774 if (!KernelEnvironment)
8775 return Loc.IP;
8776
8777 if (!updateToLocation(Loc))
8778 return Loc.IP;
8779
8780 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8782 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8783
8784 Value *KernelLaunchEnvironment =
8785 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8786 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8787 KernelLaunchEnvironment =
8788 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8789 ? KernelLaunchEnvironment
8790 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8791 KernelLaunchEnvParamTy);
8792 CallInst *ThreadKind = createRuntimeFunctionCall(
8793 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8794
8795 Value *ExecUserCode = Builder.CreateICmpEQ(
8796 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8797 "exec_user_code");
8798
8799 // ThreadKind = __kmpc_target_init(...)
8800 // if (ThreadKind == -1)
8801 // user_code
8802 // else
8803 // return;
8804
8805 auto *UI = Builder.CreateUnreachable();
8806 BasicBlock *CheckBB = UI->getParent();
8807 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8808
8809 BasicBlock *WorkerExitBB = BasicBlock::Create(
8810 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8811 Builder.SetInsertPoint(WorkerExitBB);
8812 Builder.CreateRetVoid();
8813
8814 auto *CheckBBTI = CheckBB->getTerminator();
8815 Builder.SetInsertPoint(CheckBBTI);
8816 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8817
8818 CheckBBTI->eraseFromParent();
8819 UI->eraseFromParent();
8820
8821 // Continue in the "user_code" block, see diagram above and in
8822 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8823 return UserCodeEntryBB->getFirstInsertionPt();
8824}
8825
8827 int32_t TeamsReductionDataSize) {
8828 if (!updateToLocation(Loc))
8829 return;
8830
8832 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8833
8835
8836 if (!TeamsReductionDataSize)
8837 return;
8838
8839 Function *Kernel = Builder.GetInsertBlock()->getParent();
8840 // We need to strip the debug prefix to get the correct kernel name.
8841 StringRef KernelName = Kernel->getName();
8842 const std::string DebugPrefix = "_debug__";
8843 if (KernelName.ends_with(DebugPrefix))
8844 KernelName = KernelName.drop_back(DebugPrefix.length());
8845 auto *KernelEnvironmentGV =
8846 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8847 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8848 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8849 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8850 KernelEnvironmentInitializer,
8851 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8852 KernelEnvironmentGV->setInitializer(NewInitializer);
8853}
8854
8855static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8856 bool Min) {
8857 if (Kernel.hasFnAttribute(Name)) {
8858 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8859 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8860 }
8861 Kernel.addFnAttr(Name, llvm::utostr(Value));
8862}
8863
8864std::pair<int32_t, int32_t>
8866 int32_t ThreadLimit =
8867 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8868
8869 if (T.isAMDGPU()) {
8870 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8871 if (!Attr.isValid() || !Attr.isStringAttribute())
8872 return {0, ThreadLimit};
8873 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8874 int32_t LB, UB;
8875 if (!llvm::to_integer(UBStr, UB, 10))
8876 return {0, ThreadLimit};
8877 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8878 if (!llvm::to_integer(LBStr, LB, 10))
8879 return {0, UB};
8880 return {LB, UB};
8881 }
8882
8883 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8884 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8885 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8886 }
8887 return {0, ThreadLimit};
8888}
8889
8891 Function &Kernel, int32_t LB,
8892 int32_t UB) {
8893 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8894
8895 if (T.isAMDGPU()) {
8896 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8897 llvm::utostr(LB) + "," + llvm::utostr(UB));
8898 return;
8899 }
8900
8902}
8903
8904std::pair<int32_t, int32_t>
8906 // TODO: Read from backend annotations if available.
8907 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8908}
8909
8911 int32_t LB, int32_t UB) {
8912 if (UB > 0) {
8913 if (T.isNVPTX())
8915 if (T.isAMDGPU())
8916 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8917 }
8918
8919 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8920}
8921
8922void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8923 Function *OutlinedFn) {
8924 if (Config.isTargetDevice()) {
8926 // TODO: Determine if DSO local can be set to true.
8927 OutlinedFn->setDSOLocal(false);
8929 if (T.isAMDGCN())
8931 else if (T.isNVPTX())
8933 else if (T.isSPIRV())
8935 }
8936}
8937
8938Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8939 StringRef EntryFnIDName) {
8940 if (Config.isTargetDevice()) {
8941 assert(OutlinedFn && "The outlined function must exist if embedded");
8942 return OutlinedFn;
8943 }
8944
8945 return new GlobalVariable(
8946 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8947 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8948}
8949
8950Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8951 StringRef EntryFnName) {
8952 if (OutlinedFn)
8953 return OutlinedFn;
8954
8955 assert(!M.getGlobalVariable(EntryFnName, true) &&
8956 "Named kernel already exists?");
8957 return new GlobalVariable(
8958 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8959 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8960}
8961
8963 TargetRegionEntryInfo &EntryInfo,
8964 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8965 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8966
8967 SmallString<64> EntryFnName;
8968 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8969
8970 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8971 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8972 if (!CBResult)
8973 return CBResult.takeError();
8974 OutlinedFn = *CBResult;
8975 } else {
8976 OutlinedFn = nullptr;
8977 }
8978
8979 // If this target outline function is not an offload entry, we don't need to
8980 // register it. This may be in the case of a false if clause, or if there are
8981 // no OpenMP targets.
8982 if (!IsOffloadEntry)
8983 return Error::success();
8984
8985 std::string EntryFnIDName =
8986 Config.isTargetDevice()
8987 ? std::string(EntryFnName)
8988 : createPlatformSpecificName({EntryFnName, "region_id"});
8989
8990 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8991 EntryFnName, EntryFnIDName);
8992 return Error::success();
8993}
8994
8996 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8997 StringRef EntryFnName, StringRef EntryFnIDName) {
8998 if (OutlinedFn)
8999 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
9000 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
9001 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
9002 OffloadInfoManager.registerTargetRegionEntryInfo(
9003 EntryInfo, EntryAddr, OutlinedFnID,
9005 return OutlinedFnID;
9006}
9007
9009 const LocationDescription &Loc, InsertPointTy AllocaIP,
9010 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
9011 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
9012 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
9013 omp::RuntimeFunction *MapperFunc,
9015 BodyGenTy BodyGenType)>
9016 BodyGenCB,
9017 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
9018 if (!updateToLocation(Loc))
9019 return InsertPointTy();
9020
9021 Builder.restoreIP(CodeGenIP);
9022
9023 bool IsStandAlone = !BodyGenCB;
9024 MapInfosTy *MapInfo;
9025 // Generate the code for the opening of the data environment. Capture all the
9026 // arguments of the runtime call by reference because they are used in the
9027 // closing of the region.
9028 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9029 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9030 MapInfo = &GenMapInfoCB(Builder.saveIP());
9031 if (Error Err = emitOffloadingArrays(
9032 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
9033 /*IsNonContiguous=*/true, DeviceAddrCB))
9034 return Err;
9035
9036 TargetDataRTArgs RTArgs;
9038
9039 // Emit the number of elements in the offloading arrays.
9040 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9041
9042 // Source location for the ident struct
9043 if (!SrcLocInfo) {
9044 uint32_t SrcLocStrSize;
9045 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9046 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9047 }
9048
9049 SmallVector<llvm::Value *, 13> OffloadingArgs = {
9050 SrcLocInfo, DeviceID,
9051 PointerNum, RTArgs.BasePointersArray,
9052 RTArgs.PointersArray, RTArgs.SizesArray,
9053 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9054 RTArgs.MappersArray};
9055
9056 if (IsStandAlone) {
9057 assert(MapperFunc && "MapperFunc missing for standalone target data");
9058
9059 auto TaskBodyCB = [&](Value *, Value *,
9061 if (Info.HasNoWait) {
9062 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
9066 }
9067
9069 OffloadingArgs);
9070
9071 if (Info.HasNoWait) {
9072 BasicBlock *OffloadContBlock =
9073 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
9074 Function *CurFn = Builder.GetInsertBlock()->getParent();
9075 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
9076 Builder.restoreIP(Builder.saveIP());
9077 }
9078 return Error::success();
9079 };
9080
9081 bool RequiresOuterTargetTask = Info.HasNoWait;
9082 if (!RequiresOuterTargetTask)
9083 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
9084 /*TargetTaskAllocaIP=*/{}));
9085 else
9086 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
9087 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
9088 } else {
9089 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
9090 omp::OMPRTL___tgt_target_data_begin_mapper);
9091
9092 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
9093
9094 for (auto DeviceMap : Info.DevicePtrInfoMap) {
9095 if (isa<AllocaInst>(DeviceMap.second.second)) {
9096 auto *LI =
9097 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
9098 Builder.CreateStore(LI, DeviceMap.second.second);
9099 }
9100 }
9101
9102 // If device pointer privatization is required, emit the body of the
9103 // region here. It will have to be duplicated: with and without
9104 // privatization.
9105 InsertPointOrErrorTy AfterIP =
9106 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9107 if (!AfterIP)
9108 return AfterIP.takeError();
9109 Builder.restoreIP(*AfterIP);
9110 }
9111 return Error::success();
9112 };
9113
9114 // If we need device pointer privatization, we need to emit the body of the
9115 // region with no privatization in the 'else' branch of the conditional.
9116 // Otherwise, we don't have to do anything.
9117 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9118 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9119 InsertPointOrErrorTy AfterIP =
9120 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9121 if (!AfterIP)
9122 return AfterIP.takeError();
9123 Builder.restoreIP(*AfterIP);
9124 return Error::success();
9125 };
9126
9127 // Generate code for the closing of the data region.
9128 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9129 ArrayRef<BasicBlock *> DeallocBlocks) {
9130 TargetDataRTArgs RTArgs;
9131 Info.EmitDebug = !MapInfo->Names.empty();
9132 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9133
9134 // Emit the number of elements in the offloading arrays.
9135 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9136
9137 // Source location for the ident struct
9138 if (!SrcLocInfo) {
9139 uint32_t SrcLocStrSize;
9140 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9141 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9142 }
9143
9144 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9145 PointerNum, RTArgs.BasePointersArray,
9146 RTArgs.PointersArray, RTArgs.SizesArray,
9147 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9148 RTArgs.MappersArray};
9149 Function *EndMapperFunc =
9150 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9151
9152 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9153 return Error::success();
9154 };
9155
9156 // We don't have to do anything to close the region if the if clause evaluates
9157 // to false.
9158 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9159 ArrayRef<BasicBlock *> DeallocBlocks) {
9160 return Error::success();
9161 };
9162
9163 Error Err = [&]() -> Error {
9164 if (BodyGenCB) {
9165 Error Err = [&]() {
9166 if (IfCond)
9167 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9168 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9169 }();
9170
9171 if (Err)
9172 return Err;
9173
9174 // If we don't require privatization of device pointers, we emit the body
9175 // in between the runtime calls. This avoids duplicating the body code.
9176 InsertPointOrErrorTy AfterIP =
9177 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9178 if (!AfterIP)
9179 return AfterIP.takeError();
9180 restoreIPandDebugLoc(Builder, *AfterIP);
9181
9182 if (IfCond)
9183 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9184 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9185 }
9186 if (IfCond)
9187 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9188 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9189 }();
9190
9191 if (Err)
9192 return Err;
9193
9194 return Builder.saveIP();
9195}
9196
9199 bool IsGPUDistribute) {
9200 assert((IVSize == 32 || IVSize == 64) &&
9201 "IV size is not compatible with the omp runtime");
9202 RuntimeFunction Name;
9203 if (IsGPUDistribute)
9204 Name = IVSize == 32
9205 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9206 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9207 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9208 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9209 else
9210 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9211 : omp::OMPRTL___kmpc_for_static_init_4u)
9212 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9213 : omp::OMPRTL___kmpc_for_static_init_8u);
9214
9215 return getOrCreateRuntimeFunction(M, Name);
9216}
9217
9219 bool IVSigned) {
9220 assert((IVSize == 32 || IVSize == 64) &&
9221 "IV size is not compatible with the omp runtime");
9222 RuntimeFunction Name = IVSize == 32
9223 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9224 : omp::OMPRTL___kmpc_dispatch_init_4u)
9225 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9226 : omp::OMPRTL___kmpc_dispatch_init_8u);
9227
9228 return getOrCreateRuntimeFunction(M, Name);
9229}
9230
9232 bool IVSigned) {
9233 assert((IVSize == 32 || IVSize == 64) &&
9234 "IV size is not compatible with the omp runtime");
9235 RuntimeFunction Name = IVSize == 32
9236 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9237 : omp::OMPRTL___kmpc_dispatch_next_4u)
9238 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9239 : omp::OMPRTL___kmpc_dispatch_next_8u);
9240
9241 return getOrCreateRuntimeFunction(M, Name);
9242}
9243
9245 bool IVSigned) {
9246 assert((IVSize == 32 || IVSize == 64) &&
9247 "IV size is not compatible with the omp runtime");
9248 RuntimeFunction Name = IVSize == 32
9249 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9250 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9251 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9252 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9253
9254 return getOrCreateRuntimeFunction(M, Name);
9255}
9256
9258 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9259}
9260
9262 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9263 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9264
9265 DISubprogram *NewSP = Func->getSubprogram();
9266 if (!NewSP)
9267 return;
9268
9270
9271 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9272 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9273 // Only use cached variable if the arg number matches. This is important
9274 // so that DIVariable created for privatized variables are not discarded.
9275 if (NewVar && (arg == NewVar->getArg()))
9276 return NewVar;
9277
9279 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9280 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9281 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9282 return NewVar;
9283 };
9284
9285 auto UpdateDebugRecord = [&](auto *DR) {
9286 DILocalVariable *OldVar = DR->getVariable();
9287 unsigned ArgNo = 0;
9288 for (auto Loc : DR->location_ops()) {
9289 auto Iter = ValueReplacementMap.find(Loc);
9290 if (Iter != ValueReplacementMap.end()) {
9291 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9292 ArgNo = std::get<1>(Iter->second) + 1;
9293 }
9294 }
9295 if (ArgNo != 0)
9296 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9297 };
9298
9300 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9301 if (DVR->getNumVariableLocationOps() != 1u) {
9302 DVR->setKillLocation();
9303 return;
9304 }
9305 Value *Loc = DVR->getVariableLocationOp(0u);
9306 BasicBlock *CurBB = DVR->getParent();
9307 BasicBlock *RequiredBB = nullptr;
9308
9309 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9310 RequiredBB = LocInst->getParent();
9311 else if (isa<llvm::Argument>(Loc))
9312 RequiredBB = &DVR->getFunction()->getEntryBlock();
9313
9314 if (RequiredBB && RequiredBB != CurBB) {
9315 assert(!RequiredBB->empty());
9316 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9317 RequiredBB->back().getIterator());
9318 DVRsToDelete.push_back(DVR);
9319 }
9320 };
9321
9322 // The location and scope of variable records still point to
9323 // the parent function of the target region. Update them.
9324 for (Instruction &I : instructions(Func)) {
9325 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9326 UpdateDebugRecord(&DVR);
9327 MoveDebugRecordToCorrectBlock(&DVR);
9328 }
9329 }
9330 for (auto *DVR : DVRsToDelete)
9331 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9332 // An extra argument is passed to the device. Create the debug data for it.
9333 if (OMPBuilder.Config.isTargetDevice()) {
9334 DICompileUnit *CU = NewSP->getUnit();
9335 Module *M = Func->getParent();
9336 DIBuilder DB(*M, true, CU);
9337 DIType *VoidPtrTy =
9338 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9339 unsigned ArgNo = Func->arg_size();
9340 DILocalVariable *Var = DB.createParameterVariable(
9341 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9342 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9343 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9344 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9345 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9346 &(*Func->begin()));
9347 }
9348}
9349
9351 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9352 return cast<Operator>(V)->getOperand(0);
9353 return V;
9354}
9355
9357 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9359 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9362 DebugLoc OutlinedFnLoc) {
9363 SmallVector<Type *> ParameterTypes;
9364 if (OMPBuilder.Config.isTargetDevice()) {
9365 // All parameters to target devices are passed as pointers
9366 // or i64. This assumes 64-bit address spaces/pointers.
9367 for (auto &Arg : Inputs)
9368 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9369 ? Arg->getType()
9370 : Type::getInt64Ty(Builder.getContext()));
9371 } else {
9372 for (auto &Arg : Inputs)
9373 ParameterTypes.push_back(Arg->getType());
9374 }
9375
9376 // The implicit dyn_ptr argument is always the last parameter on both host
9377 // and device so the argument counts match without runtime manipulation.
9378 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9379 ParameterTypes.push_back(PtrTy);
9380
9381 auto BB = Builder.GetInsertBlock();
9382 auto M = BB->getModule();
9383 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9384 /*isVarArg*/ false);
9385 auto Func =
9386 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9387
9388 // Forward target-cpu and target-features function attributes from the
9389 // original function to the new outlined function.
9390 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9391
9392 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9393 if (TargetCpuAttr.isStringAttribute())
9394 Func->addFnAttr(TargetCpuAttr);
9395
9396 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9397 if (TargetFeaturesAttr.isStringAttribute())
9398 Func->addFnAttr(TargetFeaturesAttr);
9399
9400 if (OMPBuilder.Config.isTargetDevice()) {
9401 Value *ExecMode =
9402 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9403 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9404 }
9405
9406 // Save insert point.
9407 IRBuilder<>::InsertPointGuard IPG(Builder);
9408 // We will generate the entries in the outlined function but the debug
9409 // location is still pointing to the parent function, which is the wrong
9410 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9411 // position scoped to the subprogram that will be attached to the outlined
9412 // function, so it is what everything emitted below needs.
9413 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9414
9415 // Generate the region into the function.
9416 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9417 Builder.SetInsertPoint(EntryBB);
9418
9419 // Insert target init call in the device compilation pass. On the host
9420 // (e.g. a non-GPU offload target), there is no runtime init/deinit
9421 // sequence, but the runtime still needs a '<kernel>_kernel_environment'
9422 // global to know how the kernel was configured, so emit it directly.
9423 if (OMPBuilder.Config.isTargetDevice())
9424 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9425 else
9426 OMPBuilder.emitKernelEnvironment(Builder, DefaultAttrs);
9427
9428 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9429
9430 // As we embed the user code in the middle of our target region after we
9431 // generate entry code, we must move what allocas we can into the entry
9432 // block to avoid possible breaking optimisations
9434
9435 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9436 BasicBlock *OutlinedBodyBB =
9437 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9439 CBFunc(Builder.saveIP(), OutlinedBodyBB->begin(), ExitBB);
9440 if (!AfterIP)
9441 return AfterIP.takeError();
9442 Builder.SetInsertPoint(ExitBB);
9443 // The body callback builds the body with its own IRBuilder and cannot reach
9444 // this one directly. But a body holding another OpenMP construct, a nested
9445 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9446 // this Builder pointing at the wrong debug location, or at none at all. The
9447 // epilogue below belongs to the target construct rather than to whatever the
9448 // body emitted last, so re-establish the location the prologue was emitted
9449 // with.
9450 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9451
9452 // Insert target deinit call in the device compilation pass.
9453 if (OMPBuilder.Config.isTargetDevice())
9454 OMPBuilder.createTargetDeinit(Builder);
9455
9456 // Insert return instruction.
9457 Builder.CreateRetVoid();
9458
9459 // New Alloca IP at entry point of created device function.
9460 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9461 auto AllocaIP = Builder.saveIP();
9462
9463 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrPseudoProbe());
9464
9465 // Do not include the artificial dyn_ptr argument.
9466 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9467
9469
9470 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9471 // Things like GEP's can come in the form of Constants. Constants and
9472 // ConstantExpr's do not have access to the knowledge of what they're
9473 // contained in, so we must dig a little to find an instruction so we
9474 // can tell if they're used inside of the function we're outlining. We
9475 // also replace the original constant expression with a new instruction
9476 // equivalent; an instruction as it allows easy modification in the
9477 // following loop, as we can now know the constant (instruction) is
9478 // owned by our target function and replaceUsesOfWith can now be invoked
9479 // on it (cannot do this with constants it seems). A brand new one also
9480 // allows us to be cautious as it is perhaps possible the old expression
9481 // was used inside of the function but exists and is used externally
9482 // (unlikely by the nature of a Constant, but still).
9483 // NOTE: We cannot remove dead constants that have been rewritten to
9484 // instructions at this stage, we run the risk of breaking later lowering
9485 // by doing so as we could still be in the process of lowering the module
9486 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9487 // constants we have created rewritten versions of.
9488 if (auto *Const = dyn_cast<Constant>(Input))
9489 convertUsersOfConstantsToInstructions(Const, Func, false);
9490
9491 // Collect users before iterating over them to avoid invalidating the
9492 // iteration in case a user uses Input more than once (e.g. a call
9493 // instruction).
9494 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9495 // Collect all the instructions
9497 if (auto *Instr = dyn_cast<Instruction>(User))
9498 if (Instr->getFunction() == Func)
9499 Instr->replaceUsesOfWith(Input, InputCopy);
9500 };
9501
9502 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9503
9504 // Rewrite uses of input valus to parameters.
9505 for (auto InArg : zip(Inputs, ArgRange)) {
9506 Value *Input = std::get<0>(InArg);
9507 Argument &Arg = std::get<1>(InArg);
9508 Value *InputCopy = nullptr;
9509
9510 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9511 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(), ExitBB->begin());
9512 if (!AfterIP)
9513 return AfterIP.takeError();
9514 Builder.restoreIP(*AfterIP);
9515 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9516
9517 // In certain cases a Global may be set up for replacement, however, this
9518 // Global may be used in multiple arguments to the kernel, just segmented
9519 // apart, for example, if we have a global array, that is sectioned into
9520 // multiple mappings (technically not legal in OpenMP, but there is a case
9521 // in Fortran for Common Blocks where this is neccesary), we will end up
9522 // with GEP's into this array inside the kernel, that refer to the Global
9523 // but are technically separate arguments to the kernel for all intents and
9524 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9525 // index, it will fold into an referal to the Global, if we then encounter
9526 // this folded GEP during replacement all of the references to the
9527 // Global in the kernel will be replaced with the argument we have generated
9528 // that corresponds to it, including any other GEP's that refer to the
9529 // Global that may be other arguments. This will invalidate all of the other
9530 // preceding mapped arguments that refer to the same global that may be
9531 // separate segments. To prevent this, we defer global processing until all
9532 // other processing has been performed.
9535 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9536 continue;
9537 }
9538
9540 continue;
9541
9542 ReplaceValue(Input, InputCopy, Func);
9543 }
9544
9545 // Replace all of our deferred Input values, currently just Globals.
9546 for (auto Deferred : DeferredReplacement)
9547 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9548
9549 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9550 ValueReplacementMap);
9551 return Func;
9552}
9553/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9554/// of pointers containing shared data between the parent task and the created
9555/// task.
9557 IRBuilderBase &Builder,
9558 Value *TaskWithPrivates,
9559 Type *TaskWithPrivatesTy) {
9560
9561 Type *TaskTy = OMPIRBuilder.Task;
9562 LLVMContext &Ctx = Builder.getContext();
9563 Value *TaskT =
9564 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9565 Value *Shareds = TaskT;
9566 // TaskWithPrivatesTy can be one of the following
9567 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9568 // %struct.privates }
9569 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9570 //
9571 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9572 // its first member has to be the task descriptor. TaskTy is the type of the
9573 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9574 // first member of TaskT, gives us the pointer to shared data.
9575 if (TaskWithPrivatesTy != TaskTy)
9576 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9577 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9578}
9579/// Create an entry point for a target task with the following.
9580/// It'll have the following signature
9581/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9582/// This function is called from emitTargetTask once the
9583/// code to launch the target kernel has been outlined already.
9584/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9585/// into the task structure so that the deferred target task can access this
9586/// data even after the stack frame of the generating task has been rolled
9587/// back. Offloading arrays contain base pointers, pointers, sizes etc
9588/// of the data that the target kernel will access. These in effect are the
9589/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9591 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9592 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9593 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9594
9595 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9596 // This is because PrivatesTy is the type of the structure in which
9597 // we pass the offloading arrays to the deferred target task.
9598 assert((!NumOffloadingArrays || PrivatesTy) &&
9599 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9600 "to privatize");
9601
9602 Module &M = OMPBuilder.M;
9603 // KernelLaunchFunction is the target launch function, i.e.
9604 // the function that sets up kernel arguments and calls
9605 // __tgt_target_kernel to launch the kernel on the device.
9606 //
9607 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9608
9609 // StaleCI is the CallInst which is the call to the outlined
9610 // target kernel launch function. If there are local live-in values
9611 // that the outlined function uses then these are aggregated into a structure
9612 // which is passed as the second argument. If there are no local live-in
9613 // values or if all values used by the outlined kernel are global variables,
9614 // then there's only one argument, the threadID. So, StaleCI can be
9615 //
9616 // %structArg = alloca { ptr, ptr }, align 8
9617 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9618 // store ptr %20, ptr %gep_, align 8
9619 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9620 // store ptr %21, ptr %gep_8, align 8
9621 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9622 //
9623 // OR
9624 //
9625 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9626 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9627
9628 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9629 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9630 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9631
9632 auto ProxyFnTy =
9633 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9634 /* isVarArg */ false);
9635 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9636 ".omp_target_task_proxy_func", M);
9637 Value *ThreadId = ProxyFn->getArg(0);
9638 Value *TaskWithPrivates = ProxyFn->getArg(1);
9639 ThreadId->setName("thread.id");
9640 TaskWithPrivates->setName("task");
9641
9642 bool HasShareds = SharedArgsOperandNo > 0;
9643 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9644 IRBuilder<>::InsertPointGuard IPG(Builder);
9645 BasicBlock *EntryBB =
9646 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9647 Builder.SetInsertPoint(EntryBB);
9648 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9649
9650 SmallVector<Value *> KernelLaunchArgs;
9651 KernelLaunchArgs.reserve(StaleCI->arg_size());
9652 KernelLaunchArgs.push_back(ThreadId);
9653
9654 if (HasOffloadingArrays) {
9655 assert(TaskTy != TaskWithPrivatesTy &&
9656 "If there are offloading arrays to pass to the target"
9657 "TaskTy cannot be the same as TaskWithPrivatesTy");
9658 (void)TaskTy;
9659 Value *Privates =
9660 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9661 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9662 KernelLaunchArgs.push_back(
9663 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9664 }
9665
9666 if (HasShareds) {
9667 auto *ArgStructAlloca =
9668 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9669 assert(ArgStructAlloca &&
9670 "Unable to find the alloca instruction corresponding to arguments "
9671 "for extracted function");
9672 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9673 std::optional<TypeSize> ArgAllocSize =
9674 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9675 assert(ArgStructType && ArgAllocSize &&
9676 "Unable to determine size of arguments for extracted function");
9677 uint64_t StructSize = ArgAllocSize->getFixedValue();
9678
9679 AllocaInst *NewArgStructAlloca =
9680 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9681
9682 Value *SharedsSize = Builder.getInt64(StructSize);
9683
9685 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9686
9687 Builder.CreateMemCpy(
9688 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9689 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9690 KernelLaunchArgs.push_back(NewArgStructAlloca);
9691 }
9692 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9693 Builder.CreateRetVoid();
9694 return ProxyFn;
9695}
9697
9698 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9699 return GEP->getSourceElementType();
9700 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9701 return Alloca->getAllocatedType();
9702
9703 llvm_unreachable("Unhandled Instruction type");
9704 return nullptr;
9705}
9706// This function returns a struct that has at most two members.
9707// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9708// descriptor. The second member, if needed, is a struct containing arrays
9709// that need to be passed to the offloaded target kernel. For example,
9710// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9711// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9712// respectively, then the types created by this function are
9713//
9714// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9715// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9716// %struct.privates }
9717// %struct.task_with_privates is returned by this function.
9718// If there aren't any offloading arrays to pass to the target kernel,
9719// %struct.kmp_task_ompbuilder_t is returned.
9720static StructType *
9722 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9723
9724 if (OffloadingArraysToPrivatize.empty())
9725 return OMPIRBuilder.Task;
9726
9727 SmallVector<Type *, 4> StructFieldTypes;
9728 for (Value *V : OffloadingArraysToPrivatize) {
9729 assert(V->getType()->isPointerTy() &&
9730 "Expected pointer to array to privatize. Got a non-pointer value "
9731 "instead");
9732 Type *ArrayTy = getOffloadingArrayType(V);
9733 assert(ArrayTy && "ArrayType cannot be nullptr");
9734 StructFieldTypes.push_back(ArrayTy);
9735 }
9736 StructType *PrivatesStructTy =
9737 StructType::create(StructFieldTypes, "struct.privates");
9738 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9739 "struct.task_with_privates");
9740}
9742 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9743 TargetRegionEntryInfo &EntryInfo,
9745 Function *&OutlinedFn, Constant *&OutlinedFnID,
9749 DebugLoc OutlinedFnLoc) {
9750
9751 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9752 [&](StringRef EntryFnName) {
9753 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9754 EntryFnName, Inputs, CBFunc,
9755 ArgAccessorFuncCB, OutlinedFnLoc);
9756 };
9757
9758 return OMPBuilder.emitTargetRegionFunction(
9759 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9760 OutlinedFnID);
9761}
9762
9764 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9766 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9767 bool HasNoWait) {
9768
9769 // The following explains the code-gen scenario for the `target` directive. A
9770 // similar scneario is followed for other device-related directives (e.g.
9771 // `target enter data`) but in similar fashion since we only need to emit task
9772 // that encapsulates the proper runtime call.
9773 //
9774 // When we arrive at this function, the target region itself has been
9775 // outlined into the function OutlinedFn.
9776 // So at ths point, for
9777 // --------------------------------------------------------------
9778 // void user_code_that_offloads(...) {
9779 // omp target depend(..) map(from:a) map(to:b) private(i)
9780 // do i = 1, 10
9781 // a(i) = b(i) + n
9782 // }
9783 //
9784 // --------------------------------------------------------------
9785 //
9786 // we have
9787 //
9788 // --------------------------------------------------------------
9789 //
9790 // void user_code_that_offloads(...) {
9791 // %.offload_baseptrs = alloca [2 x ptr], align 8
9792 // %.offload_ptrs = alloca [2 x ptr], align 8
9793 // %.offload_mappers = alloca [2 x ptr], align 8
9794 // ;; target region has been outlined and now we need to
9795 // ;; offload to it via a target task.
9796 // }
9797 // void outlined_device_function(ptr a, ptr b, ptr n) {
9798 // n = *n_ptr;
9799 // do i = 1, 10
9800 // a(i) = b(i) + n
9801 // }
9802 //
9803 // We have to now do the following
9804 // (i) Make an offloading call to outlined_device_function using the OpenMP
9805 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9806 // emitted by emitKernelLaunch
9807 // (ii) Create a task entry point function that calls kernel_launch_function
9808 // and is the entry point for the target task. See
9809 // '@.omp_target_task_proxy_func in the pseudocode below.
9810 // (iii) Create a task with the task entry point created in (ii)
9811 //
9812 // That is we create the following
9813 // struct task_with_privates {
9814 // struct kmp_task_ompbuilder_t task_struct;
9815 // struct privates {
9816 // [2 x ptr] ; baseptrs
9817 // [2 x ptr] ; ptrs
9818 // [2 x i64] ; sizes
9819 // }
9820 // }
9821 // void user_code_that_offloads(...) {
9822 // %.offload_baseptrs = alloca [2 x ptr], align 8
9823 // %.offload_ptrs = alloca [2 x ptr], align 8
9824 // %.offload_sizes = alloca [2 x i64], align 8
9825 //
9826 // %structArg = alloca { ptr, ptr, ptr }, align 8
9827 // %strucArg[0] = a
9828 // %strucArg[1] = b
9829 // %strucArg[2] = &n
9830 //
9831 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
9832 // sizeof(kmp_task_ompbuilder_t),
9833 // sizeof(structArg),
9834 // @.omp_target_task_proxy_func,
9835 // ...)
9836 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
9837 // sizeof(structArg))
9838 // memcpy(target_task_with_privates->privates->baseptrs,
9839 // offload_baseptrs, sizeof(offload_baseptrs)
9840 // memcpy(target_task_with_privates->privates->ptrs,
9841 // offload_ptrs, sizeof(offload_ptrs)
9842 // memcpy(target_task_with_privates->privates->sizes,
9843 // offload_sizes, sizeof(offload_sizes)
9844 // dependencies_array = ...
9845 // ;; if nowait not present
9846 // call @__kmpc_omp_wait_deps(..., dependencies_array)
9847 // call @__kmpc_omp_task_begin_if0(...)
9848 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
9849 // %target_task_with_privates)
9850 // call @__kmpc_omp_task_complete_if0(...)
9851 // }
9852 //
9853 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
9854 // ptr %task) {
9855 // %structArg = alloca {ptr, ptr, ptr}
9856 // %task_ptr = getelementptr(%task, 0, 0)
9857 // %shared_data = load (getelementptr %task_ptr, 0, 0)
9858 // mempcy(%structArg, %shared_data, sizeof(%structArg))
9859 //
9860 // %offloading_arrays = getelementptr(%task, 0, 1)
9861 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
9862 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
9863 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
9864 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
9865 // %offload_sizes, %structArg)
9866 // }
9867 //
9868 // We need the proxy function because the signature of the task entry point
9869 // expected by kmpc_omp_task is always the same and will be different from
9870 // that of the kernel_launch function.
9871 //
9872 // kernel_launch_function is generated by emitKernelLaunch and has the
9873 // always_inline attribute. For this example, it'll look like so:
9874 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
9875 // %offload_sizes, %structArg) alwaysinline {
9876 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
9877 // ; load aggregated data from %structArg
9878 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
9879 // ; offload_sizes
9880 // call i32 @__tgt_target_kernel(...,
9881 // outlined_device_function,
9882 // ptr %kernel_args)
9883 // }
9884 // void outlined_device_function(ptr a, ptr b, ptr n) {
9885 // n = *n_ptr;
9886 // do i = 1, 10
9887 // a(i) = b(i) + n
9888 // }
9889 //
9890 BasicBlock *TargetTaskBodyBB =
9891 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
9892 BasicBlock *TargetTaskAllocaBB =
9893 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
9894
9895 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB->begin());
9896 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB->begin());
9897
9898 auto OI = std::make_unique<OutlineInfo>();
9899 OI->EntryBB = TargetTaskAllocaBB;
9900 OI->OuterAllocBB = AllocaIP.getNodeParent();
9901
9902 // Add the thread ID argument.
9904 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
9905 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
9906
9907 // Generate the task body which will subsequently be outlined.
9908 Builder.restoreIP(TargetTaskBodyIP);
9909 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
9910 return Err;
9911
9912 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
9913 // it is given. These blocks are enumerated by
9914 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
9915 // to be outside the region. In other words, OI.ExitBlock is expected to be
9916 // the start of the region after the outlining. We used to set OI.ExitBlock
9917 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
9918 // except when the task body is a single basic block. In that case,
9919 // OI.ExitBlock is set to the single task body block and will get left out of
9920 // the outlining process. So, simply create a new empty block to which we
9921 // uncoditionally branch from where TaskBodyCB left off
9922 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
9923 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
9924 /*IsFinished=*/true);
9925
9926 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
9927 bool NeedsTargetTask = HasNoWait && DeviceID;
9928 if (NeedsTargetTask) {
9929 for (auto *V :
9930 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
9931 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
9932 RTArgs.SizesArray}) {
9934 OffloadingArraysToPrivatize.push_back(V);
9935 OI->ExcludeArgsFromAggregate.push_back(V);
9936 }
9937 }
9938 }
9939 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
9940 DeviceID, OffloadingArraysToPrivatize](
9941 Function &OutlinedFn) mutable {
9942 assert(OutlinedFn.hasOneUse() &&
9943 "there must be a single user for the outlined function");
9944
9945 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
9946
9947 // The first argument of StaleCI is always the thread id.
9948 // The next few arguments are the pointers to offloading arrays
9949 // if any. (see OffloadingArraysToPrivatize)
9950 // Finally, all other local values that are live-in into the outlined region
9951 // end up in a structure whose pointer is passed as the last argument. This
9952 // piece of data is passed in the "shared" field of the task structure. So,
9953 // we know we have to pass shareds to the task if the number of arguments is
9954 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
9955 // thread id. Further, for safety, we assert that the number of arguments of
9956 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
9957 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
9958 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
9959 assert((!HasShareds ||
9960 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
9961 "Wrong number of arguments for StaleCI when shareds are present");
9962 int SharedArgOperandNo =
9963 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
9964
9965 StructType *TaskWithPrivatesTy =
9966 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
9967 StructType *PrivatesTy = nullptr;
9968
9969 if (!OffloadingArraysToPrivatize.empty())
9970 PrivatesTy =
9971 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
9972
9974 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
9975 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
9976
9977 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
9978 << "\n");
9979
9980 Builder.SetInsertPoint(StaleCI);
9981
9982 // Gather the arguments for emitting the runtime call.
9983 uint32_t SrcLocStrSize;
9984 Constant *SrcLocStr =
9986 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9987
9988 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
9989 //
9990 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
9991 // the DeviceID to the deferred task and also since
9992 // @__kmpc_omp_target_task_alloc creates an untied/async task.
9993 Function *TaskAllocFn =
9994 !NeedsTargetTask
9995 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
9997 OMPRTL___kmpc_omp_target_task_alloc);
9998
9999 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
10000 // call.
10001 Value *ThreadID = getOrCreateThreadID(Ident);
10002
10003 // Argument - `sizeof_kmp_task_t` (TaskSize)
10004 // Tasksize refers to the size in bytes of kmp_task_t data structure
10005 // plus any other data to be passed to the target task, if any, which
10006 // is packed into a struct. kmp_task_t and the struct so created are
10007 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
10008 Value *TaskSize = Builder.getInt64(
10009 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
10010
10011 // Argument - `sizeof_shareds` (SharedsSize)
10012 // SharedsSize refers to the shareds array size in the kmp_task_t data
10013 // structure.
10014 Value *SharedsSize = Builder.getInt64(0);
10015 if (HasShareds) {
10016 auto *ArgStructAlloca =
10017 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
10018 assert(ArgStructAlloca &&
10019 "Unable to find the alloca instruction corresponding to arguments "
10020 "for extracted function");
10021 std::optional<TypeSize> ArgAllocSize =
10022 ArgStructAlloca->getAllocationSize(M.getDataLayout());
10023 assert(ArgAllocSize &&
10024 "Unable to determine size of arguments for extracted function");
10025 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
10026 }
10027
10028 // Argument - `flags`
10029 // Task is tied iff (Flags & 1) == 1.
10030 // Task is untied iff (Flags & 1) == 0.
10031 // Task is final iff (Flags & 2) == 2.
10032 // Task is not final iff (Flags & 2) == 0.
10033 // A target task is not final and is untied.
10034 Value *Flags = Builder.getInt32(0);
10035
10036 // Emit the @__kmpc_omp_task_alloc runtime call
10037 // The runtime call returns a pointer to an area where the task captured
10038 // variables must be copied before the task is run (TaskData)
10039 CallInst *TaskData = nullptr;
10040
10041 SmallVector<llvm::Value *> TaskAllocArgs = {
10042 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
10043 /*flags=*/Flags,
10044 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
10045 /*task_func=*/ProxyFn};
10046
10047 if (NeedsTargetTask) {
10048 assert(DeviceID && "Expected non-empty device ID.");
10049 TaskAllocArgs.push_back(DeviceID);
10050 }
10051
10052 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
10053
10054 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
10055 if (HasShareds) {
10056 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
10058 *this, Builder, TaskData, TaskWithPrivatesTy);
10059 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
10060 SharedsSize);
10061 }
10062 if (!OffloadingArraysToPrivatize.empty()) {
10063 Value *Privates =
10064 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
10065 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
10066 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
10067 [[maybe_unused]] Type *ArrayType =
10068 getOffloadingArrayType(PtrToPrivatize);
10069 assert(ArrayType && "ArrayType cannot be nullptr");
10070
10071 Type *ElementType = PrivatesTy->getElementType(i);
10072 assert(ElementType == ArrayType &&
10073 "ElementType should match ArrayType");
10074 (void)ArrayType;
10075
10076 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
10077 Builder.CreateMemCpy(
10078 Dst, Alignment, PtrToPrivatize, Alignment,
10079 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
10080 }
10081 }
10082
10083 Value *DepArray = nullptr;
10084 Value *NumDeps = nullptr;
10085 if (Dependencies.DepArray) {
10086 DepArray = Dependencies.DepArray;
10087 NumDeps = Dependencies.NumDeps;
10088 } else if (!Dependencies.Deps.empty()) {
10089 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
10090 NumDeps = Builder.getInt32(Dependencies.Deps.size());
10091 }
10092
10093 // ---------------------------------------------------------------
10094 // V5.2 13.8 target construct
10095 // If the nowait clause is present, execution of the target task
10096 // may be deferred. If the nowait clause is not present, the target task is
10097 // an included task.
10098 // ---------------------------------------------------------------
10099 // The above means that the lack of a nowait on the target construct
10100 // translates to '#pragma omp task if(0)'
10101 if (!NeedsTargetTask) {
10102 if (DepArray) {
10103 Function *TaskWaitFn =
10104 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10106 TaskWaitFn,
10107 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10108 /*ndeps=*/NumDeps,
10109 /*dep_list=*/DepArray,
10110 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10111 /*noalias_dep_list=*/
10113 }
10114 // Included task.
10115 Function *TaskBeginFn =
10116 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10117 Function *TaskCompleteFn =
10118 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10119 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10120 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10121 CI->setDebugLoc(StaleCI->getDebugLoc());
10122 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10123 } else if (DepArray) {
10124 // HasNoWait - meaning the task may be deferred. Call
10125 // __kmpc_omp_task_with_deps if there are dependencies,
10126 // else call __kmpc_omp_task
10127 Function *TaskFn =
10128 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10130 TaskFn,
10131 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10132 ConstantInt::get(Builder.getInt32Ty(), 0),
10134 } else {
10135 // Emit the @__kmpc_omp_task runtime call to spawn the task
10136 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10137 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10138 }
10139
10140 Builder.ClearInsertionPoint();
10141 StaleCI->eraseFromParent();
10142 for (Instruction *I : llvm::reverse(ToBeDeleted))
10143 I->eraseFromParent();
10144 };
10145 addOutlineInfo(std::move(OI));
10146
10147 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10148 << *(Builder.GetInsertBlock()) << "\n");
10149 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10150 << *(Builder.getModule()) << "\n");
10151 return Builder.saveIP();
10152}
10153
10155 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10156 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10157 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10158 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10159 if (Error Err =
10160 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10161 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10162 return Err;
10163 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10164 return Error::success();
10165}
10166
10168 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride,
10173 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10177 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10178 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10179 // Generate a function call to the host fallback implementation of the target
10180 // region. This is called by the host when no offload entry was generated for
10181 // the target region and when the offloading call fails at runtime.
10182 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10184 Builder.restoreIP(IP);
10185 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10186 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10187 FallbackArgs.push_back(
10188 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10189 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10190 return Builder.saveIP();
10191 };
10192
10193 bool HasDependencies = !Dependencies.empty();
10194 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10195
10197
10198 auto TaskBodyCB =
10199 [&](Value *DeviceID, Value *RTLoc,
10200 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10201 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10202 // produce any.
10204 // emitKernelLaunch makes the necessary runtime call to offload the
10205 // kernel. We then outline all that code into a separate function
10206 // ('kernel_launch_function' in the pseudo code above). This function is
10207 // then called by the target task proxy function (see
10208 // '@.omp_target_task_proxy_func' in the pseudo code above)
10209 // "@.omp_target_task_proxy_func' is generated by
10210 // emitTargetTaskProxyFunction.
10211 if (OutlinedFnID && DeviceID)
10212 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10213 EmitTargetCallFallbackCB, KArgs,
10214 DeviceID, RTLoc, TargetTaskAllocaIP);
10215
10216 // We only need to do the outlining if `DeviceID` is set to avoid calling
10217 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10218 // generating the `else` branch of an `if` clause.
10219 //
10220 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10221 // In this case, we execute the host implementation directly.
10222 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10223 }());
10224
10225 OMPBuilder.Builder.restoreIP(AfterIP);
10226 return Error::success();
10227 };
10228
10229 auto &&EmitTargetCallElse =
10230 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10232 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10233 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10234 // produce any.
10236 if (RequiresOuterTargetTask) {
10237 // Arguments that are intended to be directly forwarded to an
10238 // emitKernelLaunch call are pased as nullptr, since
10239 // OutlinedFnID=nullptr results in that call not being done.
10241 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10242 /*RTLoc=*/nullptr, AllocaIP,
10243 Dependencies, EmptyRTArgs, HasNoWait);
10244 }
10245 return EmitTargetCallFallbackCB(Builder.saveIP());
10246 }());
10247
10248 Builder.restoreIP(AfterIP);
10249 return Error::success();
10250 };
10251
10252 auto &&EmitTargetCallThen =
10253 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10255 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10256 Info.HasNoWait = HasNoWait;
10257 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10258
10260 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10261 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10262 /*IsNonContiguous=*/true,
10263 /*ForEndCall=*/false))
10264 return Err;
10265
10266 SmallVector<Value *, 3> NumTeamsC;
10267 for (auto [DefaultVal, RuntimeVal] :
10268 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10269 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10270 : Builder.getInt32(DefaultVal));
10271
10272 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10273 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10274 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10275 if (Clause)
10276 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10277 /*isSigned=*/false);
10278 return Clause;
10279 };
10280 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10281 if (Clause)
10282 Result =
10283 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10284 Result, Clause)
10285 : Clause;
10286 };
10287
10288 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10289 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10290 SmallVector<Value *, 3> NumThreadsC;
10291 Value *MaxThreadsClause =
10292 RuntimeAttrs.TeamsThreadLimit.size() == 1
10293 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10294 : nullptr;
10295
10296 for (auto [TeamsVal, TargetVal] : zip_equal(
10297 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10298 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10299 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10300
10301 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10302 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10303
10304 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10305 }
10306
10307 unsigned NumTargetItems = Info.NumberOfPtrs;
10308 Value *RTLoc = RTLocOverride;
10309 if (!RTLoc) {
10310 uint32_t SrcLocStrSize;
10311 Constant *SrcLocStr =
10312 OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10313 RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10314 llvm::omp::IdentFlag(0), 0);
10315 }
10316
10317 Value *TripCount = RuntimeAttrs.LoopTripCount
10318 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10319 Builder.getInt64Ty(),
10320 /*isSigned=*/false)
10321 : Builder.getInt64(0);
10322
10323 // Request zero groupprivate bytes by default.
10324 if (!DynCGroupMem)
10325 DynCGroupMem = Builder.getInt32(0);
10326
10328 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10329 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10330 DynCGroupMemFallback);
10331
10332 // Assume no error was returned because TaskBodyCB and
10333 // EmitTargetCallFallbackCB don't produce any.
10335 // The presence of certain clauses on the target directive require the
10336 // explicit generation of the target task.
10337 if (RequiresOuterTargetTask)
10338 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10339 RTLoc, AllocaIP, Dependencies,
10340 KArgs.RTArgs, Info.HasNoWait);
10341
10342 return OMPBuilder.emitKernelLaunch(
10343 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10344 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10345 }());
10346
10347 Builder.restoreIP(AfterIP);
10348 return Error::success();
10349 };
10350
10351 // If we don't have an ID for the target region, it means an offload entry
10352 // wasn't created. In this case we just run the host fallback directly and
10353 // ignore any potential 'if' clauses.
10354 if (!OutlinedFnID)
10355 return EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks);
10356
10357 // If there's no 'if' clause, only generate the kernel launch code path.
10358 if (!IfCond)
10359 return EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks);
10360
10361 return OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen, EmitTargetCallElse,
10362 AllocaIP);
10363}
10364
10366 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10367 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10368 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10369 const TargetKernelDefaultAttrs &DefaultAttrs,
10370 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10371 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10374 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10375 bool HasNowait, Value *DynCGroupMem,
10376 OMPDynGroupprivateFallbackType DynCGroupMemFallback, DebugLoc OutlinedFnLoc,
10377 Value *RTLocOverride) {
10378
10379 if (!updateToLocation(Loc))
10380 return InsertPointTy();
10381
10382 Builder.restoreIP(CodeGenIP);
10383
10384 Function *OutlinedFn;
10385 Constant *OutlinedFnID = nullptr;
10386 // The target region is outlined into its own function. The LLVM IR for
10387 // the target region itself is generated using the callbacks CBFunc
10388 // and ArgAccessorFuncCB
10390 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10391 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10392 return Err;
10393
10394 // If we are not on the target device, then we need to generate code
10395 // to make a remote call (offload) to the previously outlined function
10396 // that represents the target region. Do that now.
10397 if (!Config.isTargetDevice()) {
10398 if (Error Err = emitTargetCall(
10399 *this, Builder, RTLocOverride, AllocaIP, DeallocBlocks, Info,
10400 DefaultAttrs, RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID,
10401 Inputs, GenMapInfoCB, CustomMapperCB, Dependencies, HasNowait,
10402 DynCGroupMem, DynCGroupMemFallback))
10403 return Err;
10404 }
10405 return Builder.saveIP();
10406}
10407
10408std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10409 StringRef FirstSeparator,
10410 StringRef Separator) {
10411 SmallString<128> Buffer;
10412 llvm::raw_svector_ostream OS(Buffer);
10413 StringRef Sep = FirstSeparator;
10414 for (StringRef Part : Parts) {
10415 OS << Sep << Part;
10416 Sep = Separator;
10417 }
10418 return OS.str().str();
10419}
10420
10421std::string
10423 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10424 Config.separator());
10425}
10426
10428 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10429 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10430 if (Elem.second) {
10431 assert(Elem.second->getValueType() == Ty &&
10432 "OMP internal variable has different type than requested");
10433 } else {
10434 // TODO: investigate the appropriate linkage type used for the global
10435 // variable for possibly changing that to internal or private, or maybe
10436 // create different versions of the function for different OMP internal
10437 // variables.
10438 const DataLayout &DL = M.getDataLayout();
10439 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10440 // default global AS is 1.
10441 // See double-target-call-with-declare-target.f90 and
10442 // declare-target-vars-in-target-region.f90 libomptarget
10443 // tests.
10444 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10445 : M.getTargetTriple().isAMDGPU()
10446 ? 0
10447 : DL.getDefaultGlobalsAddressSpace();
10448 auto Linkage = this->M.getTargetTriple().isWasm()
10451 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10452 Constant::getNullValue(Ty), Elem.first(),
10453 /*InsertBefore=*/nullptr,
10454 GlobalValue::NotThreadLocal, AddressSpaceVal);
10455 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10456 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10457 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10458 Elem.second = GV;
10459 }
10460
10461 return Elem.second;
10462}
10463
10464Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10465 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10466 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10467 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10468}
10469
10471 LLVMContext &Ctx = Builder.getContext();
10472 Value *Null =
10473 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10474 Value *SizeGep =
10475 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10476 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10477 return SizePtrToInt;
10478}
10479
10482 std::string VarName) {
10483 llvm::Constant *MaptypesArrayInit =
10484 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10485 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10486 M, MaptypesArrayInit->getType(),
10487 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10488 VarName);
10489 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10490 return MaptypesArrayGlobal;
10491}
10492
10494 InsertPointTy AllocaIP,
10495 unsigned NumOperands,
10496 struct MapperAllocas &MapperAllocas) {
10497 if (!updateToLocation(Loc))
10498 return;
10499
10500 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10501 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10502 Builder.restoreIP(AllocaIP);
10503 AllocaInst *ArgsBase = Builder.CreateAlloca(
10504 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10505 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10506 ".offload_ptrs");
10507 AllocaInst *ArgSizes = Builder.CreateAlloca(
10508 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10510 MapperAllocas.ArgsBase = ArgsBase;
10511 MapperAllocas.Args = Args;
10512 MapperAllocas.ArgSizes = ArgSizes;
10513}
10514
10516 Function *MapperFunc, Value *SrcLocInfo,
10517 Value *MaptypesArg, Value *MapnamesArg,
10519 int64_t DeviceID, unsigned NumOperands) {
10520 if (!updateToLocation(Loc))
10521 return;
10522
10523 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10524 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10525 Value *ArgsBaseGEP =
10526 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10527 {Builder.getInt32(0), Builder.getInt32(0)});
10528 Value *ArgsGEP =
10529 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10530 {Builder.getInt32(0), Builder.getInt32(0)});
10531 Value *ArgSizesGEP =
10532 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10533 {Builder.getInt32(0), Builder.getInt32(0)});
10534 Value *NullPtr =
10535 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10536 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10537 Builder.getInt32(NumOperands),
10538 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10539 MaptypesArg, MapnamesArg, NullPtr});
10540}
10541
10543 TargetDataRTArgs &RTArgs,
10544 TargetDataInfo &Info,
10545 bool ForEndCall) {
10546 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10547 "expected region end call to runtime only when end call is separate");
10548 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10549 auto VoidPtrTy = UnqualPtrTy;
10550 auto VoidPtrPtrTy = UnqualPtrTy;
10551 auto Int64Ty = Type::getInt64Ty(M.getContext());
10552 auto Int64PtrTy = UnqualPtrTy;
10553
10554 if (!Info.NumberOfPtrs) {
10555 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10556 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10557 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10558 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10559 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10560 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10561 return;
10562 }
10563
10564 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10565 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10566 Info.RTArgs.BasePointersArray,
10567 /*Idx0=*/0, /*Idx1=*/0);
10568 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10569 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10570 /*Idx0=*/0,
10571 /*Idx1=*/0);
10572 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10573 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10574 /*Idx0=*/0, /*Idx1=*/0);
10575 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10576 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10577 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10578 : Info.RTArgs.MapTypesArray,
10579 /*Idx0=*/0,
10580 /*Idx1=*/0);
10581
10582 // Only emit the mapper information arrays if debug information is
10583 // requested.
10584 if (!Info.EmitDebug)
10585 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10586 else
10587 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10588 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10589 /*Idx0=*/0,
10590 /*Idx1=*/0);
10591 // If there is no user-defined mapper, set the mapper array to nullptr to
10592 // avoid an unnecessary data privatization
10593 if (!Info.HasMapper)
10594 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10595 else
10596 RTArgs.MappersArray =
10597 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10598}
10599
10601 InsertPointTy CodeGenIP,
10602 MapInfosTy &CombinedInfo,
10603 TargetDataInfo &Info) {
10605 CombinedInfo.NonContigInfo;
10606
10607 // Build an array of struct descriptor_dim and then assign it to
10608 // offload_args.
10609 //
10610 // struct descriptor_dim {
10611 // uint64_t offset;
10612 // uint64_t count;
10613 // uint64_t stride
10614 // };
10615 Type *Int64Ty = Builder.getInt64Ty();
10617 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10618 "struct.descriptor_dim");
10619
10620 enum { OffsetFD = 0, CountFD, StrideFD };
10621 // We need two index variable here since the size of "Dims" is the same as
10622 // the size of Components, however, the size of offset, count, and stride is
10623 // equal to the size of base declaration that is non-contiguous.
10624 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10625 // Skip emitting ir if dimension size is 1 since it cannot be
10626 // non-contiguous.
10627 if (NonContigInfo.Dims[I] == 1)
10628 continue;
10629 Builder.restoreIP(AllocaIP);
10630 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10631 AllocaInst *DimsAddr =
10632 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10633 Builder.restoreIP(CodeGenIP);
10634 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10635 unsigned RevIdx = EE - II - 1;
10636 Value *DimsLVal = Builder.CreateInBoundsGEP(
10637 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10638 // Offset
10639 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10640 Builder.CreateAlignedStore(
10641 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10642 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10643 // Count
10644 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10645 Builder.CreateAlignedStore(
10646 NonContigInfo.Counts[L][RevIdx], CountLVal,
10647 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10648 // Stride
10649 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10650 Builder.CreateAlignedStore(
10651 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10652 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10653 }
10654 // args[I] = &dims
10655 Builder.restoreIP(CodeGenIP);
10656 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10657 DimsAddr, Builder.getPtrTy());
10658 Value *P = Builder.CreateConstInBoundsGEP2_32(
10659 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10660 Info.RTArgs.PointersArray, 0, I);
10661 Builder.CreateAlignedStore(
10662 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10663 ++L;
10664 }
10665}
10666
10667void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10668 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10669 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10670 BasicBlock *ExitBB, bool IsInit) {
10671 StringRef Prefix = IsInit ? ".init" : ".del";
10672
10673 // Evaluate if this is an array section.
10675 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10676 Value *IsArray =
10677 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10678 Value *DeleteBit = Builder.CreateAnd(
10679 MapType,
10680 Builder.getInt64(
10681 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10682 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10683 Value *DeleteCond;
10684 Value *Cond;
10685 if (IsInit) {
10686 // base != begin?
10687 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10688 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10689 DeleteCond = Builder.CreateIsNull(
10690 DeleteBit,
10691 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10692 } else {
10693 Cond = IsArray;
10694 DeleteCond = Builder.CreateIsNotNull(
10695 DeleteBit,
10696 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10697 }
10698 Cond = Builder.CreateAnd(Cond, DeleteCond);
10699 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10700
10701 emitBlock(BodyBB, MapperFn);
10702 // Get the array size by multiplying element size and element number (i.e., \p
10703 // Size).
10704 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10705 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10706 // memory allocation/deletion purpose only.
10707 Value *MapTypeArg = Builder.CreateAnd(
10708 MapType,
10709 Builder.getInt64(
10710 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10711 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10712 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10713 MapTypeArg = Builder.CreateOr(
10714 MapTypeArg,
10715 Builder.getInt64(
10716 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10717 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10718
10719 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10720 // data structure.
10721 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10722 ArraySize, MapTypeArg, MapName};
10724 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10725 OffloadingArgs);
10726}
10727
10730 llvm::Value *BeginArg)>
10731 GenMapInfoCB,
10732 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10733 bool PreserveMemberOfFlags, bool PropagatePresentToPointeeStorage,
10734 bool PropagatePresentToNonPointeeStorage) {
10735 SmallVector<Type *> Params;
10736 Params.emplace_back(Builder.getPtrTy());
10737 Params.emplace_back(Builder.getPtrTy());
10738 Params.emplace_back(Builder.getPtrTy());
10739 Params.emplace_back(Builder.getInt64Ty());
10740 Params.emplace_back(Builder.getInt64Ty());
10741 Params.emplace_back(Builder.getPtrTy());
10742
10743 auto *FnTy =
10744 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10745
10746 Function *MapperFn =
10748 MapperFn->addFnAttr(Attribute::NoInline);
10749 MapperFn->addFnAttr(Attribute::NoUnwind);
10750 MapperFn->addParamAttr(0, Attribute::NoUndef);
10751 MapperFn->addParamAttr(1, Attribute::NoUndef);
10752 MapperFn->addParamAttr(2, Attribute::NoUndef);
10753 MapperFn->addParamAttr(3, Attribute::NoUndef);
10754 MapperFn->addParamAttr(4, Attribute::NoUndef);
10755 MapperFn->addParamAttr(5, Attribute::NoUndef);
10756
10757 // Start the mapper function code generation.
10758 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10760 Builder.SetInsertPoint(EntryBB);
10761 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10762
10763 Value *MapperHandle = MapperFn->getArg(0);
10764 Value *BaseIn = MapperFn->getArg(1);
10765 Value *BeginIn = MapperFn->getArg(2);
10766 Value *Size = MapperFn->getArg(3);
10767 Value *MapType = MapperFn->getArg(4);
10768 Value *MapName = MapperFn->getArg(5);
10769
10770 // Compute the starting and end addresses of array elements.
10771 // Prepare common arguments for array initiation and deletion.
10772 // Convert the size in bytes into the number of array elements.
10773 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10774 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10775 Value *PtrBegin = BeginIn;
10776 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10777
10778 // Emit array initiation if this is an array section and \p MapType indicates
10779 // that memory allocation is required.
10780 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10781 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10782 MapType, MapName, ElementSize, HeadBB,
10783 /*IsInit=*/true);
10784
10785 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10786
10787 // Emit the loop header block.
10788 emitBlock(HeadBB, MapperFn);
10789 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10790 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10791 // Evaluate whether the initial condition is satisfied.
10792 Value *IsEmpty =
10793 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10794 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10795
10796 // Emit the loop body block.
10797 emitBlock(BodyBB, MapperFn);
10798 BasicBlock *LastBB = BodyBB;
10799 PHINode *PtrPHI =
10800 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10801 PtrPHI->addIncoming(PtrBegin, HeadBB);
10802
10803 // Get map clause information. Fill up the arrays with all mapped variables.
10804 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10805 if (!Info)
10806 return Info.takeError();
10807
10808 // Call the runtime API __tgt_mapper_num_components to get the number of
10809 // pre-existing components.
10810 Value *OffloadingArgs[] = {MapperHandle};
10811 Value *PreviousSize = createRuntimeFunctionCall(
10812 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
10813 OffloadingArgs);
10814 Value *ShiftedPreviousSize =
10815 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
10816
10817 // Fill up the runtime mapper handle for all components.
10818 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
10819 Value *CurBaseArg = Info->BasePointers[I];
10820 Value *CurBeginArg = Info->Pointers[I];
10821 Value *CurSizeArg = Info->Sizes[I];
10822 Value *CurNameArg = Info->Names.size()
10823 ? Info->Names[I]
10824 : Constant::getNullValue(Builder.getPtrTy());
10825
10826 Value *OriMapType = Builder.getInt64(
10827 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10828 Info->Types[I]));
10829 auto RawType =
10830 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10831 Info->Types[I]);
10832 constexpr uint64_t MemberOfMask =
10833 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
10834 constexpr uint64_t AttachBit =
10835 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10836 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
10837 constexpr uint64_t PtrAndObjBit =
10838 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10839 OpenMPOffloadMappingFlags::OMP_MAP_PTR_AND_OBJ);
10840
10841 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
10842 // current array element (N = __tgt_mapper_num_components() at loop body
10843 // start).
10844 //
10845 // Example 1:
10846 // struct S { int x; int *p; };
10847 //
10848 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
10849 // use: S arr[2]; ... map(arr)
10850 // entries per element:
10851 //
10852 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
10853 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
10854 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
10855 //
10856 // Example 2:
10857 // struct S1 { int x; int y; };
10858 // struct S2 { int z; S1 *s1p; };
10859 //
10860 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
10861 // s2.s1p->y)
10862 // use: S2 arr[2]; ... map(arr)
10863 // entries per element:
10864 //
10865 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
10866 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
10867 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10868 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
10869 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
10870 //
10871 // x/y carry inner MEMBER_OF(2)
10872 // which is shifted by N to become MEMBER_OF(N+2).
10873 //
10874 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
10875 // the combined ALLOC entry for the s1p->x..y block, and the individual
10876 // x/y entries that are MEMBER_OF that block, all describe storage
10877 // reached through the attach ptr arr[i].s1p.
10878 //
10879 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
10880 // linking them to the parent struct:
10881 //
10882 // * (*) Entries with HasAttachPtr: they represent pointee data that
10883 // occupies a different storage block than the struct being mapped, so
10884 // they are not a member of it. They may still be MEMBER_OF an entry
10885 // within that pointee block, in which case those pre-existing bits are
10886 // shifted -- see (***).
10887 // * (**) ATTACH entries: they are not a member of anything — they just
10888 // link a ptr to its ptee.
10889 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
10890 // its pre-shaped entries already carry their final MEMBER_OF bits.
10891 // TODO: set HasAttachPtr from Flang for entries whose storage is the
10892 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
10893 // it.
10894 //
10895 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
10896 // s1p->x/y entries above), those bits are still shifted by N.
10897 Value *MemberMapType;
10898 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
10899 Info->HasAttachPtr[I]) {
10900 if (RawType & MemberOfMask)
10901 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10902 else
10903 MemberMapType = OriMapType;
10904 } else {
10905 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
10906 }
10907
10908 // Combine the map type inherited from user-defined mapper with that
10909 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
10910 // bits of the \a MapType, which is the input argument of the mapper
10911 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
10912 // bits of MemberMapType.
10913 // [OpenMP 5.0], 1.2.6. map-type decay.
10914 // | alloc | to | from | tofrom | release | delete
10915 // ----------------------------------------------------------
10916 // alloc | alloc | alloc | alloc | alloc | release | delete
10917 // to | alloc | to | alloc | to | release | delete
10918 // from | alloc | alloc | from | from | release | delete
10919 // tofrom | alloc | to | from | tofrom | release | delete
10920 Value *LeftToFrom = Builder.CreateAnd(
10921 MapType,
10922 Builder.getInt64(
10923 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10924 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10925 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10926 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
10927 BasicBlock *AllocElseBB =
10928 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
10929 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
10930 BasicBlock *ToElseBB =
10931 BasicBlock::Create(M.getContext(), "omp.type.to.else");
10932 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
10933 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
10934 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
10935 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
10936 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
10937 emitBlock(AllocBB, MapperFn);
10938 Value *AllocMapType = Builder.CreateAnd(
10939 MemberMapType,
10940 Builder.getInt64(
10941 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10942 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10943 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10944 Builder.CreateBr(EndBB);
10945 emitBlock(AllocElseBB, MapperFn);
10946 Value *IsTo = Builder.CreateICmpEQ(
10947 LeftToFrom,
10948 Builder.getInt64(
10949 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10950 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10951 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
10952 // In case of to, clear OMP_MAP_FROM.
10953 emitBlock(ToBB, MapperFn);
10954 Value *ToMapType = Builder.CreateAnd(
10955 MemberMapType,
10956 Builder.getInt64(
10957 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10958 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10959 Builder.CreateBr(EndBB);
10960 emitBlock(ToElseBB, MapperFn);
10961 Value *IsFrom = Builder.CreateICmpEQ(
10962 LeftToFrom,
10963 Builder.getInt64(
10964 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10965 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10966 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
10967 // In case of from, clear OMP_MAP_TO.
10968 emitBlock(FromBB, MapperFn);
10969 Value *FromMapType = Builder.CreateAnd(
10970 MemberMapType,
10971 Builder.getInt64(
10972 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10973 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
10974 // In case of tofrom, do nothing.
10975 emitBlock(EndBB, MapperFn);
10976 LastBB = EndBB;
10977 PHINode *CurMapType =
10978 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
10979 CurMapType->addIncoming(AllocMapType, AllocBB);
10980 CurMapType->addIncoming(ToMapType, ToBB);
10981 CurMapType->addIncoming(FromMapType, FromBB);
10982 CurMapType->addIncoming(MemberMapType, ToElseBB);
10983
10984 // Propagate map-type-modifying bits from the outer map clause to each map
10985 // inserted by the mapper.
10986 //
10987 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
10988 // list item from the map clause and to apply the clauses specified in the
10989 // declared mapper to the construct on which the map clause appears...
10990 // If any modifier with the map-type-modifying property appears in the map
10991 // clause then the effect is as if that modifier appears in each map clause
10992 // specified in the declared mapper.
10993 //
10994 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
10995 //
10996 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
10997 //
10998 // PRESENT propagation is controlled independently for entries describing
10999 // pointee storage (HasAttachPtr) and entries describing non-pointee
11000 // storage. This lets frontends select current Clang behavior (pointee only)
11001 // or OpenMP 6.x behavior for mapper propagation (all eligible storage).
11002 // PRESENT is not propagated to ATTACH or PTR_AND_OBJ entries.
11003 //
11004 // Pointee storage is gated on \p PropagatePresentToPointeeStorage (set by
11005 // caller only for OpenMP >= 6.0). Before 6.0 the present modifier is
11006 // treated as not applying to the pointee: the spec committee confirmed the
11007 // divergence between the present "motion" modifier (to/from) and the
11008 // present map-type modifier (map) was unintentional, to be fixed as an
11009 // OpenMP 6.0 erratum, so for 5.2 present is ignored for the pointee for
11010 // both map and to/from.
11011 //
11012 // Non-pointee storage is gated on \p PropagatePresentToNonPointeeStorage:
11013 // PRESENT is ten propagated to the struct's own members (e.g. the s.x,
11014 // s.y of map(present, mapper(id): s)) so that an absent member triggers
11015 // the present-check.
11016 //
11017 // Present cannot be mapped to members with PTR_AND_OBJ, a single combined
11018 // entry allocates the whole struct (including the pointer's storage),
11019 // so propagating PRESENT to it would wrongly require the pointer's
11020 // pointee to be present.
11021 uint64_t ModifierBits =
11022 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11023 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
11024 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
11025 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
11026 bool IsPresentEligibleStorage = !(RawType & (AttachBit | PtrAndObjBit));
11027 bool PropagatePresent =
11028 IsPresentEligibleStorage &&
11029 (Info->HasAttachPtr[I] ? PropagatePresentToPointeeStorage
11030 : PropagatePresentToNonPointeeStorage);
11031 if (PropagatePresent)
11032 ModifierBits |=
11033 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11034 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11035 Value *ImportedModifierBits =
11036 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
11037 Value *CurMapTypeWithModifiers = Builder.CreateOr(
11038 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
11039
11040 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
11041 // reserved for the attach(always) map-type modifier, and other modifier
11042 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
11043 Value *FinalMapType =
11044 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
11045
11046 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
11047 CurSizeArg, FinalMapType, CurNameArg};
11048
11049 auto ChildMapperFn = CustomMapperCB(I);
11050 if (!ChildMapperFn)
11051 return ChildMapperFn.takeError();
11052 if (*ChildMapperFn) {
11053 // Call the corresponding mapper function.
11054 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
11055 ->setDoesNotThrow();
11056 } else {
11057 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
11058 // data structure.
11060 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
11061 OffloadingArgs);
11062 }
11063 }
11064
11065 // Update the pointer to point to the next element that needs to be mapped,
11066 // and check whether we have mapped all elements.
11067 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
11068 "omp.arraymap.next");
11069 PtrPHI->addIncoming(PtrNext, LastBB);
11070 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
11071 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
11072 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
11073
11074 emitBlock(ExitBB, MapperFn);
11075 // Emit array deletion if this is an array section and \p MapType indicates
11076 // that deletion is required.
11077 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
11078 MapType, MapName, ElementSize, DoneBB,
11079 /*IsInit=*/false);
11080
11081 // Emit the function exit block.
11082 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
11083
11084 Builder.CreateRetVoid();
11085 return MapperFn;
11086}
11087
11089 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
11090 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
11091 bool IsNonContiguous,
11092 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
11093
11094 // Reset the array information.
11095 Info.clearArrayInfo();
11096 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
11097
11098 if (Info.NumberOfPtrs == 0)
11099 return Error::success();
11100
11101 Builder.restoreIP(AllocaIP);
11102 // Detect if we have any capture size requiring runtime evaluation of the
11103 // size so that a constant array could be eventually used.
11104 ArrayType *PointerArrayType =
11105 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
11106
11107 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
11108 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
11109
11110 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11111 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11112 AllocaInst *MappersArray = Builder.CreateAlloca(
11113 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11114 Info.RTArgs.MappersArray = MappersArray;
11115
11116 // If we don't have any VLA types or other types that require runtime
11117 // evaluation, we can use a constant array for the map sizes, otherwise we
11118 // need to fill up the arrays as we do for the pointers.
11119 Type *Int64Ty = Builder.getInt64Ty();
11120 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11121 ConstantInt::get(Int64Ty, 0));
11122 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11123 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11124 bool IsNonContigEntry =
11125 IsNonContiguous &&
11126 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11127 CombinedInfo.Types[I] &
11128 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11129 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11130 // descriptor_dim records), not the byte size.
11131 if (IsNonContigEntry) {
11132 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11133 "Index must be in-bounds for NON_CONTIG Dims array");
11134 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11135 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11136 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11137 continue;
11138 }
11139 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11140 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11141 ConstSizes[I] = CI;
11142 continue;
11143 }
11144 }
11145 RuntimeSizes.set(I);
11146 }
11147
11148 if (RuntimeSizes.all()) {
11149 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11150 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11151 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11152 restoreIPandDebugLoc(Builder, CodeGenIP);
11153 } else {
11154 auto *SizesArrayInit = ConstantArray::get(
11155 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11156 std::string Name = createPlatformSpecificName({"offload_sizes"});
11157 auto *SizesArrayGbl =
11158 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11159 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11160 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11161
11162 if (!RuntimeSizes.any()) {
11163 Info.RTArgs.SizesArray = SizesArrayGbl;
11164 } else {
11165 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11166 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11167 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11168 AllocaInst *Buffer = Builder.CreateAlloca(
11169 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11170 Buffer->setAlignment(OffloadSizeAlign);
11171 restoreIPandDebugLoc(Builder, CodeGenIP);
11172 Builder.CreateMemCpy(
11173 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11174 SizesArrayGbl, OffloadSizeAlign,
11175 Builder.getIntN(
11176 IndexSize,
11177 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11178
11179 Info.RTArgs.SizesArray = Buffer;
11180 }
11181 restoreIPandDebugLoc(Builder, CodeGenIP);
11182 }
11183
11184 // The map types are always constant so we don't need to generate code to
11185 // fill arrays. Instead, we create an array constant.
11187 for (auto mapFlag : CombinedInfo.Types)
11188 Mapping.push_back(
11189 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11190 mapFlag));
11191 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11192 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11193 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11194
11195 // The information types are only built if provided.
11196 if (!CombinedInfo.Names.empty()) {
11197 auto *MapNamesArrayGbl = createOffloadMapnames(
11198 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11199 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11200 Info.EmitDebug = true;
11201 } else {
11202 Info.RTArgs.MapNamesArray =
11204 Info.EmitDebug = false;
11205 }
11206
11207 // If there's a present map type modifier, it must not be applied to the end
11208 // of a region, so generate a separate map type array in that case.
11209 if (Info.separateBeginEndCalls()) {
11210 bool EndMapTypesDiffer = false;
11211 for (uint64_t &Type : Mapping) {
11212 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11213 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11214 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11215 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11216 EndMapTypesDiffer = true;
11217 }
11218 }
11219 if (EndMapTypesDiffer) {
11220 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11221 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11222 }
11223 }
11224
11225 PointerType *PtrTy = Builder.getPtrTy();
11226 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11227 Value *BPVal = CombinedInfo.BasePointers[I];
11228 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11229 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11230 0, I);
11231 Builder.CreateAlignedStore(BPVal, BP,
11232 M.getDataLayout().getPrefTypeAlign(PtrTy));
11233
11234 if (Info.requiresDevicePointerInfo()) {
11235 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11236 CodeGenIP = Builder.saveIP();
11237 Builder.restoreIP(AllocaIP);
11238 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11239 restoreIPandDebugLoc(Builder, CodeGenIP);
11240 if (DeviceAddrCB)
11241 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11242 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11243 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11244 if (DeviceAddrCB)
11245 DeviceAddrCB(I, BP);
11246 }
11247 }
11248
11249 Value *PVal = CombinedInfo.Pointers[I];
11250 Value *P = Builder.CreateConstInBoundsGEP2_32(
11251 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11252 I);
11253 // TODO: Check alignment correct.
11254 Builder.CreateAlignedStore(PVal, P,
11255 M.getDataLayout().getPrefTypeAlign(PtrTy));
11256
11257 if (RuntimeSizes.test(I)) {
11258 Value *S = Builder.CreateConstInBoundsGEP2_32(
11259 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11260 /*Idx0=*/0,
11261 /*Idx1=*/I);
11262 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11263 Int64Ty,
11264 /*isSigned=*/true),
11265 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11266 }
11267 // Fill up the mapper array.
11268 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11269 Value *MFunc = ConstantPointerNull::get(PtrTy);
11270
11271 auto CustomMFunc = CustomMapperCB(I);
11272 if (!CustomMFunc)
11273 return CustomMFunc.takeError();
11274 if (*CustomMFunc)
11275 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11276
11277 Value *MAddr = Builder.CreateInBoundsGEP(
11278 PointerArrayType, MappersArray,
11279 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11280 Builder.CreateAlignedStore(
11281 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11282 }
11283
11284 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11285 Info.NumberOfPtrs == 0)
11286 return Error::success();
11287 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11288 return Error::success();
11289}
11290
11292 BasicBlock *CurBB = Builder.GetInsertBlock();
11293
11294 if (!CurBB || CurBB->hasTerminator()) {
11295 // If there is no insert point or the previous block is already
11296 // terminated, don't touch it.
11297 } else {
11298 // Otherwise, create a fall-through branch.
11299 Builder.CreateBr(Target);
11300 }
11301
11302 Builder.ClearInsertionPoint();
11303}
11304
11306 bool IsFinished) {
11307 BasicBlock *CurBB = Builder.GetInsertBlock();
11308
11309 // Fall out of the current block (if necessary).
11310 emitBranch(BB);
11311
11312 if (IsFinished && BB->use_empty()) {
11313 BB->eraseFromParent();
11314 return;
11315 }
11316
11317 // Place the block after the current block, if possible, or else at
11318 // the end of the function.
11319 if (CurBB && CurBB->getParent())
11320 CurFn->insert(std::next(CurBB->getIterator()), BB);
11321 else
11322 CurFn->insert(CurFn->end(), BB);
11323 Builder.SetInsertPoint(BB);
11324}
11325
11327 BodyGenCallbackTy ElseGen,
11328 InsertPointTy AllocaIP,
11329 ArrayRef<BasicBlock *> DeallocBlocks) {
11330 // If the condition constant folds and can be elided, try to avoid emitting
11331 // the condition and the dead arm of the if/else.
11332 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11333 auto CondConstant = CI->getSExtValue();
11334 if (CondConstant)
11335 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11336
11337 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11338 }
11339
11340 Function *CurFn = Builder.GetInsertBlock()->getParent();
11341
11342 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11343 // emit the conditional branch.
11344 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11345 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11346 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11347 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11348 // Emit the 'then' code.
11349 emitBlock(ThenBlock, CurFn);
11350 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks)) {
11351 // Let the function own the blocks not emitted yet so they are freed.
11352 ElseBlock->insertInto(CurFn);
11353 ContBlock->insertInto(CurFn);
11354 return Err;
11355 }
11356 emitBranch(ContBlock);
11357 // Emit the 'else' code if present.
11358 // There is no need to emit line number for unconditional branch.
11359 emitBlock(ElseBlock, CurFn);
11360 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks)) {
11361 ContBlock->insertInto(CurFn);
11362 return Err;
11363 }
11364 // There is no need to emit line number for unconditional branch.
11365 emitBranch(ContBlock);
11366 // Emit the continuation block for code after the if.
11367 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11368 return Error::success();
11369}
11370
11371bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11372 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11375 "Unexpected Atomic Ordering.");
11376
11377 bool Flush = false;
11379
11380 switch (AK) {
11381 case Read:
11384 FlushAO = AtomicOrdering::Acquire;
11385 Flush = true;
11386 }
11387 break;
11388 case Write:
11389 case Compare:
11390 case Update:
11393 FlushAO = AtomicOrdering::Release;
11394 Flush = true;
11395 }
11396 break;
11397 case Capture:
11398 switch (AO) {
11400 FlushAO = AtomicOrdering::Acquire;
11401 Flush = true;
11402 break;
11404 FlushAO = AtomicOrdering::Release;
11405 Flush = true;
11406 break;
11410 Flush = true;
11411 break;
11412 default:
11413 // do nothing - leave silently.
11414 break;
11415 }
11416 }
11417
11418 if (Flush) {
11419 // Currently Flush RT call still doesn't take memory_ordering, so for when
11420 // that happens, this tries to do the resolution of which atomic ordering
11421 // to use with but issue the flush call
11422 // TODO: pass `FlushAO` after memory ordering support is added
11423 (void)FlushAO;
11424 emitFlush(Loc);
11425 }
11426
11427 // for AO == AtomicOrdering::Monotonic and all other case combinations
11428 // do nothing
11429 return Flush;
11430}
11431
11435 AtomicOrdering AO, InsertPointTy AllocaIP) {
11436 if (!updateToLocation(Loc))
11437 return Loc.IP;
11438
11439 assert(X.Var->getType()->isPointerTy() &&
11440 "OMP Atomic expects a pointer to target memory");
11441 Type *XElemTy = X.ElemTy;
11442 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11443 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11444 "OMP atomic read expected a scalar type");
11445
11446 Value *XRead = nullptr;
11447
11448 if (XElemTy->isIntegerTy()) {
11449 LoadInst *XLD =
11450 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11451 XLD->setAtomic(AO);
11452 XRead = cast<Value>(XLD);
11453 } else if (XElemTy->isStructTy()) {
11454 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11455 // target does not support `atomicrmw` of the size of the struct
11456 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11457 OldVal->setAtomic(AO);
11458 const DataLayout &DL = OldVal->getDataLayout();
11459 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11460 OpenMPIRBuilder::AtomicInfo atomicInfo(
11461 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11462 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11463 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11464 XRead = AtomicLoadRes.first;
11465 OldVal->eraseFromParent();
11466 } else {
11467 // We need to perform atomic op as integer
11468 IntegerType *IntCastTy =
11469 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11470 LoadInst *XLoad =
11471 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11472 XLoad->setAtomic(AO);
11473 if (XElemTy->isFloatingPointTy()) {
11474 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11475 } else {
11476 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11477 }
11478 }
11479 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11480 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11481 return Builder.saveIP();
11482}
11483
11486 AtomicOpValue &X, Value *Expr,
11487 AtomicOrdering AO, InsertPointTy AllocaIP) {
11488 if (!updateToLocation(Loc))
11489 return Loc.IP;
11490
11491 assert(X.Var->getType()->isPointerTy() &&
11492 "OMP Atomic expects a pointer to target memory");
11493 Type *XElemTy = X.ElemTy;
11494 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11495 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11496 "OMP atomic write expected a scalar type");
11497
11498 if (XElemTy->isIntegerTy()) {
11499 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11500 XSt->setAtomic(AO);
11501 } else if (XElemTy->isStructTy()) {
11502 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11503 const DataLayout &DL = OldVal->getDataLayout();
11504 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11505 OpenMPIRBuilder::AtomicInfo atomicInfo(
11506 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11507 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11508 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11509 OldVal->eraseFromParent();
11510 } else {
11511 // We need to bitcast and perform atomic op as integers
11512 IntegerType *IntCastTy =
11513 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11514 Value *ExprCast =
11515 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11516 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11517 XSt->setAtomic(AO);
11518 }
11519
11520 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11521 return Builder.saveIP();
11522}
11523
11526 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11527 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11528 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11529 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11530 if (!updateToLocation(Loc))
11531 return Loc.IP;
11532
11533 LLVM_DEBUG({
11534 Type *XTy = X.Var->getType();
11535 assert(XTy->isPointerTy() &&
11536 "OMP Atomic expects a pointer to target memory");
11537 Type *XElemTy = X.ElemTy;
11538 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11539 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11540 "OMP atomic update expected a scalar or struct type");
11541 });
11542
11543 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11544 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11545 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11546 if (!AtomicResult)
11547 return AtomicResult.takeError();
11548 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11549 return Builder.saveIP();
11550}
11551
11552// FIXME: Duplicating AtomicExpand
11553Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11554 AtomicRMWInst::BinOp RMWOp) {
11555 switch (RMWOp) {
11556 case AtomicRMWInst::Add:
11557 return Builder.CreateAdd(Src1, Src2);
11558 case AtomicRMWInst::Sub:
11559 return Builder.CreateSub(Src1, Src2);
11560 case AtomicRMWInst::And:
11561 return Builder.CreateAnd(Src1, Src2);
11563 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11564 case AtomicRMWInst::Or:
11565 return Builder.CreateOr(Src1, Src2);
11566 case AtomicRMWInst::Xor:
11567 return Builder.CreateXor(Src1, Src2);
11568 case AtomicRMWInst::Max:
11569 return Builder.CreateBinaryIntrinsic(Intrinsic::smax, Src1, Src2);
11570 case AtomicRMWInst::Min:
11571 return Builder.CreateBinaryIntrinsic(Intrinsic::smin, Src1, Src2);
11573 return Builder.CreateBinaryIntrinsic(Intrinsic::umax, Src1, Src2);
11575 return Builder.CreateBinaryIntrinsic(Intrinsic::umin, Src1, Src2);
11577 return Builder.CreateFAdd(Src1, Src2);
11579 return Builder.CreateFSub(Src1, Src2);
11581 return Builder.CreateMaxNum(Src1, Src2);
11583 return Builder.CreateMinNum(Src1, Src2);
11585 return Builder.CreateMaximum(Src1, Src2);
11587 return Builder.CreateMinimum(Src1, Src2);
11596 llvm_unreachable("Unsupported atomic update operation");
11597 }
11598 llvm_unreachable("Unsupported atomic update operation");
11599}
11600
11602 // Loads cannot use Release or AcquireRelease ordering. This load is
11603 // just the initial value for the cmpxchg loop; the cmpxchg itself
11604 // retains the original ordering.
11605 AtomicOrdering LoadAO = AO;
11606
11607 if (AO == AtomicOrdering::Release) {
11609 } else if (AO == AtomicOrdering::AcquireRelease) {
11610 LoadAO = AtomicOrdering::Acquire;
11611 }
11612
11613 return LoadAO;
11614}
11615
11616Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11617 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11619 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11620 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11621 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11622 bool emitRMWOp = false;
11623 switch (RMWOp) {
11624 case AtomicRMWInst::Add:
11625 case AtomicRMWInst::And:
11627 case AtomicRMWInst::Or:
11628 case AtomicRMWInst::Xor:
11630 case AtomicRMWInst::Max:
11631 case AtomicRMWInst::Min:
11639 emitRMWOp = XElemTy;
11640 break;
11641 case AtomicRMWInst::Sub:
11643 emitRMWOp = (IsXBinopExpr && XElemTy);
11644 break;
11645 default:
11646 emitRMWOp = false;
11647 }
11648 emitRMWOp &=
11649 (XElemTy->isIntegerTy() || XElemTy->isFloatTy() || XElemTy->isDoubleTy());
11650
11651 std::pair<Value *, Value *> Res;
11652 if (emitRMWOp) {
11653 AtomicRMWInst *RMWInst =
11654 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11655 if (IsIgnoreDenormalMode)
11656 RMWInst->setMetadata(llvm::LLVMContext::MD_atomic_ignore_denormal_mode,
11657 llvm::MDNode::get(Builder.getContext(), {}));
11658 if (T.isAMDGPU()) {
11659 if (!IsFineGrainedMemory)
11660 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11661 llvm::MDNode::get(Builder.getContext(), {}));
11662 if (!IsRemoteMemory)
11663 RMWInst->setMetadata("amdgpu.no.remote.memory",
11664 llvm::MDNode::get(Builder.getContext(), {}));
11665 }
11666 Res.first = RMWInst;
11667 // not needed except in case of postfix captures. Generate anyway for
11668 // consistency with the else part. Will be removed with any DCE pass.
11669 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11670 if (RMWOp == AtomicRMWInst::Xchg)
11671 Res.second = Res.first;
11672 else
11673 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11674 } else if (XElemTy->isStructTy()) {
11675 LoadInst *OldVal =
11676 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11678 OldVal->setAtomic(LoadAO);
11679 const DataLayout &LoadDL = OldVal->getDataLayout();
11680 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11681
11682 OpenMPIRBuilder::AtomicInfo atomicInfo(
11683 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11684 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11685 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11686 BasicBlock *CurBB = Builder.GetInsertBlock();
11687 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11688 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11689 BasicBlock *ExitBB =
11690 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11691 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11692 X->getName() + ".atomic.cont");
11693 ContBB->getTerminator()->eraseFromParent();
11694 Builder.restoreIP(AllocaIP);
11695 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11696 NewAtomicAddr->setName(X->getName() + "x.new.val");
11697 Builder.SetInsertPoint(ContBB);
11698 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11699 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11700 Value *OldExprVal = PHI;
11701 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11702 if (!CBResult)
11703 return CBResult.takeError();
11704 Value *Upd = *CBResult;
11705 Builder.CreateStore(Upd, NewAtomicAddr);
11708 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11709 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11710 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11711 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11712 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11713 OldVal->eraseFromParent();
11714 Res.first = OldExprVal;
11715 Res.second = Upd;
11716
11717 if (UnreachableInst *ExitTI =
11719 CurBBTI->eraseFromParent();
11720 Builder.SetInsertPoint(ExitBB);
11721 } else {
11722 Builder.SetInsertPoint(ExitTI);
11723 }
11724 } else {
11725 IntegerType *IntCastTy =
11726 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11727 LoadInst *OldVal =
11728 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11730 OldVal->setAtomic(LoadAO);
11731 // CurBB
11732 // | /---\
11733 // ContBB |
11734 // | \---/
11735 // ExitBB
11736 BasicBlock *CurBB = Builder.GetInsertBlock();
11737 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11738 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11739 BasicBlock *ExitBB =
11740 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11741 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11742 X->getName() + ".atomic.cont");
11743 ContBB->getTerminator()->eraseFromParent();
11744 Builder.restoreIP(AllocaIP);
11745 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11746 NewAtomicAddr->setName(X->getName() + "x.new.val");
11747 Builder.SetInsertPoint(ContBB);
11748 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11749 PHI->addIncoming(OldVal, CurBB);
11750 bool IsIntTy = XElemTy->isIntegerTy();
11751 Value *OldExprVal = PHI;
11752 if (!IsIntTy) {
11753 if (XElemTy->isFloatingPointTy()) {
11754 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11755 X->getName() + ".atomic.fltCast");
11756 } else {
11757 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11758 X->getName() + ".atomic.ptrCast");
11759 }
11760 }
11761
11762 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11763 if (!CBResult)
11764 return CBResult.takeError();
11765 Value *Upd = *CBResult;
11766 Builder.CreateStore(Upd, NewAtomicAddr);
11767 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11770 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11771 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11772 Result->setVolatile(VolatileX);
11773 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11774 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11775 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11776 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11777
11778 Res.first = OldExprVal;
11779 Res.second = Upd;
11780
11781 // set Insertion point in exit block
11782 if (UnreachableInst *ExitTI =
11784 CurBBTI->eraseFromParent();
11785 Builder.SetInsertPoint(ExitBB);
11786 } else {
11787 Builder.SetInsertPoint(ExitTI);
11788 }
11789 }
11790
11791 return Res;
11792}
11793
11796 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11797 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11798 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11799 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11800 if (!updateToLocation(Loc))
11801 return Loc.IP;
11802
11803 LLVM_DEBUG({
11804 Type *XTy = X.Var->getType();
11805 assert(XTy->isPointerTy() &&
11806 "OMP Atomic expects a pointer to target memory");
11807 Type *XElemTy = X.ElemTy;
11808 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11809 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11810 "OMP atomic capture expected a scalar or struct type");
11811 });
11812
11813 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11814 // 'x' is simply atomically rewritten with 'expr'.
11815 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11816 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11817 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11818 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11819 if (!AtomicResult)
11820 return AtomicResult.takeError();
11821 Value *CapturedVal =
11822 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11823 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11824
11825 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11826 return Builder.saveIP();
11827}
11828
11832 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11833 bool IsFailOnly, bool IsWeak) {
11834
11836 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
11837 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
11838}
11839
11843 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
11844 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
11845
11846 if (!updateToLocation(Loc))
11847 return Loc.IP;
11848
11849 assert(X.Var->getType()->isPointerTy() &&
11850 "OMP atomic expects a pointer to target memory");
11851 // compare capture
11852 if (V.Var) {
11853 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
11854 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
11855 }
11856
11857 bool IsInteger = E->getType()->isIntegerTy();
11858
11859 if (Op == OMPAtomicCompareOp::EQ) {
11860 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
11861 // R.Var handling.
11862 Value *OldValue = nullptr;
11863 Value *SuccessOrFail = nullptr;
11864
11865 if (!IsInteger && HandleFPNegZero) {
11866 // IEEE 754 special cases for cmpxchg (which is bitwise):
11867 // 1. -0.0 == +0.0 but they have different bit patterns.
11868 // 2. NaN != NaN but identical NaN bit patterns would match.
11869 //
11870 // CurBB:
11871 // %e_int = bitcast E to intN
11872 // %d_int = bitcast D to intN
11873 // %x_curr = load atomic intN, X
11874 // %x_fp = bitcast %x_curr to FP
11875 // %e_is_nan = fcmp uno E, E
11876 // %x_is_nan = fcmp uno %x_fp, %x_fp
11877 // %either_nan = or %e_is_nan, %x_is_nan
11878 // br %either_nan, NaNBB, NotNaNBB
11879 // NaNBB: ; NaN == anything is always false
11880 // br ExitBB
11881 // NotNaNBB:
11882 // %x_is_zero = fcmp oeq %x_fp, 0.0
11883 // %e_is_zero = fcmp oeq E, 0.0
11884 // %both_zero = and %x_is_zero, %e_is_zero
11885 // br %both_zero, ZeroBB, NormalBB
11886 // ZeroBB: ; both ±0.0 → x = d
11887 // cmpxchg X, %x_curr, %d_int
11888 // br ExitBB
11889 // NormalBB: ; original path
11890 // cmpxchg X, %e_int, %d_int
11891 // br ExitBB
11892 // ExitBB:
11893 // phi merge
11894 IntegerType *IntCastTy =
11895 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11896 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11897 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11898
11899 // Load X atomically.
11900 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
11901 X.Var->getName() + ".atomic.load");
11903 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
11904
11905 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
11906 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
11907 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
11908 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
11909 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
11910
11911 BasicBlock *CurBB = Builder.GetInsertBlock();
11912 Function *F = CurBB->getParent();
11913 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11914 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11915 BasicBlock *ExitBB =
11916 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
11918 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
11919 BasicBlock *NotNaNBB = BasicBlock::Create(
11920 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
11922 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
11923 BasicBlock *NormalBB = BasicBlock::Create(
11924 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
11925
11926 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
11927 CurBB->getTerminator()->eraseFromParent();
11928 Builder.SetInsertPoint(CurBB);
11929 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
11930
11931 // NaNBB: NaN == anything is always false; skip cmpxchg.
11932 Builder.SetInsertPoint(NaNBB);
11933 Builder.CreateBr(ExitBB);
11934
11935 // NotNaNBB: check both X and E for ±0.0.
11936 Builder.SetInsertPoint(NotNaNBB);
11937 Value *XIsZero =
11938 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
11939 X.Var->getName() + ".atomic.xiszero");
11940 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
11941 "atomic.e.iszero");
11942 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
11943 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
11944
11945 // ZeroBB: cmpxchg with X's loaded bit-pattern.
11946 Builder.SetInsertPoint(ZeroBB);
11947 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
11948 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
11949 ResZero->setWeak(IsWeak);
11950 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
11951 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
11952 Builder.CreateBr(ExitBB);
11953
11954 // NormalBB: original bitwise cmpxchg.
11955 Builder.SetInsertPoint(NormalBB);
11956 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
11957 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
11958 ResNormal->setWeak(IsWeak);
11959 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
11960 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
11961 Builder.CreateBr(ExitBB);
11962
11963 // ExitBB: merge results from NaN, Zero, and Normal paths.
11964 Builder.SetInsertPoint(ExitBB->begin());
11965 PHINode *OldIntPHI =
11966 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
11967 OldIntPHI->addIncoming(XCurr, NaNBB);
11968 OldIntPHI->addIncoming(OldZero, ZeroBB);
11969 OldIntPHI->addIncoming(OldNormal, NormalBB);
11970 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
11971 X.Var->getName() + ".atomic.ok");
11972 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
11973 SuccessPHI->addIncoming(OkZero, ZeroBB);
11974 SuccessPHI->addIncoming(OkNormal, NormalBB);
11975
11976 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
11977 CurBBTI->eraseFromParent();
11978 Builder.SetInsertPoint(ExitBB);
11979 } else {
11980 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
11981 }
11982
11983 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
11984 X.Var->getName() + ".atomic.old.fp");
11985 SuccessOrFail = SuccessPHI;
11986 } else {
11987 AtomicCmpXchgInst *Result = nullptr;
11988 if (!IsInteger) {
11989 IntegerType *IntCastTy =
11990 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
11991 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
11992 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
11993 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
11994 MaybeAlign(), AO, Failure);
11995 } else {
11996 Result =
11997 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
11998 }
11999 Result->setWeak(IsWeak);
12000
12001 if (V.Var) {
12002 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
12003 if (!IsInteger)
12004 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
12005 assert(OldValue->getType() == V.ElemTy &&
12006 "OldValue and V must be of same type");
12007 if (IsPostfixUpdate) {
12008 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
12009 } else {
12010 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
12011 if (IsFailOnly) {
12012 BasicBlock *CurBB = Builder.GetInsertBlock();
12013 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
12014 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
12015 BasicBlock *ExitBB = CurBB->splitBasicBlock(
12016 CurBBTI, X.Var->getName() + ".atomic.exit");
12017 BasicBlock *ContBB = CurBB->splitBasicBlock(
12018 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
12019 ContBB->getTerminator()->eraseFromParent();
12020 CurBB->getTerminator()->eraseFromParent();
12021
12022 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
12023
12024 Builder.SetInsertPoint(ContBB);
12025 Builder.CreateStore(OldValue, V.Var);
12026 Builder.CreateBr(ExitBB);
12027
12028 if (UnreachableInst *ExitTI =
12030 CurBBTI->eraseFromParent();
12031 Builder.SetInsertPoint(ExitBB);
12032 } else {
12033 Builder.SetInsertPoint(ExitTI);
12034 }
12035 } else {
12036 Value *CapturedValue =
12037 Builder.CreateSelect(SuccessOrFail, E, OldValue);
12038 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12039 }
12040 }
12041 }
12042 // The comparison result has to be stored.
12043 if (R.Var) {
12044 assert(R.Var->getType()->isPointerTy() &&
12045 "r.var must be of pointer type");
12046 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12047
12048 Value *SuccessFailureVal =
12049 Builder.CreateExtractValue(Result, /*Idxs=*/1);
12050 Value *ResultCast =
12051 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
12052 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
12053 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12054 }
12055 }
12056
12057 // For the HandleFPNegZero path, handle V.Var and R.Var using the
12058 // pre-computed OldValue and SuccessOrFail.
12059 if (HandleFPNegZero && !IsInteger) {
12060 if (V.Var) {
12061 assert(OldValue->getType() == V.ElemTy &&
12062 "OldValue and V must be of same type");
12063 if (IsPostfixUpdate) {
12064 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
12065 } else {
12066 if (IsFailOnly) {
12067 BasicBlock *CurBB = Builder.GetInsertBlock();
12068 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
12069 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
12070 BasicBlock *ExitBB = CurBB->splitBasicBlock(
12071 CurBBTI, X.Var->getName() + ".atomic.exit");
12072 BasicBlock *ContBB = CurBB->splitBasicBlock(
12073 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
12074 ContBB->getTerminator()->eraseFromParent();
12075 CurBB->getTerminator()->eraseFromParent();
12076
12077 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
12078
12079 Builder.SetInsertPoint(ContBB);
12080 Builder.CreateStore(OldValue, V.Var);
12081 Builder.CreateBr(ExitBB);
12082
12083 if (UnreachableInst *ExitTI =
12085 CurBBTI->eraseFromParent();
12086 Builder.SetInsertPoint(ExitBB);
12087 } else {
12088 Builder.SetInsertPoint(ExitTI);
12089 }
12090 } else {
12091 Value *CapturedValue =
12092 Builder.CreateSelect(SuccessOrFail, E, OldValue);
12093 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12094 }
12095 }
12096 }
12097 // The comparison result has to be stored.
12098 if (R.Var) {
12099 assert(R.Var->getType()->isPointerTy() &&
12100 "r.var must be of pointer type");
12101 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12102
12103 Value *ResultCast = R.IsSigned
12104 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
12105 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
12106 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12107 }
12108 }
12109 } else {
12110 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
12111 "Op should be either max or min at this point");
12112 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
12113
12114 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
12115 // Let's take max as example.
12116 // OpenMP form:
12117 // x = x > expr ? expr : x;
12118 // LLVM form:
12119 // *ptr = *ptr > val ? *ptr : val;
12120 // We need to transform to LLVM form.
12121 // x = x <= expr ? x : expr;
12123 if (IsXBinopExpr) {
12124 if (IsInteger) {
12125 if (X.IsSigned)
12126 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
12128 else
12129 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
12131 } else {
12132 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12134 }
12135 } else {
12136 if (IsInteger) {
12137 if (X.IsSigned)
12138 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12140 else
12141 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12143 } else {
12144 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12146 }
12147 }
12148
12149 AtomicRMWInst *OldValue =
12150 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12151 if (V.Var) {
12152 Value *CapturedValue = nullptr;
12153 if (IsPostfixUpdate) {
12154 CapturedValue = OldValue;
12155 } else {
12156 CmpInst::Predicate Pred;
12157 switch (NewOp) {
12158 case AtomicRMWInst::Max:
12159 Pred = CmpInst::ICMP_SGT;
12160 break;
12162 Pred = CmpInst::ICMP_UGT;
12163 break;
12165 Pred = CmpInst::FCMP_OGT;
12166 break;
12167 case AtomicRMWInst::Min:
12168 Pred = CmpInst::ICMP_SLT;
12169 break;
12171 Pred = CmpInst::ICMP_ULT;
12172 break;
12174 Pred = CmpInst::FCMP_OLT;
12175 break;
12176 default:
12177 llvm_unreachable("unexpected comparison op");
12178 }
12179 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12180 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12181 }
12182 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12183 }
12184 }
12185
12186 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12187
12188 return Builder.saveIP();
12189}
12190
12193 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12194 Value *NumTeamsUpper, Value *ThreadLimit,
12195 Value *IfExpr) {
12196 if (!updateToLocation(Loc))
12197 return InsertPointTy();
12198
12199 uint32_t SrcLocStrSize;
12200 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12201 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12202 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12203
12204 // Outer allocation basicblock is the entry block of the current function.
12205 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12206 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12207 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12208 Builder.SetInsertPoint(BodyBB->begin());
12209 }
12210
12211 // The current basic block is split into four basic blocks. After outlining,
12212 // they will be mapped as follows:
12213 // ```
12214 // def current_fn() {
12215 // current_basic_block:
12216 // br label %teams.exit
12217 // teams.exit:
12218 // ; instructions after teams
12219 // }
12220 //
12221 // def outlined_fn() {
12222 // teams.alloca:
12223 // br label %teams.body
12224 // teams.body:
12225 // ; instructions within teams body
12226 // }
12227 // ```
12228 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12229 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12230 BasicBlock *AllocaBB =
12231 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12232
12233 bool SubClausesPresent =
12234 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12235 // Push num_teams
12236 if (!Config.isTargetDevice() && SubClausesPresent) {
12237 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12238 "if lowerbound is non-null, then upperbound must also be non-null "
12239 "for bounds on num_teams");
12240
12241 if (NumTeamsUpper == nullptr)
12242 NumTeamsUpper = Builder.getInt32(0);
12243
12244 if (NumTeamsLower == nullptr)
12245 NumTeamsLower = NumTeamsUpper;
12246
12247 if (IfExpr) {
12248 assert(IfExpr->getType()->isIntegerTy() &&
12249 "argument to if clause must be an integer value");
12250
12251 // upper = ifexpr ? upper : 1
12252 if (IfExpr->getType() != Int1)
12253 IfExpr = Builder.CreateICmpNE(IfExpr,
12254 ConstantInt::get(IfExpr->getType(), 0));
12255 NumTeamsUpper = Builder.CreateSelect(
12256 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12257
12258 // lower = ifexpr ? lower : 1
12259 NumTeamsLower = Builder.CreateSelect(
12260 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12261 }
12262
12263 if (ThreadLimit == nullptr)
12264 ThreadLimit = Builder.getInt32(0);
12265
12266 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12267 // truncate or sign extend the passed values to match the int32 parameters.
12268 Value *NumTeamsLowerInt32 =
12269 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12270 Value *NumTeamsUpperInt32 =
12271 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12272 Value *ThreadLimitInt32 =
12273 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12274
12275 Value *ThreadNum = getOrCreateThreadID(Ident);
12276
12278 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12279 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12280 ThreadLimitInt32});
12281 }
12282 // Generate the body of teams.
12283 InsertPointTy AllocaIP(AllocaBB->begin());
12284 InsertPointTy CodeGenIP(BodyBB->begin());
12285 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12286 return Err;
12287
12288 auto OI = std::make_unique<OutlineInfo>();
12289 OI->EntryBB = AllocaBB;
12290 OI->ExitBB = ExitBB;
12291 OI->OuterAllocBB = &OuterAllocaBB;
12292
12293 // Insert fake values for global tid and bound tid.
12295 InsertPointTy OuterAllocaIP(OuterAllocaBB.begin());
12296 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12297 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12298 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12299 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12300
12301 auto HostPostOutlineCB = [this, Ident,
12302 ToBeDeleted](Function &OutlinedFn) mutable {
12303 // The stale call instruction will be replaced with a new call instruction
12304 // for runtime call with the outlined function.
12305
12306 assert(OutlinedFn.hasOneUse() &&
12307 "there must be a single user for the outlined function");
12308 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12309 ToBeDeleted.push_back(StaleCI);
12310
12311 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12312 "Outlined function must have two or three arguments only");
12313
12314 bool HasShared = OutlinedFn.arg_size() == 3;
12315
12316 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12317 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12318 if (HasShared)
12319 OutlinedFn.getArg(2)->setName("data");
12320
12321 // Call to the runtime function for teams in the current function.
12322 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12323 "outlined function.");
12324 Builder.SetInsertPoint(StaleCI);
12325 SmallVector<Value *> Args = {
12326 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12327 if (HasShared)
12328 Args.push_back(StaleCI->getArgOperand(2));
12331 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12332 Args);
12333
12334 Builder.ClearInsertionPoint();
12335 for (Instruction *I : llvm::reverse(ToBeDeleted))
12336 I->eraseFromParent();
12337 };
12338
12339 if (!Config.isTargetDevice())
12340 OI->PostOutlineCB = HostPostOutlineCB;
12341
12342 addOutlineInfo(std::move(OI));
12343
12344 Builder.SetInsertPoint(ExitBB);
12345
12346 return Builder.saveIP();
12347}
12348
12350 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12351 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12352 if (!updateToLocation(Loc))
12353 return InsertPointTy();
12354
12355 BasicBlock *OuterAllocaBB = OuterAllocIP.getNodeParent();
12356
12357 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12358 BasicBlock *BodyBB =
12359 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12360 Builder.SetInsertPoint(BodyBB->begin());
12361 }
12362 BasicBlock *ExitBB =
12363 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12364 BasicBlock *BodyBB =
12365 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12366 BasicBlock *AllocaBB =
12367 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12368
12369 // Generate the body of distribute clause
12370 InsertPointTy AllocaIP(AllocaBB->begin());
12371 InsertPointTy CodeGenIP(BodyBB->begin());
12372 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12373 return Err;
12374
12375 // When using target we use different runtime functions which require a
12376 // callback.
12377 if (Config.isTargetDevice()) {
12378 auto OI = std::make_unique<OutlineInfo>();
12379 OI->OuterAllocBB = OuterAllocIP.getNodeParent();
12380 OI->EntryBB = AllocaBB;
12381 OI->ExitBB = ExitBB;
12382 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12383 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12384
12385 addOutlineInfo(std::move(OI));
12386 }
12387 Builder.SetInsertPoint(ExitBB);
12388
12389 return Builder.saveIP();
12390}
12391
12394 std::string VarName) {
12395 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12397 Names.size()),
12398 Names);
12399 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12400 M, MapNamesArrayInit->getType(),
12401 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12402 VarName);
12403 return MapNamesArrayGlobal;
12404}
12405
12406// Create all simple and struct types exposed by the runtime and remember
12407// the llvm::PointerTypes of them for easy access later.
12408void OpenMPIRBuilder::initializeTypes(Module &M) {
12409 LLVMContext &Ctx = M.getContext();
12410 StructType *T;
12411 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12412 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12413#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12414#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12415 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12416 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12417#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12418 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12419 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12420#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12421 T = StructType::getTypeByName(Ctx, StructName); \
12422 if (!T) \
12423 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12424 VarName = T; \
12425 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12426#include "llvm/Frontend/OpenMP/OMPKinds.def"
12427}
12428
12431 SmallVectorImpl<BasicBlock *> &BlockVector) {
12433 BlockSet.insert(EntryBB);
12434 BlockSet.insert(ExitBB);
12435
12436 Worklist.push_back(EntryBB);
12437 while (!Worklist.empty()) {
12438 BasicBlock *BB = Worklist.pop_back_val();
12439 BlockVector.push_back(BB);
12440 for (BasicBlock *SuccBB : successors(BB))
12441 if (BlockSet.insert(SuccBB).second)
12442 Worklist.push_back(SuccBB);
12443 }
12444}
12445
12446std::unique_ptr<CodeExtractor>
12448 bool ArgsInZeroAddressSpace,
12449 Twine Suffix) {
12450 return std::make_unique<CodeExtractor>(
12451 Blocks, /* DominatorTree */ nullptr,
12452 /* AggregateArgs */ true,
12453 /* BlockFrequencyInfo */ nullptr,
12454 /* BranchProbabilityInfo */ nullptr,
12455 /* AssumptionCache */ nullptr,
12456 /* AllowVarArgs */ true,
12457 /* AllowAlloca */ true,
12458 /* AllocationBlock*/ OuterAllocBB,
12459 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12460 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12461}
12462
12463std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12464 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12465 return std::make_unique<DeviceSharedMemCodeExtractor>(
12466 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12467 /* AggregateArgs */ true,
12468 /* BlockFrequencyInfo */ nullptr,
12469 /* BranchProbabilityInfo */ nullptr,
12470 /* AssumptionCache */ nullptr,
12471 /* AllowVarArgs */ true,
12472 /* AllowAlloca */ true,
12473 /* AllocationBlock*/ OuterAllocBB,
12474 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12476 : OuterDeallocBBs,
12477 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12478}
12479
12481 uint64_t Size, int32_t Flags,
12483 StringRef Name) {
12484 if (!Config.isGPU()) {
12487 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12488 return;
12489 }
12490 // TODO: Add support for global variables on the device after declare target
12491 // support.
12492 Function *Fn = dyn_cast<Function>(Addr);
12493 if (!Fn)
12494 return;
12495
12496 // Add a function attribute for the kernel.
12497 Fn->addFnAttr("kernel");
12498 if (T.isAMDGCN())
12499 Fn->addFnAttr("uniform-work-group-size");
12500 Fn->addFnAttr(Attribute::MustProgress);
12501}
12502
12503// We only generate metadata for function that contain target regions.
12506
12507 // If there are no entries, we don't need to do anything.
12508 if (OffloadInfoManager.empty())
12509 return;
12510
12511 LLVMContext &C = M.getContext();
12514 16>
12515 OrderedEntries(OffloadInfoManager.size());
12516
12517 // Auxiliary methods to create metadata values and strings.
12518 auto &&GetMDInt = [this](unsigned V) {
12519 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12520 };
12521
12522 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12523
12524 // Create the offloading info metadata node.
12525 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12526 auto &&TargetRegionMetadataEmitter =
12527 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12528 const TargetRegionEntryInfo &EntryInfo,
12530 // Generate metadata for target regions. Each entry of this metadata
12531 // contains:
12532 // - Entry 0 -> Kind of this type of metadata (0).
12533 // - Entry 1 -> Device ID of the file where the entry was identified.
12534 // - Entry 2 -> File ID of the file where the entry was identified.
12535 // - Entry 3 -> Mangled name of the function where the entry was
12536 // identified.
12537 // - Entry 4 -> Line in the file where the entry was identified.
12538 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12539 // - Entry 6 -> Order the entry was created.
12540 // The first element of the metadata node is the kind.
12541 Metadata *Ops[] = {
12542 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12543 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12544 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12545 GetMDInt(E.getOrder())};
12546
12547 // Save this entry in the right position of the ordered entries array.
12548 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12549
12550 // Add metadata to the named metadata node.
12551 MD->addOperand(MDNode::get(C, Ops));
12552 };
12553
12554 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12555
12556 // Create function that emits metadata for each device global variable entry;
12557 auto &&DeviceGlobalVarMetadataEmitter =
12558 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12559 StringRef MangledName,
12561 // Generate metadata for global variables. Each entry of this metadata
12562 // contains:
12563 // - Entry 0 -> Kind of this type of metadata (1).
12564 // - Entry 1 -> Mangled name of the variable.
12565 // - Entry 2 -> Declare target kind.
12566 // - Entry 3 -> Order the entry was created.
12567 // The first element of the metadata node is the kind.
12568 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12569 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12570
12571 // Save this entry in the right position of the ordered entries array.
12572 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12573 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12574
12575 // Add metadata to the named metadata node.
12576 MD->addOperand(MDNode::get(C, Ops));
12577 };
12578
12579 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12580 DeviceGlobalVarMetadataEmitter);
12581
12582 for (const auto &E : OrderedEntries) {
12583 assert(E.first && "All ordered entries must exist!");
12584 if (const auto *CE =
12586 E.first)) {
12587 if (!CE->getID() || !CE->getAddress()) {
12588 // Do not blame the entry if the parent funtion is not emitted.
12589 TargetRegionEntryInfo EntryInfo = E.second;
12590 StringRef FnName = EntryInfo.ParentName;
12591 if (!M.getNamedValue(FnName))
12592 continue;
12593 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12594 continue;
12595 }
12596 createOffloadEntry(CE->getID(), CE->getAddress(),
12597 /*Size=*/0, CE->getFlags(),
12599 } else if (const auto *CE = dyn_cast<
12601 E.first)) {
12604 CE->getFlags());
12605 switch (Flags) {
12608 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12609 continue;
12610 if (!CE->getAddress()) {
12611 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12612 continue;
12613 }
12614 // The vaiable has no definition - no need to add the entry.
12615 if (CE->getVarSize() == 0)
12616 continue;
12617 break;
12619 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12620 (!Config.isTargetDevice() && CE->getAddress())) &&
12621 "Declaret target link address is set.");
12622 if (Config.isTargetDevice())
12623 continue;
12624 if (!CE->getAddress()) {
12626 continue;
12627 }
12628 break;
12631 if (!CE->getAddress()) {
12632 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12633 continue;
12634 }
12635 break;
12636 default:
12637 break;
12638 }
12639
12640 // Hidden or internal symbols on the device are not externally visible.
12641 // We should not attempt to register them by creating an offloading
12642 // entry. Indirect variables are handled separately on the device.
12643 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12644 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12645 (Flags !=
12647 Flags != OffloadEntriesInfoManager::
12648 OMPTargetGlobalVarEntryIndirectVTable))
12649 continue;
12650
12651 // Indirect globals need to use a special name that doesn't match the name
12652 // of the associated host global.
12654 Flags ==
12656 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12657 Flags, CE->getLinkage(), CE->getVarName());
12658 else
12659 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12660 Flags, CE->getLinkage());
12661
12662 } else {
12663 llvm_unreachable("Unsupported entry kind.");
12664 }
12665 }
12666
12667 // Emit requires directive globals to a special entry so the runtime can
12668 // register them when the device image is loaded.
12669 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12670 // entries should be redesigned to better suit this use-case.
12671 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12675 ".requires", /*Size=*/0,
12677 Config.getRequiresFlags());
12678}
12679
12682 unsigned FileID, unsigned Line, unsigned Count) {
12683 raw_svector_ostream OS(Name);
12684 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12685 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12686 if (Count)
12687 OS << "_" << Count;
12688}
12689
12691 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12692 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12694 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12695 EntryInfo.Line, NewCount);
12696}
12697
12700 vfs::FileSystem &VFS,
12701 StringRef ParentName) {
12702 sys::fs::UniqueID ID(0xdeadf17e, 0);
12703 auto FileIDInfo = CallBack();
12704 uint64_t FileID = 0;
12705 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12706 ID = Status->getUniqueID();
12707 FileID = Status->getUniqueID().getFile();
12708 } else {
12709 // If the inode ID could not be determined, create a hash value
12710 // the current file name and use that as an ID.
12711 FileID = hash_value(std::get<0>(FileIDInfo));
12712 }
12713
12714 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12715 std::get<1>(FileIDInfo));
12716}
12717
12719 unsigned Offset = 0;
12720 for (uint64_t Remain =
12721 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12723 !(Remain & 1); Remain = Remain >> 1)
12724 Offset++;
12725 return Offset;
12726}
12727
12730 // Rotate by getFlagMemberOffset() bits.
12731 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12732 << getFlagMemberOffset());
12733}
12734
12737 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12738 // If the entry is PTR_AND_OBJ but has not been marked with the special
12739 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12740 // marked as MEMBER_OF.
12741 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12743 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12746 return;
12747
12748 // Entries with ATTACH are not members-of anything. They are handled
12749 // separately by the runtime after other maps have been handled.
12750 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12752 return;
12753
12754 // Reset the placeholder value to prepare the flag for the assignment of the
12755 // proper MEMBER_OF value.
12756 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12757 Flags |= MemberOfFlag;
12758}
12759
12763 bool IsDeclaration, bool IsExternallyVisible,
12764 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12765 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12766 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12767 std::function<Constant *()> GlobalInitializer,
12768 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12769 // TODO: convert this to utilise the IRBuilder Config rather than
12770 // a passed down argument.
12771 if (OpenMPSIMD)
12772 return nullptr;
12773
12776 CaptureClause ==
12778 Config.hasRequiresUnifiedSharedMemory())) {
12779 SmallString<64> PtrName;
12780 {
12781 raw_svector_ostream OS(PtrName);
12782 OS << MangledName;
12783 if (!IsExternallyVisible)
12784 OS << format("_%x", EntryInfo.FileID);
12785 OS << "_decl_tgt_ref_ptr";
12786 }
12787
12788 Value *Ptr = M.getNamedValue(PtrName);
12789
12790 if (!Ptr) {
12791 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12792 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12793
12794 auto *GV = cast<GlobalVariable>(Ptr);
12795 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12796
12797 if (!Config.isTargetDevice()) {
12798 if (GlobalInitializer)
12799 GV->setInitializer(GlobalInitializer());
12800 else
12801 GV->setInitializer(GlobalValue);
12802 }
12803
12805 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12806 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12807 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12808 }
12809
12810 return cast<Constant>(Ptr);
12811 }
12812
12813 return nullptr;
12814}
12815
12819 bool IsDeclaration, bool IsExternallyVisible,
12820 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12821 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12822 std::vector<Triple> TargetTriple,
12823 std::function<Constant *()> GlobalInitializer,
12824 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12825 Constant *Addr) {
12827 (TargetTriple.empty() && !Config.isTargetDevice()))
12828 return;
12829
12831 StringRef VarName;
12832 int64_t VarSize;
12834
12836 CaptureClause ==
12838 !Config.hasRequiresUnifiedSharedMemory()) {
12840 VarName = MangledName;
12841 GlobalValue *LlvmVal = M.getNamedValue(VarName);
12842
12843 if (!IsDeclaration)
12844 VarSize = divideCeil(
12845 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
12846 else
12847 VarSize = 0;
12848 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
12849
12850 // This is a workaround carried over from Clang which prevents undesired
12851 // optimisation of internal variables.
12852 if (Config.isTargetDevice() &&
12853 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
12854 // Do not create a "ref-variable" if the original is not also available
12855 // on the host.
12856 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
12857 return;
12858
12859 std::string RefName = createPlatformSpecificName({VarName, "ref"});
12860
12861 if (!M.getNamedValue(RefName)) {
12862 Constant *AddrRef =
12863 getOrCreateInternalVariable(Addr->getType(), RefName);
12864 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
12865 GvAddrRef->setConstant(true);
12866 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
12867 GvAddrRef->setInitializer(Addr);
12868 GeneratedRefs.push_back(GvAddrRef);
12869 }
12870 }
12871 } else {
12874 else
12876
12877 if (Config.isTargetDevice()) {
12878 VarName = (Addr) ? Addr->getName() : "";
12879 Addr = nullptr;
12880 } else {
12882 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12883 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12884 LlvmPtrTy, GlobalInitializer, VariableLinkage);
12885 VarName = (Addr) ? Addr->getName() : "";
12886 }
12887 VarSize = M.getDataLayout().getPointerSize();
12889 }
12890
12891 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
12892 Flags, Linkage);
12893}
12894
12895/// Loads all the offload entries information from the host IR
12896/// metadata.
12898 // If we are in target mode, load the metadata from the host IR. This code has
12899 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
12900
12901 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
12902 if (!MD)
12903 return;
12904
12905 for (MDNode *MN : MD->operands()) {
12906 auto &&GetMDInt = [MN](unsigned Idx) {
12907 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
12908 return cast<ConstantInt>(V->getValue())->getZExtValue();
12909 };
12910
12911 auto &&GetMDString = [MN](unsigned Idx) {
12912 auto *V = cast<MDString>(MN->getOperand(Idx));
12913 return V->getString();
12914 };
12915
12916 switch (GetMDInt(0)) {
12917 default:
12918 llvm_unreachable("Unexpected metadata!");
12919 break;
12920 case OffloadEntriesInfoManager::OffloadEntryInfo::
12921 OffloadingEntryInfoTargetRegion: {
12922 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
12923 /*DeviceID=*/GetMDInt(1),
12924 /*FileID=*/GetMDInt(2),
12925 /*Line=*/GetMDInt(4),
12926 /*Count=*/GetMDInt(5));
12927 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
12928 /*Order=*/GetMDInt(6));
12929 break;
12930 }
12931 case OffloadEntriesInfoManager::OffloadEntryInfo::
12932 OffloadingEntryInfoDeviceGlobalVar:
12933 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
12934 /*MangledName=*/GetMDString(1),
12936 /*Flags=*/GetMDInt(2)),
12937 /*Order=*/GetMDInt(3));
12938 break;
12939 }
12940 }
12941}
12942
12944 StringRef HostFilePath) {
12945 if (HostFilePath.empty())
12946 return;
12947
12948 auto Buf = VFS.getBufferForFile(HostFilePath);
12949 if (std::error_code Err = Buf.getError()) {
12950 report_fatal_error(("error opening host file from host file path inside of "
12951 "OpenMPIRBuilder: " +
12952 Err.message())
12953 .c_str());
12954 }
12955
12956 LLVMContext Ctx;
12958 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
12959 if (std::error_code Err = M.getError()) {
12961 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
12962 .c_str());
12963 }
12964
12965 loadOffloadInfoMetadata(*M.get());
12966}
12967
12970 llvm::StringRef Name) {
12971 Builder.restoreIP(Loc.IP);
12972
12973 BasicBlock *CurBB = Builder.GetInsertBlock();
12974 assert(CurBB &&
12975 "expected a valid insertion block for creating an iterator loop");
12976 Function *F = CurBB->getParent();
12977
12978 InsertPointTy SplitIP = Builder.saveIP();
12979 if (SplitIP == CurBB->end())
12980 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
12981 SplitIP = Terminator->getIterator();
12982
12983 BasicBlock *ContBB =
12984 splitBB(SplitIP, /*CreateBranch=*/false,
12985 Builder.getCurrentDebugLocation(), "omp.it.cont");
12986
12987 CanonicalLoopInfo *CLI =
12988 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
12989 /*PreInsertBefore=*/ContBB,
12990 /*PostInsertBefore=*/ContBB, Name);
12991
12992 // Enter loop from original block.
12993 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
12994
12995 // Remove the unconditional branch inserted by createLoopSkeleton in the body
12996 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
12997 T->eraseFromParent();
12998
12999 InsertPointTy BodyIP = CLI->getBodyIP();
13000 // Blocks that exist before BodyGen, other than the body and latch, are
13001 // outside the loop body.
13002 SmallPtrSet<BasicBlock *, 32> ExistingBlocks;
13003 for (BasicBlock &Block : *F)
13004 ExistingBlocks.insert(&Block);
13005 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
13006 return Err;
13007
13008 // The body may span several blocks. Branch its single unterminated block to
13009 // the latch; otherwise some block must already branch there.
13010 BasicBlock *Latch = CLI->getLatch();
13011 BasicBlock *OpenBB = nullptr;
13012 bool ReachesLatch = false;
13013 SmallVector<BasicBlock *> Worklist{CLI->getBody()};
13014 SmallPtrSet<BasicBlock *, 8> Visited{CLI->getBody()};
13015 while (!Worklist.empty()) {
13016 BasicBlock *BB = Worklist.pop_back_val();
13017 if (!BB->hasTerminator()) {
13018 if (OpenBB)
13020 "iterator bodygen must leave at most one unterminated block",
13022 OpenBB = BB;
13023 continue;
13024 }
13025 for (BasicBlock *Succ : successors(BB)) {
13026 if (Succ == Latch) {
13027 ReachesLatch = true;
13028 continue;
13029 }
13030 if (Succ != CLI->getBody() && ExistingBlocks.contains(Succ))
13032 "iterator bodygen must not branch out of the loop body",
13034 if (Visited.insert(Succ).second)
13035 Worklist.push_back(Succ);
13036 }
13037 }
13038
13039 if (OpenBB) {
13040 Builder.SetInsertPoint(OpenBB);
13041 Builder.CreateBr(Latch);
13042 } else if (!ReachesLatch) {
13043 return make_error<StringError>("iterator bodygen must reach the loop latch",
13045 }
13046
13047 // Link After -> ContBB
13048 Builder.SetInsertPoint(CLI->getAfter()->begin());
13049 if (!CLI->getAfter()->hasTerminator())
13050 Builder.CreateBr(ContBB);
13051
13052 return ContBB->begin();
13053}
13054
13055/// Mangle the parameter part of the vector function name according to
13056/// their OpenMP classification. The mangling function is defined in
13057/// section 4.5 of the AAVFABI(2021Q1).
13058static std::string mangleVectorParameters(
13060 SmallString<256> Buffer;
13061 llvm::raw_svector_ostream Out(Buffer);
13062 for (const auto &ParamAttr : ParamAttrs) {
13063 switch (ParamAttr.Kind) {
13065 Out << 'l';
13066 break;
13068 Out << 'R';
13069 break;
13071 Out << 'U';
13072 break;
13074 Out << 'L';
13075 break;
13077 Out << 'u';
13078 break;
13080 Out << 'v';
13081 break;
13082 }
13083 if (ParamAttr.HasVarStride)
13084 Out << "s" << ParamAttr.StrideOrArg;
13085 else if (ParamAttr.Kind ==
13087 ParamAttr.Kind ==
13089 ParamAttr.Kind ==
13091 ParamAttr.Kind ==
13093 // Don't print the step value if it is not present or if it is
13094 // equal to 1.
13095 if (ParamAttr.StrideOrArg < 0)
13096 Out << 'n' << -ParamAttr.StrideOrArg;
13097 else if (ParamAttr.StrideOrArg != 1)
13098 Out << ParamAttr.StrideOrArg;
13099 }
13100
13101 if (!!ParamAttr.Alignment)
13102 Out << 'a' << ParamAttr.Alignment;
13103 }
13104
13105 return std::string(Out.str());
13106}
13107
13109 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
13111 struct ISADataTy {
13112 char ISA;
13113 unsigned VecRegSize;
13114 };
13115 ISADataTy ISAData[] = {
13116 {'b', 128}, // SSE
13117 {'c', 256}, // AVX
13118 {'d', 256}, // AVX2
13119 {'e', 512}, // AVX512
13120 };
13122 switch (Branch) {
13124 Masked.push_back('N');
13125 Masked.push_back('M');
13126 break;
13128 Masked.push_back('N');
13129 break;
13131 Masked.push_back('M');
13132 break;
13133 }
13134 for (char Mask : Masked) {
13135 for (const ISADataTy &Data : ISAData) {
13137 llvm::raw_svector_ostream Out(Buffer);
13138 Out << "_ZGV" << Data.ISA << Mask;
13139 if (!VLENVal) {
13140 assert(NumElts && "Non-zero simdlen/cdtsize expected");
13141 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
13142 } else {
13143 Out << VLENVal;
13144 }
13145 Out << mangleVectorParameters(ParamAttrs);
13146 Out << '_' << Fn->getName();
13147 Fn->addFnAttr(Out.str());
13148 }
13149 }
13150}
13151
13152// Function used to add the attribute. The parameter `VLEN` is templated to
13153// allow the use of `x` when targeting scalable functions for SVE.
13154template <typename T>
13155static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
13156 char ISA, StringRef ParSeq,
13157 StringRef MangledName, bool OutputBecomesInput,
13158 llvm::Function *Fn) {
13159 SmallString<256> Buffer;
13160 llvm::raw_svector_ostream Out(Buffer);
13161 Out << Prefix << ISA << LMask << VLEN;
13162 if (OutputBecomesInput)
13163 Out << 'v';
13164 Out << ParSeq << '_' << MangledName;
13165 Fn->addFnAttr(Out.str());
13166}
13167
13168// Helper function to generate the Advanced SIMD names depending on the value
13169// of the NDS when simdlen is not present.
13170static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13171 StringRef Prefix, char ISA,
13172 StringRef ParSeq, StringRef MangledName,
13173 bool OutputBecomesInput,
13174 llvm::Function *Fn) {
13175 switch (NDS) {
13176 case 8:
13177 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13178 OutputBecomesInput, Fn);
13179 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13180 OutputBecomesInput, Fn);
13181 break;
13182 case 16:
13183 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13184 OutputBecomesInput, Fn);
13185 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13186 OutputBecomesInput, Fn);
13187 break;
13188 case 32:
13189 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13190 OutputBecomesInput, Fn);
13191 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13192 OutputBecomesInput, Fn);
13193 break;
13194 case 64:
13195 case 128:
13196 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13197 OutputBecomesInput, Fn);
13198 break;
13199 default:
13200 llvm_unreachable("Scalar type is too wide.");
13201 }
13202}
13203
13204/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13206 llvm::Function *Fn, unsigned UserVLEN,
13208 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13209 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13210
13211 // Sort out parameter sequence.
13212 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13213 StringRef Prefix = "_ZGV";
13214 StringRef MangledName = Fn->getName();
13215
13216 // Generate simdlen from user input (if any).
13217 if (UserVLEN) {
13218 if (ISA == 's') {
13219 // SVE generates only a masked function.
13220 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13221 OutputBecomesInput, Fn);
13222 return;
13223 }
13224
13225 switch (Branch) {
13227 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13228 OutputBecomesInput, Fn);
13229 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13230 OutputBecomesInput, Fn);
13231 break;
13233 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13234 OutputBecomesInput, Fn);
13235 break;
13237 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13238 OutputBecomesInput, Fn);
13239 break;
13240 }
13241 return;
13242 }
13243
13244 if (ISA == 's') {
13245 // SVE, section 3.4.1, item 1.
13246 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13247 OutputBecomesInput, Fn);
13248 return;
13249 }
13250
13251 switch (Branch) {
13253 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13254 MangledName, OutputBecomesInput, Fn);
13255 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13256 MangledName, OutputBecomesInput, Fn);
13257 break;
13259 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13260 MangledName, OutputBecomesInput, Fn);
13261 break;
13263 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13264 MangledName, OutputBecomesInput, Fn);
13265 break;
13266 }
13267}
13268
13269//===----------------------------------------------------------------------===//
13270// OffloadEntriesInfoManager
13271//===----------------------------------------------------------------------===//
13272
13274 return OffloadEntriesTargetRegion.empty() &&
13275 OffloadEntriesDeviceGlobalVar.empty();
13276}
13277
13278unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13279 const TargetRegionEntryInfo &EntryInfo) const {
13280 auto It = OffloadEntriesTargetRegionCount.find(
13281 getTargetRegionEntryCountKey(EntryInfo));
13282 if (It == OffloadEntriesTargetRegionCount.end())
13283 return 0;
13284 return It->second;
13285}
13286
13287void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13288 const TargetRegionEntryInfo &EntryInfo) {
13289 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13290 EntryInfo.Count + 1;
13291}
13292
13293/// Initialize target region entry.
13295 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13296 OffloadEntriesTargetRegion[EntryInfo] =
13297 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13299 ++OffloadingEntriesNum;
13300}
13301
13303 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13305 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13306
13307 // Update the EntryInfo with the next available count for this location.
13308 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13309
13310 // If we are emitting code for a target, the entry is already initialized,
13311 // only has to be registered.
13312 if (OMPBuilder->Config.isTargetDevice()) {
13313 // This could happen if the device compilation is invoked standalone.
13314 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13315 return;
13316 }
13317 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13318 Entry.setAddress(Addr);
13319 Entry.setID(ID);
13320 Entry.setFlags(Flags);
13321 } else {
13323 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13324 return;
13325 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13326 "Target region entry already registered!");
13327 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13328 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13329 ++OffloadingEntriesNum;
13330 }
13331 incrementTargetRegionEntryInfoCount(EntryInfo);
13332}
13333
13335 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13336
13337 // Update the EntryInfo with the next available count for this location.
13338 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13339
13340 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13341 if (It == OffloadEntriesTargetRegion.end()) {
13342 return false;
13343 }
13344 // Fail if this entry is already registered.
13345 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13346 return false;
13347 return true;
13348}
13349
13351 const OffloadTargetRegionEntryInfoActTy &Action) {
13352 // Scan all target region entries and perform the provided action.
13353 for (const auto &It : OffloadEntriesTargetRegion) {
13354 Action(It.first, It.second);
13355 }
13356}
13357
13359 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13360 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13361 ++OffloadingEntriesNum;
13362}
13363
13365 StringRef VarName, Constant *Addr, int64_t VarSize,
13367 if (OMPBuilder->Config.isTargetDevice()) {
13368 // This could happen if the device compilation is invoked standalone.
13369 if (!hasDeviceGlobalVarEntryInfo(VarName))
13370 return;
13371 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13372 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13373 if (Entry.getVarSize() == 0) {
13374 Entry.setVarSize(VarSize);
13375 Entry.setLinkage(Linkage);
13376 }
13377 return;
13378 }
13379 Entry.setVarSize(VarSize);
13380 Entry.setLinkage(Linkage);
13381 Entry.setAddress(Addr);
13382 } else {
13383 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13384 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13385 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13386 "Entry not initialized!");
13387 if (Entry.getVarSize() == 0) {
13388 Entry.setVarSize(VarSize);
13389 Entry.setLinkage(Linkage);
13390 }
13391 return;
13392 }
13394 Flags ==
13396 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13397 Addr, VarSize, Flags, Linkage,
13398 VarName.str());
13399 else
13400 OffloadEntriesDeviceGlobalVar.try_emplace(
13401 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13402 ++OffloadingEntriesNum;
13403 }
13404}
13405
13408 // Scan all target region entries and perform the provided action.
13409 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13410 Action(E.getKey(), E.getValue());
13411}
13412
13413//===----------------------------------------------------------------------===//
13414// CanonicalLoopInfo
13415//===----------------------------------------------------------------------===//
13416
13417void CanonicalLoopInfo::reportUnsupportedVirtualCollapse() const {
13418 assert(isValid() && "Requires a valid canonical loop");
13419 assert(VirtualCollapse && "Requires Virtual collapse state");
13421 "virtual collapse must be the final structural loop transformation");
13422}
13423
13424void CanonicalLoopInfo::collectControlBlocks(
13426 if (VirtualCollapse)
13427 reportUnsupportedVirtualCollapse();
13428 // We only count those BBs as control block for which we do not need to
13429 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13430 // flow. For consistency, this also means we do not add the Body block, which
13431 // is just the entry to the body code.
13432 BBs.reserve(BBs.size() + 6);
13433 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13434}
13435
13436BasicBlock *CanonicalLoopInfo::getVirtualCollapseInit() const {
13437 return VirtualCollapse ? VirtualCollapse->Count->getParent() : nullptr;
13438}
13439
13441 assert(isValid() && "Requires a valid canonical loop");
13442 if (VirtualCollapse)
13443 return getVirtualCollapseInit()->getSinglePredecessor();
13444
13445 for (BasicBlock *Pred : predecessors(Header)) {
13446 if (Pred != Latch)
13447 return Pred;
13448 }
13449 llvm_unreachable("Missing preheader");
13450}
13451
13452void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13453 assert(isValid() && "Requires a valid canonical loop");
13454
13455 Instruction *CmpI = &getCond()->front();
13456 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13457 CmpI->setOperand(1, TripCount);
13458
13459#ifndef NDEBUG
13460 assertOK();
13461#endif
13462}
13463
13464void CanonicalLoopInfo::mapIndVar(
13465 llvm::function_ref<Value *(Instruction *)> Updater) {
13466 assert(isValid() && "Requires a valid canonical loop");
13467
13468 Instruction *OldIV = getIndVar();
13469
13470 // Record all uses excluding those introduced by the updater. Uses by the
13471 // CanonicalLoopInfo itself to keep track of the number of iterations are
13472 // excluded.
13473 SmallVector<Use *> ReplacableUses;
13474 for (Use &U : OldIV->uses()) {
13475 auto *User = dyn_cast<Instruction>(U.getUser());
13476 if (!User)
13477 continue;
13478 if (User->getParent() == getCond())
13479 continue;
13480 if (User->getParent() == getLatch())
13481 continue;
13482 ReplacableUses.push_back(&U);
13483 }
13484
13485 // Run the updater that may introduce new uses
13486 Value *NewIV = Updater(OldIV);
13487
13488 // Replace the old uses with the value returned by the updater.
13489 for (Use *U : ReplacableUses)
13490 U->set(NewIV);
13491
13492#ifndef NDEBUG
13493 assertOK();
13494#endif
13495}
13496
13498#ifndef NDEBUG
13499 // No constraints if this object currently does not describe a loop.
13500 if (!isValid())
13501 return;
13502
13503 bool IsVirtual = VirtualCollapse.has_value();
13504 BasicBlock *Preheader = getPreheader();
13505 BasicBlock *Init = getVirtualCollapseInit();
13506 BasicBlock *IndVarEntry = Preheader;
13507 if (IsVirtual) {
13508 auto *Guard = cast<CondBrInst>(Init->getTerminator());
13509 IndVarEntry = Guard->getSuccessor(0);
13510 assert(IndVarEntry->getSinglePredecessor() == Init);
13511 assert(IndVarEntry->getSingleSuccessor() == Header);
13512 assert(Guard->getSuccessor(1) == Exit);
13513 assert(VirtualCollapse->Begin->getParent() == Init);
13514 for (BinaryOperator *Copy :
13515 {VirtualCollapse->Begin, VirtualCollapse->Count}) {
13516 auto *Zero = dyn_cast<ConstantInt>(Copy->getOperand(1));
13517 assert(Copy->getOpcode() == Instruction::Add && Zero && Zero->isZero() &&
13518 "Virtual range copies must add zero");
13519 }
13520 auto *NonEmpty = cast<ICmpInst>(Guard->getCondition());
13521 assert(NonEmpty->getPredicate() == ICmpInst::ICMP_NE &&
13522 NonEmpty->getOperand(0) == VirtualCollapse->Count &&
13523 cast<ConstantInt>(NonEmpty->getOperand(1))->isZero() &&
13524 "Collapsed guard must skip empty ranges with zero count");
13525 }
13526
13527 // Validate the retained physical loop without exposing a logical body or IV.
13528 BasicBlock *Body =
13529 IsVirtual ? cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0)
13530 : getBody();
13531 BasicBlock *After = getAfter();
13532
13533 // Verify standard control-flow we use for OpenMP loops.
13534 assert(Preheader);
13535 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13536 "Preheader must terminate with unconditional branch");
13537 if (IsVirtual)
13538 assert(Preheader->getSingleSuccessor() == Init &&
13539 "Preheader must jump to collapsed initialization");
13540 else
13541 assert(Preheader->getSingleSuccessor() == Header &&
13542 "Preheader must jump to header");
13543
13544 assert(Header);
13545 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13546 "Header must terminate with unconditional branch");
13547 assert(Header->getSingleSuccessor() == Cond &&
13548 "Header must jump to exiting block");
13549
13550 assert(Cond);
13551 assert(Cond->getSinglePredecessor() == Header &&
13552 "Exiting block only reachable from header");
13553
13554 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13555 "Exiting block must terminate with conditional branch");
13556 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13557 "Exiting block's first successor jump to the body");
13558 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13559 "Exiting block's second successor must exit the loop");
13560
13561 assert(Body);
13562 assert(Body->getSinglePredecessor() == Cond &&
13563 "Body only reachable from exiting block");
13564 assert(!isa<PHINode>(Body->front()));
13565
13566 assert(Latch);
13567 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13568 "Latch must terminate with unconditional branch");
13569 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13570 // TODO: To support simple redirecting of the end of the body code that has
13571 // multiple; introduce another auxiliary basic block like preheader and after.
13572 assert(Latch->getSinglePredecessor() != nullptr);
13573 assert(!isa<PHINode>(Latch->front()));
13574
13575 assert(Exit);
13576 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13577 "Exit block must terminate with unconditional branch");
13578 assert(Exit->getSingleSuccessor() == After &&
13579 "Exit block must jump to after block");
13580
13581 assert(After);
13582 assert(After->getSinglePredecessor() == Exit &&
13583 "After block only reachable from exit block");
13584 assert(After->empty() || !isa<PHINode>(After->front()));
13585
13586 Instruction *IndVar = IsVirtual ? &Header->front() : getIndVar();
13587 assert(IndVar && "Canonical induction variable not found?");
13588 assert(isa<IntegerType>(IndVar->getType()) &&
13589 "Induction variable must be an integer");
13590 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13591 "Induction variable must be a PHI in the loop header");
13592 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == IndVarEntry);
13593 if (!IsVirtual)
13594 assert(cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))
13595 ->isZero());
13596 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13597
13598 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13599 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13600 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13601 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13602 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13603 ->isOne());
13604
13605 Value *TripCount =
13606 IsVirtual ? cast<CmpInst>(&Cond->front())->getOperand(1) : getTripCount();
13607 assert(TripCount && "Loop trip count not found?");
13608 assert(IndVar->getType() == TripCount->getType() &&
13609 "Trip count and induction variable must have the same type");
13610
13611 auto *CmpI = cast<CmpInst>(&Cond->front());
13612 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13613 "Exit condition must be a signed less-than comparison");
13614 assert(CmpI->getOperand(0) == IndVar &&
13615 "Exit condition must compare the induction variable");
13616 assert(CmpI->getOperand(1) == TripCount &&
13617 "Exit condition must compare with the trip count");
13618#endif
13619}
13620
13622 Header = nullptr;
13623 Cond = nullptr;
13624 Latch = nullptr;
13625 Exit = nullptr;
13626 VirtualCollapse.reset();
13627}
SDValue Pred
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:665
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 bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
Definition Lint.cpp:550
#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 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< 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 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 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 Error 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 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 ...
SmallVector< MachineOperand, 4 > 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
const T & back() const
Get the last element.
Definition ArrayRef.h:150
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.
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 void insertInto(Function *Parent, BasicBlock *InsertBefore=nullptr)
Insert unlinked basic block into a function.
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.
This class is the base class for the comparison instructions.
Definition InstrTypes.h:728
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:44
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
bool properlyDominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
properlyDominates - Returns true iff A dominates B and A != B.
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:257
BasicBlock::iterator InsertPoint
InsertPoint - A saved insertion point.
Definition IRBuilder.h:254
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:267
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2987
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:598
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:595
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:39
size_type size() const
Definition MapVector.h:59
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 using Config.CollapseMapping.
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.
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 PropagatePresentToPointeeStorage=false, bool PropagatePresentToNonPointeeStorage=false)
Emit the user-defined mapper function.
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 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
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...
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:28
void append(StringRef RHS)
Append from a StringRef.
Definition SmallString.h:70
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.
Target - Wrapper for Target specific information.
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.
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:389
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:548
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:985
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:142
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:180
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:556
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:314
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
#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.
@ Entry
Definition COFF.h:862
@ 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()
constexpr unsigned DefaultPartialUnrollFactor
Unroll factor for a partial unroll without an explicit factor, when the unrolled loop must be materia...
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
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:385
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).
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
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
LLVM_ABI void reportFatalInternalError(Error Err)
Report a fatal error that indicates a bug in LLVM.
Definition Error.cpp:173
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 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
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
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.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
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:320
static auto filterDbgVars(iterator_range< simple_ilist< DbgRecord >::iterator > R)
Filter the DbgRecord range to DbgVariableRecord types only and downcast.
Physical handles captured before the source descriptors are invalidated.
SmallVector< PHINode *, 4 > IndVars
SmallVector< BasicBlock *, 4 > Preheaders
SmallVector< BasicBlock *, 4 > Latches
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
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.
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...