LLVM 24.0.0git
OMPIRBuilder.cpp
Go to the documentation of this file.
1//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// This file implements the OpenMPIRBuilder class, which is used as a
11/// convenient way to create LLVM instructions for OpenMP directives.
12///
13//===----------------------------------------------------------------------===//
14
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/StringRef.h"
31#include "llvm/IR/Attributes.h"
32#include "llvm/IR/BasicBlock.h"
33#include "llvm/IR/CFG.h"
34#include "llvm/IR/CallingConv.h"
35#include "llvm/IR/Constant.h"
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/DIBuilder.h"
40#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
44#include "llvm/IR/LLVMContext.h"
45#include "llvm/IR/MDBuilder.h"
46#include "llvm/IR/Metadata.h"
48#include "llvm/IR/PassManager.h"
50#include "llvm/IR/Value.h"
53#include "llvm/Support/Error.h"
64
65#include <cstdint>
66#include <optional>
67
68#define DEBUG_TYPE "openmp-ir-builder"
69
70using namespace llvm;
71using namespace omp;
72
73static cl::opt<bool>
74 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
75 cl::desc("Use optimistic attributes describing "
76 "'as-if' properties of runtime calls."),
77 cl::init(false));
78
80 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
81 cl::desc("Factor for the unroll threshold to account for code "
82 "simplifications still taking place"),
83 cl::init(1.5));
84
86 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
87 cl::desc("Use a default max threads if none is provided."), cl::init(true));
88
89#ifndef NDEBUG
90/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
91/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
92/// an InsertPoint stores the instruction before something is inserted. For
93/// instance, if both point to the same instruction, two IRBuilders alternating
94/// creating instruction will cause the instructions to be interleaved.
97 if (!IP1.isValid() || !IP2.isValid())
98 return false;
99 return IP1 == IP2;
100}
101
103 // Valid ordered/unordered and base algorithm combinations.
104 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
105 case OMPScheduleType::UnorderedStaticChunked:
106 case OMPScheduleType::UnorderedStatic:
107 case OMPScheduleType::UnorderedDynamicChunked:
108 case OMPScheduleType::UnorderedGuidedChunked:
109 case OMPScheduleType::UnorderedRuntime:
110 case OMPScheduleType::UnorderedAuto:
111 case OMPScheduleType::UnorderedTrapezoidal:
112 case OMPScheduleType::UnorderedGreedy:
113 case OMPScheduleType::UnorderedBalanced:
114 case OMPScheduleType::UnorderedGuidedIterativeChunked:
115 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
116 case OMPScheduleType::UnorderedSteal:
117 case OMPScheduleType::UnorderedStaticBalancedChunked:
118 case OMPScheduleType::UnorderedGuidedSimd:
119 case OMPScheduleType::UnorderedRuntimeSimd:
120 case OMPScheduleType::OrderedStaticChunked:
121 case OMPScheduleType::OrderedStatic:
122 case OMPScheduleType::OrderedDynamicChunked:
123 case OMPScheduleType::OrderedGuidedChunked:
124 case OMPScheduleType::OrderedRuntime:
125 case OMPScheduleType::OrderedAuto:
126 case OMPScheduleType::OrderdTrapezoidal:
127 case OMPScheduleType::NomergeUnorderedStaticChunked:
128 case OMPScheduleType::NomergeUnorderedStatic:
129 case OMPScheduleType::NomergeUnorderedDynamicChunked:
130 case OMPScheduleType::NomergeUnorderedGuidedChunked:
131 case OMPScheduleType::NomergeUnorderedRuntime:
132 case OMPScheduleType::NomergeUnorderedAuto:
133 case OMPScheduleType::NomergeUnorderedTrapezoidal:
134 case OMPScheduleType::NomergeUnorderedGreedy:
135 case OMPScheduleType::NomergeUnorderedBalanced:
136 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
137 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
138 case OMPScheduleType::NomergeUnorderedSteal:
139 case OMPScheduleType::NomergeOrderedStaticChunked:
140 case OMPScheduleType::NomergeOrderedStatic:
141 case OMPScheduleType::NomergeOrderedDynamicChunked:
142 case OMPScheduleType::NomergeOrderedGuidedChunked:
143 case OMPScheduleType::NomergeOrderedRuntime:
144 case OMPScheduleType::NomergeOrderedAuto:
145 case OMPScheduleType::NomergeOrderedTrapezoidal:
146 case OMPScheduleType::OrderedDistributeChunked:
147 case OMPScheduleType::OrderedDistribute:
148 break;
149 default:
150 return false;
151 }
152
153 // Must not set both monotonicity modifiers at the same time.
154 OMPScheduleType MonotonicityFlags =
155 SchedType & OMPScheduleType::MonotonicityMask;
156 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
157 return false;
158
159 return true;
160}
161#endif
162
163/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
164/// debug location when the insert point is at the end of a block. It picks a
165/// location scoped to the current function: the block's last instruction
166/// location if the block is non-empty, otherwise a location synthesized from
167/// the function's subprogram (when the function has debug info).
170 Builder.restoreIP(IP);
171 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
172 // set the debug location from that instruction, so leave it alone.
173 llvm::BasicBlock *BB = Builder.GetInsertBlock();
174 if (Builder.GetInsertPoint() != BB->end())
175 return;
176
177 // At the end of a block, pick a location guaranteed to belong to the current
178 // insertion function's subprogram. Prefer the block's own last instruction;
179 // otherwise synthesize a location from the function's subprogram.
180 if (!BB->empty())
181 Builder.SetCurrentDebugLocation(BB->back().getDebugLoc());
182 else if (llvm::DISubprogram *FSP =
183 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
184 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
185 Builder.SetCurrentDebugLocation(
186 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
187 }
188}
189
190static bool hasGridValue(const Triple &T) {
191 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
192}
193
194static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
195 if (T.isAMDGPU()) {
196 StringRef Features =
197 Kernel->getFnAttribute("target-features").getValueAsString();
198 if (Features.count("+wavefrontsize64"))
201 }
202 if (T.isNVPTX())
204 if (T.isSPIRV())
206 llvm_unreachable("No grid value available for this architecture!");
207}
208
209/// Determine which scheduling algorithm to use, determined from schedule clause
210/// arguments.
211static OMPScheduleType
212getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
213 bool HasSimdModifier, bool HasDistScheduleChunks) {
214 // Currently, the default schedule it static.
215 switch (ClauseKind) {
216 case OMP_SCHEDULE_Default:
217 case OMP_SCHEDULE_Static:
218 return HasChunks ? OMPScheduleType::BaseStaticChunked
219 : OMPScheduleType::BaseStatic;
220 case OMP_SCHEDULE_Dynamic:
221 return OMPScheduleType::BaseDynamicChunked;
222 case OMP_SCHEDULE_Guided:
223 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
224 : OMPScheduleType::BaseGuidedChunked;
225 case OMP_SCHEDULE_Auto:
227 case OMP_SCHEDULE_Runtime:
228 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
229 : OMPScheduleType::BaseRuntime;
230 case OMP_SCHEDULE_Distribute:
231 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
232 : OMPScheduleType::BaseDistribute;
233 }
234 llvm_unreachable("unhandled schedule clause argument");
235}
236
237/// Adds ordering modifier flags to schedule type.
238static OMPScheduleType
240 bool HasOrderedClause) {
241 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
242 OMPScheduleType::None &&
243 "Must not have ordering nor monotonicity flags already set");
244
245 OMPScheduleType OrderingModifier = HasOrderedClause
246 ? OMPScheduleType::ModifierOrdered
247 : OMPScheduleType::ModifierUnordered;
248 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
249
250 // Unsupported combinations
251 if (OrderingScheduleType ==
252 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
253 return OMPScheduleType::OrderedGuidedChunked;
254 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
255 OMPScheduleType::ModifierOrdered))
256 return OMPScheduleType::OrderedRuntime;
257
258 return OrderingScheduleType;
259}
260
261/// Adds monotonicity modifier flags to schedule type.
262static OMPScheduleType
264 bool HasSimdModifier, bool HasMonotonic,
265 bool HasNonmonotonic, bool HasOrderedClause) {
266 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
267 OMPScheduleType::None &&
268 "Must not have monotonicity flags already set");
269 assert((!HasMonotonic || !HasNonmonotonic) &&
270 "Monotonic and Nonmonotonic are contradicting each other");
271
272 if (HasMonotonic) {
273 return ScheduleType | OMPScheduleType::ModifierMonotonic;
274 } else if (HasNonmonotonic) {
275 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
276 } else {
277 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
278 // If the static schedule kind is specified or if the ordered clause is
279 // specified, and if the nonmonotonic modifier is not specified, the
280 // effect is as if the monotonic modifier is specified. Otherwise, unless
281 // the monotonic modifier is specified, the effect is as if the
282 // nonmonotonic modifier is specified.
283 OMPScheduleType BaseScheduleType =
284 ScheduleType & ~OMPScheduleType::ModifierMask;
285 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
286 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
287 HasOrderedClause) {
288 // The monotonic is used by default in openmp runtime library, so no need
289 // to set it.
290 return ScheduleType;
291 } else {
292 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
293 }
294 }
295}
296
297/// Determine the schedule type using schedule and ordering clause arguments.
298static OMPScheduleType
299computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
300 bool HasSimdModifier, bool HasMonotonicModifier,
301 bool HasNonmonotonicModifier, bool HasOrderedClause,
302 bool HasDistScheduleChunks) {
304 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
305 OMPScheduleType OrderedSchedule =
306 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
308 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
309 HasNonmonotonicModifier, HasOrderedClause);
310
312 return Result;
313}
314
315/// Given a function, if it represents the entry point of a target kernel, this
316/// returns the execution mode flags associated with that kernel.
317static std::optional<omp::OMPTgtExecModeFlags>
319 CallInst *TargetInitCall = nullptr;
320 for (Instruction &Inst : Kernel.getEntryBlock()) {
321 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
322 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
323 TargetInitCall = Call;
324 break;
325 }
326 }
327 }
328
329 if (!TargetInitCall)
330 return std::nullopt;
331
332 // Get the kernel mode information from the global variable associated to the
333 // first argument to the call to __kmpc_target_init. Refer to
334 // createTargetInit() to see how this is initialized.
335 Value *InitOperand = TargetInitCall->getArgOperand(0);
336 GlobalVariable *KernelEnv = nullptr;
337 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
338 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
339 else
340 KernelEnv = cast<GlobalVariable>(InitOperand);
341 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
342 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
343 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
344 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
345}
346
347static bool isGenericKernel(Function &Fn) {
348 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
350 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
351}
352
353/// Make \p Source branch to \p Target.
354///
355/// Handles two situations:
356/// * \p Source already has an unconditional branch.
357/// * \p Source is a degenerate block (no terminator because the BB is
358/// the current head of the IR construction).
360 if (Instruction *Term = Source->getTerminatorOrNull()) {
361 auto *Br = cast<UncondBrInst>(Term);
362 BasicBlock *Succ = Br->getSuccessor();
363 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
364 Br->setSuccessor(Target);
365 return;
366 }
367
368 auto *NewBr = UncondBrInst::Create(Target, Source);
369 NewBr->setDebugLoc(DL);
370}
371
373 bool CreateBranch, DebugLoc DL) {
374 assert(New->getFirstInsertionPt() == New->begin() &&
375 "Target BB must not have PHI nodes");
376
377 // Move instructions to new block.
378 BasicBlock *Old = IP.getNodeParent();
379 // If the `Old` block is empty then there are no instructions to move. But in
380 // the new debug scheme, it could have trailing debug records which will be
381 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
382 // reasons:
383 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
384 // 2. Even if `New` is not empty, the rationale to move those records to `New`
385 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
386 // assumes that `Old` is optimized out and is going away. This is not the case
387 // here. The `Old` block is still being used e.g. a branch instruction is
388 // added to it later in this function.
389 // So we call `BasicBlock::splice` only when `Old` is not empty.
390 if (!Old->empty())
391 New->splice(New->begin(), Old, IP, Old->end());
392
393 if (CreateBranch) {
394 auto *NewBr = UncondBrInst::Create(New, Old);
395 NewBr->setDebugLoc(DL);
396 }
397}
398
399void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
400 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
401 BasicBlock *Old = Builder.GetInsertBlock();
402
403 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
404 if (CreateBranch)
405 Builder.SetInsertPoint(Old->getTerminator());
406 else
407 Builder.SetInsertPoint(Old);
408
409 // SetInsertPoint also updates the Builder's debug location, but we want to
410 // keep the one the Builder was configured to use.
411 Builder.SetCurrentDebugLocation(DebugLoc);
412}
413
415 DebugLoc DL, llvm::Twine Name) {
416 BasicBlock *Old = IP.getNodeParent();
418 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
419 Old->getParent(), Old->getNextNode());
420 spliceBB(IP, New, CreateBranch, DL);
421 New->replaceSuccessorsPhiUsesWith(Old, New);
422 return New;
423}
424
425BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
426 llvm::Twine Name) {
427 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
428 BasicBlock *Old = Builder.GetInsertBlock();
429 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
430 if (CreateBranch)
431 Builder.SetInsertPoint(Old->getTerminator());
432 else
433 Builder.SetInsertPoint(Old);
434 // SetInsertPoint also updates the Builder's debug location, but we want to
435 // keep the one the Builder was configured to use.
436 Builder.SetCurrentDebugLocation(DebugLoc);
437 return New;
438}
439
440BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
441 llvm::Twine Name) {
442 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
443 BasicBlock *Old = Builder.GetInsertBlock();
444 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
445 if (CreateBranch)
446 Builder.SetInsertPoint(Old->getTerminator());
447 else
448 Builder.SetInsertPoint(Old);
449 // SetInsertPoint also updates the Builder's debug location, but we want to
450 // keep the one the Builder was configured to use.
451 Builder.SetCurrentDebugLocation(DebugLoc);
452 return New;
453}
454
456 llvm::Twine Suffix) {
457 BasicBlock *Old = Builder.GetInsertBlock();
458 return splitBB(Builder, CreateBranch, Old->getName() + Suffix);
459}
460
461// This function creates a fake integer value and a fake use for the integer
462// value. It returns the fake value created. This is useful in modeling the
463// extra arguments to the outlined functions.
465 OpenMPIRBuilder::InsertPointTy OuterAllocaIP,
467 OpenMPIRBuilder::InsertPointTy InnerAllocaIP,
468 const Twine &Name = "", bool AsPtr = true,
469 bool Is64Bit = false) {
470 Builder.restoreIP(OuterAllocaIP);
471 IntegerType *IntTy = Is64Bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
472 Instruction *FakeVal;
473 AllocaInst *FakeValAddr =
474 Builder.CreateAlloca(IntTy, nullptr, Name + ".addr");
475 ToBeDeleted.push_back(FakeValAddr);
476
477 if (AsPtr) {
478 FakeVal = FakeValAddr;
479 // The runtime passes these extra arguments to the outlined function as
480 // generic pointers, so cast away a non-zero alloca address space.
481 if (FakeValAddr->getAddressSpace() != 0) {
482 FakeVal = cast<Instruction>(Builder.CreateAddrSpaceCast(
483 FakeValAddr, Builder.getPtrTy(), Name + ".ascast"));
484 ToBeDeleted.push_back(FakeVal);
485 }
486 } else {
487 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
488 ToBeDeleted.push_back(FakeVal);
489 }
490
491 // Generate a fake use of this value
492 Builder.restoreIP(InnerAllocaIP);
493 Instruction *UseFakeVal;
494 if (AsPtr) {
495 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
496 } else {
497 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
498 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
499 }
500 ToBeDeleted.push_back(UseFakeVal);
501 return FakeVal;
502}
503
504//===----------------------------------------------------------------------===//
505// OpenMPIRBuilderConfig
506//===----------------------------------------------------------------------===//
507
508namespace {
510/// Values for bit flags for marking which requires clauses have been used.
511enum OpenMPOffloadingRequiresDirFlags {
512 /// flag undefined.
513 OMP_REQ_UNDEFINED = 0x000,
514 /// no requires directive present.
515 OMP_REQ_NONE = 0x001,
516 /// reverse_offload clause.
517 OMP_REQ_REVERSE_OFFLOAD = 0x002,
518 /// unified_address clause.
519 OMP_REQ_UNIFIED_ADDRESS = 0x004,
520 /// unified_shared_memory clause.
521 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
522 /// dynamic_allocators clause.
523 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
524 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
525};
526
527class OMPCodeExtractor : public CodeExtractor {
528public:
529 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
530 DominatorTree *DT = nullptr, bool AggregateArgs = false,
531 BlockFrequencyInfo *BFI = nullptr,
532 BranchProbabilityInfo *BPI = nullptr,
533 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
534 bool AllowAlloca = false,
535 BasicBlock *AllocationBlock = nullptr,
536 ArrayRef<BasicBlock *> DeallocationBlocks = {},
537 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
538 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
539 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
540 ArgsInZeroAddressSpace),
541 OMPBuilder(OMPBuilder) {}
542
543 virtual ~OMPCodeExtractor() = default;
544
545protected:
546 OpenMPIRBuilder &OMPBuilder;
547};
548
549class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
550public:
551 using OMPCodeExtractor::OMPCodeExtractor;
552 virtual ~DeviceSharedMemCodeExtractor() = default;
553
554protected:
555 virtual Instruction *
556 allocateVar(IRBuilder<>::InsertPoint AllocaIP, DebugLoc DL, Type *VarType,
557 const Twine &Name = Twine(""),
558 AddrSpaceCastInst **CastedAlloc = nullptr) override {
559 return OMPBuilder.createOMPAllocShared({AllocaIP, DL}, VarType, Name);
560 }
561
562 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
563 DebugLoc DL, Value *Var,
564 Type *VarType) override {
565 return OMPBuilder.createOMPFreeShared({DeallocIP, DL}, Var, VarType);
566 }
567};
568
569/// Helper storing information about regions to outline using device shared
570/// memory for intermediate allocations.
571struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
572 OpenMPIRBuilder &OMPBuilder;
573
574 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
575 : OMPBuilder(OMPBuilder) {}
576 virtual ~DeviceSharedMemOutlineInfo() = default;
577
578 virtual std::unique_ptr<CodeExtractor>
579 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
580 bool ArgsInZeroAddressSpace,
581 Twine Suffix = Twine("")) override;
582};
583
584} // anonymous namespace
585
587 : RequiresFlags(OMP_REQ_UNDEFINED) {}
588
591 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
592 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
595 RequiresFlags(OMP_REQ_UNDEFINED) {
596 if (HasRequiresReverseOffload)
597 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
598 if (HasRequiresUnifiedAddress)
599 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
600 if (HasRequiresUnifiedSharedMemory)
601 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
602 if (HasRequiresDynamicAllocators)
603 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
604}
605
607 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
608}
609
611 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
612}
613
615 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
616}
617
619 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
620}
621
623 return hasRequiresFlags() ? RequiresFlags
624 : static_cast<int64_t>(OMP_REQ_NONE);
625}
626
628 if (Value)
629 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
630 else
631 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
632}
633
635 if (Value)
636 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
637 else
638 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
639}
640
642 if (Value)
643 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
644 else
645 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
646}
647
649 if (Value)
650 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
651 else
652 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
653}
654
655//===----------------------------------------------------------------------===//
656// OpenMPIRBuilder
657//===----------------------------------------------------------------------===//
658
661 SmallVector<Value *> &ArgsVector) {
663 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
664 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
665 constexpr size_t MaxDim = 3;
666 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
667
668 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
669
670 Value *DynCGroupMemFallbackFlag =
671 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
672 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
673
674 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
675 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
676
677 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
678 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
679
680 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
681 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
682 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
683
684 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
685
686 Value *NumTeams3D =
687 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
688 Value *NumThreads3D =
689 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
690 for (unsigned I :
691 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
692 NumTeams3D =
693 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
694 for (unsigned I :
695 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
696 NumThreads3D =
697 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
698
699 ArgsVector = {Version,
700 PointerNum,
701 KernelArgs.RTArgs.BasePointersArray,
702 KernelArgs.RTArgs.PointersArray,
703 KernelArgs.RTArgs.SizesArray,
704 KernelArgs.RTArgs.MapTypesArray,
705 KernelArgs.RTArgs.MapNamesArray,
706 KernelArgs.RTArgs.MappersArray,
707 KernelArgs.NumIterations,
708 Flags,
709 NumTeams3D,
710 NumThreads3D,
711 KernelArgs.DynCGroupMem};
712}
713
715 LLVMContext &Ctx = Fn.getContext();
716
717 // Get the function's current attributes.
718 auto Attrs = Fn.getAttributes();
719 auto FnAttrs = Attrs.getFnAttrs();
720 auto RetAttrs = Attrs.getRetAttrs();
722 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
723 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
724
725 // Add AS to FnAS while taking special care with integer extensions.
726 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
727 bool Param = true) -> void {
728 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
729 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
730 if (HasSignExt || HasZeroExt) {
731 assert(AS.getNumAttributes() == 1 &&
732 "Currently not handling extension attr combined with others.");
733 if (Param) {
734 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
735 FnAS = FnAS.addAttribute(Ctx, AK);
736 } else if (auto AK =
737 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
738 FnAS = FnAS.addAttribute(Ctx, AK);
739 } else {
740 FnAS = FnAS.addAttributes(Ctx, AS);
741 }
742 };
743
744#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
745#include "llvm/Frontend/OpenMP/OMPKinds.def"
746
747 // Add attributes to the function declaration.
748 switch (FnID) {
749#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
750 case Enum: \
751 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
752 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
753 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
754 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
755 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
756 break;
757#include "llvm/Frontend/OpenMP/OMPKinds.def"
758 default:
759 // Attributes are optional.
760 break;
761 }
762}
763
766 FunctionType *FnTy = nullptr;
767 Function *Fn = nullptr;
768
769 // Try to find the declation in the module first.
770 switch (FnID) {
771#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
772 case Enum: \
773 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
774 IsVarArg); \
775 Fn = M.getFunction(Str); \
776 break;
777#include "llvm/Frontend/OpenMP/OMPKinds.def"
778 }
779
780 if (!Fn) {
781 // Create a new declaration if we need one.
782 switch (FnID) {
783#define OMP_RTL(Enum, Str, ...) \
784 case Enum: \
785 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
786 break;
787#include "llvm/Frontend/OpenMP/OMPKinds.def"
788 }
789 Fn->setCallingConv(Config.getRuntimeCC());
790 // Add information if the runtime function takes a callback function
791 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
792 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
793 LLVMContext &Ctx = Fn->getContext();
794 MDBuilder MDB(Ctx);
795 // Annotate the callback behavior of the runtime function:
796 // - The callback callee is argument number 2 (microtask).
797 // - The first two arguments of the callback callee are unknown (-1).
798 // - All variadic arguments to the runtime function are passed to the
799 // callback callee.
800 Fn->addMetadata(
801 LLVMContext::MD_callback,
803 2, {-1, -1}, /* VarArgsArePassed */ true)}));
804 }
805 }
806
807 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
808 << " with type " << *Fn->getFunctionType() << "\n");
809 addAttributes(FnID, *Fn);
810
811 } else {
812 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
813 << " with type " << *Fn->getFunctionType() << "\n");
814 }
815
816 assert(Fn && "Failed to create OpenMP runtime function");
817
818 return {FnTy, Fn};
819}
820
823 if (!FiniBB) {
824 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
826 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
827 Builder.SetInsertPoint(FiniBB);
828 // FiniCB adds the branch to the exit stub.
829 if (Error Err = FiniCB(Builder.saveIP()))
830 return Err;
831 }
832 return FiniBB;
833}
834
836 BasicBlock *OtherFiniBB) {
837 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
838 if (!FiniBB) {
839 FiniBB = OtherFiniBB;
840
841 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
842 if (Error Err = FiniCB(Builder.saveIP()))
843 return Err;
844
845 return Error::success();
846 }
847
848 // Move instructions from FiniBB to the start of OtherFiniBB.
849 auto EndIt = FiniBB->end();
850 if (FiniBB->size() >= 1)
851 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
852 EndIt = Prev;
853 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
854 EndIt);
855
856 FiniBB->replaceAllUsesWith(OtherFiniBB);
857 FiniBB->eraseFromParent();
858 FiniBB = OtherFiniBB;
859 return Error::success();
860}
861
864 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
865 assert(Fn && "Failed to create OpenMP runtime function pointer");
866 return Fn;
867}
868
871 StringRef Name) {
872 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
873 Call->setCallingConv(Config.getRuntimeCC());
874 return Call;
875}
876
877void OpenMPIRBuilder::initialize() { initializeTypes(M); }
878
881 BasicBlock &EntryBlock = Function->getEntryBlock();
882 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
883
884 // Loop over blocks looking for constant allocas, skipping the entry block
885 // as any allocas there are already in the desired location.
886 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
887 Block++) {
888 for (auto Inst = Block->getReverseIterator()->begin();
889 Inst != Block->getReverseIterator()->end();) {
891 Inst++;
893 continue;
894 AllocaInst->moveBeforePreserving(MoveLocInst);
895 } else {
896 Inst++;
897 }
898 }
899 }
900}
901
904
905 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
906 // TODO: For now, we support simple static allocations, we might need to
907 // move non-static ones as well. However, this will need further analysis to
908 // move the lenght arguments as well.
910 };
911
912 for (llvm::Instruction &Inst : Block)
914 if (ShouldHoistAlloca(*AllocaInst))
915 AllocasToMove.push_back(AllocaInst);
916
917 auto InsertPoint =
918 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
919
920 for (llvm::Instruction *AllocaInst : AllocasToMove)
922}
923
925 PostDominatorTree PostDomTree(*Func);
926 for (llvm::BasicBlock &BB : *Func)
927 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
929}
930
932 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
934 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
935 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
936 // Skip functions that have not finalized yet; may happen with nested
937 // function generation.
938 if (Fn && OI->getFunction() != Fn) {
939 DeferredOutlines.push_back(std::move(OI));
940 continue;
941 }
942
943 ParallelRegionBlockSet.clear();
944 Blocks.clear();
945 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
946
947 Function *OuterFn = OI->getFunction();
948 CodeExtractorAnalysisCache CEAC(*OuterFn);
949 // If we generate code for the target device, we need to allocate
950 // struct for aggregate params in the device default alloca address space.
951 // OpenMP runtime requires that the params of the extracted functions are
952 // passed as zero address space pointers. This flag ensures that
953 // CodeExtractor generates correct code for extracted functions
954 // which are used by OpenMP runtime.
955 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
956 std::unique_ptr<CodeExtractor> Extractor =
957 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
958
959 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
960 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
961 << " Exit: " << OI->ExitBB->getName() << "\n");
962 assert(Extractor->isEligible() &&
963 "Expected OpenMP outlining to be possible!");
964
965 for (auto *V : OI->ExcludeArgsFromAggregate)
966 Extractor->excludeArgFromAggregate(V);
967
968 Function *OutlinedFn =
969 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
970
971 // Forward target-cpu, target-features attributes to the outlined function.
972 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
973 if (TargetCpuAttr.isStringAttribute())
974 OutlinedFn->addFnAttr(TargetCpuAttr);
975
976 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
977 if (TargetFeaturesAttr.isStringAttribute())
978 OutlinedFn->addFnAttr(TargetFeaturesAttr);
979
980 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
981 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
982 assert(OutlinedFn->getReturnType()->isVoidTy() &&
983 "OpenMP outlined functions should not return a value!");
984
985 // For compability with the clang CG we move the outlined function after the
986 // one with the parallel region.
987 OutlinedFn->removeFromParent();
988 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
989
990 // Remove the artificial entry introduced by the extractor right away, we
991 // made our own entry block after all.
992 {
993 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
994 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
995 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
996 // Move instructions from the to-be-deleted ArtificialEntry to the entry
997 // basic block of the parallel region. CodeExtractor generates
998 // instructions to unwrap the aggregate argument and may sink
999 // allocas/bitcasts for values that are solely used in the outlined region
1000 // and do not escape.
1001 assert(!ArtificialEntry.empty() &&
1002 "Expected instructions to add in the outlined region entry");
1003 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
1004 End = ArtificialEntry.rend();
1005 It != End;) {
1006 Instruction &I = *It;
1007 It++;
1008
1009 if (I.isTerminator()) {
1010 // Absorb any debug value that terminator may have
1011 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1012 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1013 continue;
1014 }
1015
1016 I.moveBeforePreserving(*OI->EntryBB,
1017 OI->EntryBB->getFirstInsertionPt());
1018 }
1019
1020 OI->EntryBB->moveBefore(&ArtificialEntry);
1021 ArtificialEntry.eraseFromParent();
1022 }
1023 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1024 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1025
1026 // Run a user callback, e.g. to add attributes.
1027 if (OI->PostOutlineCB)
1028 OI->PostOutlineCB(*OutlinedFn);
1029
1030 if (OI->FixUpNonEntryAllocas)
1032 }
1033
1034 // Remove work items that have been completed.
1035 OutlineInfos = std::move(DeferredOutlines);
1036
1037 // The createTarget functions embeds user written code into
1038 // the target region which may inject allocas which need to
1039 // be moved to the entry block of our target or risk malformed
1040 // optimisations by later passes (however, we do not block on
1041 // that here, it's up to the inserter to the list to do so).
1042 // This notbaly has to occur after the OutlinedInfo candidates
1043 // have been extracted so we have an end product that will not
1044 // be implicitly adversely affected by any raises unless
1045 // intentionally appended to the list.
1046 // NOTE: This only does so for ConstantData, it could be extended
1047 // to ConstantExpr's with further effort, however, they should
1048 // largely be folded when they get here. Extending it to runtime
1049 // defined/read+writeable allocation sizes would be non-trivial
1050 // (need to factor in movement of any stores to variables the
1051 // allocation size depends on, as well as the usual loads,
1052 // otherwise it'll yield the wrong result after movement) and
1053 // likely be more suitable as an LLVM optimisation pass.
1056
1057 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1058 [](EmitMetadataErrorKind Kind,
1059 const TargetRegionEntryInfo &EntryInfo) -> void {
1060 errs() << "Error of kind: " << Kind
1061 << " when emitting offload entries and metadata during "
1062 "OMPIRBuilder finalization \n";
1063 };
1064
1065 if (!OffloadInfoManager.empty())
1067
1068 // Rewrite uses of globals to their replacement declare target globals if
1069 // we are processing a device module.
1070 if (Config.isTargetDevice())
1071 applyDeclareTargetGlobalReplacements();
1072
1073 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1074 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1075 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1076 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1077 }
1078
1079 IsFinalized = true;
1080}
1081
1082bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1083
1085 GlobalValue *Original, GlobalValue *Replacement) {
1086 assert(Original && Replacement &&
1087 "Null values provided to registerDeclareTargetGlobalReplacement");
1088 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1089}
1090
1091void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1092 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1093 GlobalValue *OldGV = R.Original;
1094 GlobalValue *NewGV = R.Replacement;
1095
1096 assert(OldGV && NewGV &&
1097 "A null value was inserted into DeclareTargetGlobalReplacements");
1098
1099 // The assert above should catch this case, but this is kept to attempt
1100 // to proceed without issue when asserts are off.
1101 if (!OldGV || !NewGV)
1102 continue;
1103
1104 // The replacement global is a reference pointer that holds the
1105 // address of the device-resident storage. Every use must load the
1106 // reference pointer first and use the loaded address.
1107 //
1108 // Constant expression users (e.g. a constant GEP embedded in another
1109 // global's initializer or in an instruction) cannot have a load inserted
1110 // in place, so first expand any constant-expression users that live inside
1111 // functions into instructions. Any remaining constant users are handled
1112 // via a direct constant rewrite below as we cannot materialize a load
1113 // there.
1114 //
1115 // NOTE: We extend the constant rewrite to module scope, as we replace all
1116 // usages.
1117 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1119 /*RestrictToFunc=*/nullptr,
1120 /*RemoveDeadConstants=*/false);
1121
1122 IRBuilderBase::InsertPointGuard Guard(Builder);
1124 for (User *U : Users) {
1125 auto *Insn = dyn_cast<Instruction>(U);
1126 if (!Insn)
1127 continue;
1128
1129 // A PHI node cannot have a load inserted immediately before it, as PHIs
1130 // must remain grouped at the top of their basic block. So we need to
1131 // make sure any loads we emit are generated in the preceding edge, a
1132 // PHI may reference the global on more than one edge, so every matching
1133 // slot must be handled.
1134 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1135 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1136 if (PHI->getIncomingValue(I) != OldGV)
1137 continue;
1138
1139 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1140 Builder.SetInsertPoint(IncomingBB->getTerminator());
1141 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1142 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1143 PHI->setIncomingValue(I, EdgeLoad);
1144 }
1145 continue;
1146 }
1147
1148 Builder.SetInsertPoint(Insn);
1149 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1150 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1151
1152 // The replacement declare target global lives in the default address
1153 // space, whereas the original global may reside in a non-default
1154 // address space. In that case the initial lowering may have
1155 // emitted an addrspacecast that is no longer valid. Replace the
1156 // whole addrspacecast with the load and erase it rather than
1157 // feeding the load back into the (now pointless) cast.
1158 // NOTE: If we end up with replacement declare target globals in
1159 // non-zero AS's the below will need some minor extensions to have the
1160 // option to alter the address space cast to the new address space where
1161 // required rather than just replacing it.
1162 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1163 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1164 assert(NewGVAS == 0 &&
1165 "Non-default address space declare target global");
1166 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1167 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1168 if (DestAS == 0 && NewGVAS != OldGVAS) {
1169 ASC->replaceAllUsesWith(Load);
1170 ASC->eraseFromParent();
1171 continue;
1172 }
1173 }
1174
1175 Insn->replaceUsesOfWith(OldGV, Load);
1176 }
1177 }
1178
1180}
1181
1183 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1184}
1185
1187 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1188 auto *GV =
1189 new GlobalVariable(M, I32Ty,
1190 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1191 ConstantInt::get(I32Ty, Value), Name);
1192 GV->setVisibility(GlobalValue::HiddenVisibility);
1193
1194 return GV;
1195}
1196
1198 if (List.empty())
1199 return;
1200
1201 // Convert List to what ConstantArray needs.
1203 UsedArray.resize(List.size());
1204 for (unsigned I = 0, E = List.size(); I != E; ++I)
1206 cast<Constant>(&*List[I]), Builder.getPtrTy());
1207
1208 if (UsedArray.empty())
1209 return;
1210 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1211
1212 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1213 ConstantArray::get(ATy, UsedArray), Name);
1214
1215 GV->setSection("llvm.metadata");
1216}
1217
1220 OMPTgtExecModeFlags Mode) {
1221 auto *Int8Ty = Builder.getInt8Ty();
1222 auto *GVMode = new GlobalVariable(
1223 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1224 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1225 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1226 return GVMode;
1227}
1228
1230 uint32_t SrcLocStrSize,
1231 IdentFlag LocFlags,
1232 unsigned Reserve2Flags) {
1233 // Enable "C-mode".
1234 LocFlags |= OMP_IDENT_FLAG_KMPC;
1235
1236 Constant *&Ident =
1237 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1238 if (!Ident) {
1239 Constant *I32Null = ConstantInt::getNullValue(Int32);
1240 Constant *IdentData[] = {I32Null,
1241 ConstantInt::get(Int32, uint32_t(LocFlags)),
1242 ConstantInt::get(Int32, Reserve2Flags),
1243 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1244
1245 size_t SrcLocStrArgIdx = 4;
1246 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1248 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1249 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1250 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1251 Constant *Initializer =
1252 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1253
1254 // Look for existing encoding of the location + flags, not needed but
1255 // minimizes the difference to the existing solution while we transition.
1256 for (GlobalVariable &GV : M.globals())
1257 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1258 if (GV.getInitializer() == Initializer)
1259 Ident = &GV;
1260
1261 if (!Ident) {
1262 auto *GV = new GlobalVariable(
1263 M, OpenMPIRBuilder::Ident,
1264 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1266 M.getDataLayout().getDefaultGlobalsAddressSpace());
1267 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1268 GV->setAlignment(Align(8));
1269 Ident = GV;
1270 }
1271 }
1272
1273 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1274}
1275
1277 uint32_t &SrcLocStrSize) {
1278 SrcLocStrSize = LocStr.size();
1279 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1280 if (!SrcLocStr) {
1281 Constant *Initializer =
1282 ConstantDataArray::getString(M.getContext(), LocStr);
1283
1284 // Look for existing encoding of the location, not needed but minimizes the
1285 // difference to the existing solution while we transition.
1286 for (GlobalVariable &GV : M.globals())
1287 if (GV.isConstant() && GV.hasInitializer() &&
1288 GV.getInitializer() == Initializer)
1289 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1290
1291 SrcLocStr = Builder.CreateGlobalString(
1292 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1293 &M);
1294 }
1295 return SrcLocStr;
1296}
1297
1299 StringRef FileName,
1300 unsigned Line, unsigned Column,
1301 uint32_t &SrcLocStrSize) {
1302 SmallString<128> Buffer;
1303 Buffer.push_back(';');
1304 Buffer.append(FileName);
1305 Buffer.push_back(';');
1306 Buffer.append(FunctionName);
1307 Buffer.push_back(';');
1308 Buffer.append(std::to_string(Line));
1309 Buffer.push_back(';');
1310 Buffer.append(std::to_string(Column));
1311 Buffer.push_back(';');
1312 Buffer.push_back(';');
1313 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1314}
1315
1316Constant *
1318 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1319 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1320}
1321
1323 uint32_t &SrcLocStrSize,
1324 const Function *F) {
1325 DILocation *DIL = DL.get();
1326 if (!DIL)
1327 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1328 StringRef FileName =
1329 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1330 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1331 if (Function.empty() && F)
1332 Function = F->getName();
1333 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1334 DIL->getColumn(), SrcLocStrSize);
1335}
1336
1338 uint32_t &SrcLocStrSize) {
1339 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1340 Loc.IP.getNodeParent()->getParent());
1341}
1342
1345 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1346 "omp_global_thread_num");
1347}
1348
1349OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1350 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1351 ArrayRef<Type *> ResultPtrTys,
1352 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1353 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1354 "expected one result pointer type per in_reduction item");
1355 if (!updateToLocation(Loc))
1356 return Loc.IP;
1357 if (OrigPtrs.empty())
1358 return Builder.saveIP();
1359
1360 // Compute the executing thread's gtid once for the whole target body and
1361 // reuse it for every in_reduction lookup, so a target with several
1362 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1363 // item.
1364 uint32_t SrcLocStrSize;
1365 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1366 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1367 Value *Gtid = getOrCreateThreadID(Ident);
1368
1369 // The runtime entry point takes (and returns) a generic, default-address-
1370 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1371 // taskgroups to find the matching task_reduction registration for the item.
1372 Type *PtrTy = PointerType::getUnqual(M.getContext());
1373 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1374 FunctionCallee GetThData =
1375 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1376
1377 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1378 // Normalize a non-default-address-space original pointer to the generic
1379 // address space before the call.
1380 Value *OrigPtr = OrigPtrs[Idx];
1381 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1382 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1383 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1384
1385 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1386 "omp.inred.priv");
1387
1388 // Cast the returned private pointer back to the requested address space
1389 // when it differs.
1390 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1391 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1392 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1393
1394 MapPrivateCB(Idx, Priv);
1395 }
1396 return Builder.saveIP();
1397}
1398
1401 bool ForceSimpleCall, bool CheckCancelFlag) {
1402 if (!updateToLocation(Loc))
1403 return Loc.IP;
1404
1405 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1406 // __kmpc_barrier(loc, thread_id);
1407
1408 IdentFlag BarrierLocFlags;
1409 switch (Kind) {
1410 case OMPD_for:
1411 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1412 break;
1413 case OMPD_sections:
1414 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1415 break;
1416 case OMPD_single:
1417 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1418 break;
1419 case OMPD_barrier:
1420 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1421 break;
1422 default:
1423 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1424 break;
1425 }
1426
1427 uint32_t SrcLocStrSize;
1428 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1429 Value *Args[] = {
1430 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1431 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1432
1433 // If we are in a cancellable parallel region, barriers are cancellation
1434 // points.
1435 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1436 bool UseCancelBarrier =
1437 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1438
1440 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1441 ? OMPRTL___kmpc_cancel_barrier
1442 : OMPRTL___kmpc_barrier),
1443 Args);
1444
1445 if (UseCancelBarrier && CheckCancelFlag)
1446 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1447 return Err;
1448
1449 return Builder.saveIP();
1450}
1451
1454 Value *IfCondition,
1455 omp::Directive CanceledDirective) {
1456 if (!updateToLocation(Loc))
1457 return Loc.IP;
1458
1459 // LLVM utilities like blocks with terminators.
1460 auto *UI = Builder.CreateUnreachable();
1461
1462 Instruction *ThenTI = UI, *ElseTI = nullptr;
1463 if (IfCondition) {
1464 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1465
1466 // Even if the if condition evaluates to false, this should count as a
1467 // cancellation point
1468 Builder.SetInsertPoint(ElseTI);
1469 auto ElseIP = Builder.saveIP();
1470
1472 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1473 if (!IPOrErr)
1474 return IPOrErr;
1475 }
1476
1477 Builder.SetInsertPoint(ThenTI);
1478
1479 Value *CancelKind = nullptr;
1480 switch (CanceledDirective) {
1481#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1482 case DirectiveEnum: \
1483 CancelKind = Builder.getInt32(Value); \
1484 break;
1485#include "llvm/Frontend/OpenMP/OMPKinds.def"
1486 default:
1487 llvm_unreachable("Unknown cancel kind!");
1488 }
1489
1490 uint32_t SrcLocStrSize;
1491 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1492 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1493 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1495 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1496
1497 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1498 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1499 return Err;
1500
1501 // Update the insertion point and remove the terminator we introduced.
1502 Builder.SetInsertPoint(UI->getParent());
1503 UI->eraseFromParent();
1504
1505 return Builder.saveIP();
1506}
1507
1510 omp::Directive CanceledDirective) {
1511 if (!updateToLocation(Loc))
1512 return Loc.IP;
1513
1514 // LLVM utilities like blocks with terminators.
1515 auto *UI = Builder.CreateUnreachable();
1516 Builder.SetInsertPoint(UI);
1517
1518 Value *CancelKind = nullptr;
1519 switch (CanceledDirective) {
1520#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1521 case DirectiveEnum: \
1522 CancelKind = Builder.getInt32(Value); \
1523 break;
1524#include "llvm/Frontend/OpenMP/OMPKinds.def"
1525 default:
1526 llvm_unreachable("Unknown cancel kind!");
1527 }
1528
1529 uint32_t SrcLocStrSize;
1530 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1531 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1532 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1534 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1535
1536 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1537 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1538 return Err;
1539
1540 // Update the insertion point and remove the terminator we introduced.
1541 Builder.SetInsertPoint(UI->getParent());
1542 UI->eraseFromParent();
1543
1544 return Builder.saveIP();
1545}
1546
1548 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1549 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1550 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1551 if (!updateToLocation(Loc))
1552 return Loc.IP;
1553
1554 Builder.restoreIP(AllocaIP);
1555 auto *KernelArgsPtr =
1556 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1558
1559 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1560 llvm::Value *Arg =
1561 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1562 Builder.CreateAlignedStore(
1563 KernelArgs[I], Arg,
1564 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1565 }
1566
1567 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1568 NumThreads, HostPtr, KernelArgsPtr};
1569
1571 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1572 OffloadingArgs);
1573
1574 return Builder.saveIP();
1575}
1576
1578 const LocationDescription &Loc, Value *OutlinedFnID,
1579 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1580 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1581
1582 if (!updateToLocation(Loc))
1583 return Loc.IP;
1584
1585 // On top of the arrays that were filled up, the target offloading call
1586 // takes as arguments the device id as well as the host pointer. The host
1587 // pointer is used by the runtime library to identify the current target
1588 // region, so it only has to be unique and not necessarily point to
1589 // anything. It could be the pointer to the outlined function that
1590 // implements the target region, but we aren't using that so that the
1591 // compiler doesn't need to keep that, and could therefore inline the host
1592 // function if proven worthwhile during optimization.
1593
1594 // From this point on, we need to have an ID of the target region defined.
1595 assert(OutlinedFnID && "Invalid outlined function ID!");
1596 (void)OutlinedFnID;
1597
1598 // Return value of the runtime offloading call.
1599 Value *Return = nullptr;
1600
1601 // Arguments for the target kernel.
1602 SmallVector<Value *> ArgsVector;
1603 getKernelArgsVector(Args, Builder, ArgsVector);
1604
1605 // The target region is an outlined function launched by the runtime
1606 // via calls to __tgt_target_kernel().
1607 //
1608 // Note that on the host and CPU targets, the runtime implementation of
1609 // these calls simply call the outlined function without forking threads.
1610 // The outlined functions themselves have runtime calls to
1611 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1612 // the compiler in emitTeamsCall() and emitParallelCall().
1613 //
1614 // In contrast, on the NVPTX target, the implementation of
1615 // __tgt_target_teams() launches a GPU kernel with the requested number
1616 // of teams and threads so no additional calls to the runtime are required.
1617 // Check the error code and execute the host version if required.
1618 Builder.restoreIP(emitTargetKernel(
1619 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1620 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1621
1622 BasicBlock *OffloadFailedBlock =
1623 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1624 BasicBlock *OffloadContBlock =
1625 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1626 Value *Failed = Builder.CreateIsNotNull(Return);
1627 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1628
1629 auto CurFn = Builder.GetInsertBlock()->getParent();
1630 emitBlock(OffloadFailedBlock, CurFn);
1631 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1632 if (!AfterIP)
1633 return AfterIP.takeError();
1634 Builder.restoreIP(*AfterIP);
1635 emitBranch(OffloadContBlock);
1636 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1637 return Builder.saveIP();
1638}
1639
1641 Value *CancelFlag, omp::Directive CanceledDirective) {
1642 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1643 "Unexpected cancellation!");
1644
1645 // For a cancel barrier we create two new blocks.
1646 BasicBlock *BB = Builder.GetInsertBlock();
1647 BasicBlock *NonCancellationBlock;
1648 if (Builder.GetInsertPoint() == BB->end()) {
1649 // TODO: This branch will not be needed once we moved to the
1650 // OpenMPIRBuilder codegen completely.
1651 NonCancellationBlock = BasicBlock::Create(
1652 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1653 } else {
1654 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1656 Builder.SetInsertPoint(BB);
1657 }
1658 BasicBlock *CancellationBlock = BasicBlock::Create(
1659 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1660
1661 // Jump to them based on the return value.
1662 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1663 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1664 /* TODO weight */ nullptr, nullptr);
1665
1666 // From the cancellation block we finalize all variables and go to the
1667 // post finalization block that is known to the FiniCB callback.
1668 auto &FI = FinalizationStack.back();
1669 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1670 if (!FiniBBOrErr)
1671 return FiniBBOrErr.takeError();
1672 Builder.SetInsertPoint(CancellationBlock);
1673 Builder.CreateBr(*FiniBBOrErr);
1674
1675 // The continuation block is where code generation continues.
1676 Builder.SetInsertPoint(NonCancellationBlock->begin());
1677 return Error::success();
1678}
1679
1680/// Create wrapper function used to gather the outlined function's argument
1681/// structure from a shared buffer and to forward them to it when running in
1682/// Generic mode.
1683///
1684/// The outlined function is expected to receive 2 integer arguments followed by
1685/// an optional pointer argument to an argument structure holding the rest.
1687 Function &OutlinedFn) {
1688 size_t NumArgs = OutlinedFn.arg_size();
1689 assert((NumArgs == 2 || NumArgs == 3) &&
1690 "expected a 2-3 argument parallel outlined function");
1691 bool UseArgStruct = NumArgs == 3;
1692
1693 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1694 IRBuilder<>::InsertPointGuard IPG(Builder);
1695 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1696 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1697 /*isVarArg=*/false);
1698 auto *WrapperFn =
1700 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1701
1702 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1703 WrapperFn->addParamAttr(0, Attribute::ZExt);
1704 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1705
1706 BasicBlock *EntryBB =
1707 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1708 Builder.SetInsertPoint(EntryBB);
1709
1710 // Allocation.
1711 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1712 /*ArraySize=*/nullptr, "addr");
1713 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1714 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1715 AddrAlloca->getName() + ".ascast");
1716
1717 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1718 /*ArraySize=*/nullptr, "zero");
1719 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1720 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1721 ZeroAlloca->getName() + ".ascast");
1722
1723 Value *ArgsAlloca = nullptr;
1724 if (UseArgStruct) {
1725 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1726 /*ArraySize=*/nullptr, "global_args");
1727 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1728 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1729 ArgsAlloca->getName() + ".ascast");
1730 }
1731
1732 // Initialization.
1733 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1734 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1735 if (UseArgStruct) {
1736 Builder.CreateCall(
1737 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1738 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1739 {ArgsAlloca});
1740 }
1741
1742 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1743
1744 // Load structArg from global_args.
1745 if (UseArgStruct) {
1746 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1747 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1748 {Builder.getInt64(0)});
1749 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1750 Args.push_back(StructArg);
1751 }
1752
1753 // Call the outlined function holding the parallel body.
1754 Builder.CreateCall(&OutlinedFn, Args);
1755 Builder.CreateRetVoid();
1756
1757 return WrapperFn;
1758}
1759
1760// Callback used to create OpenMP runtime calls to support
1761// omp parallel clause for the device.
1762// We need to use this callback to replace call to the OutlinedFn in OuterFn
1763// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1765 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1766 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1767 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1768 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1769 assert(OutlinedFn.arg_size() >= 2 &&
1770 "Expected at least tid and bounded tid as arguments");
1771 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1772
1773 // Add some known attributes.
1774 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1775 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1776 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1777 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1778 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1779 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1780
1781 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1782 assert(CI && "Expected call instruction to outlined function");
1783 CI->getParent()->setName("omp_parallel");
1784
1785 Builder.SetInsertPoint(CI);
1786 Type *PtrTy = OMPIRBuilder->VoidPtr;
1787
1788 // Add alloca for kernel args
1789 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1790 Builder.SetInsertPoint(OuterAllocaBB->getFirstInsertionPt());
1791 AllocaInst *ArgsAlloca =
1792 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1793 Value *Args = ArgsAlloca;
1794 // Add address space cast if array for storing arguments is not allocated
1795 // in address space 0
1796 if (ArgsAlloca->getAddressSpace())
1797 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1798 Builder.restoreIP(CurrentIP);
1799
1800 // Store captured vars which are used by kmpc_parallel_60
1801 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1802 Value *V = *(CI->arg_begin() + 2 + Idx);
1803 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1804 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1805 Builder.CreateStore(V, StoreAddress);
1806 }
1807
1808 Value *Cond =
1809 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1810 : Builder.getInt32(1);
1811 Value *NumThreadsArg =
1812 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1813 : Builder.getInt32(-1);
1814
1815 // If this is not a Generic kernel, we can skip generating the wrapper.
1816 Value *WrapperFn;
1817 if (isGenericKernel(*OuterFn))
1818 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1819 else
1820 WrapperFn = Constant::getNullValue(PtrTy);
1821
1822 // Build kmpc_parallel_60 call
1823 Value *Parallel60CallArgs[] = {
1824 /* identifier*/ Ident,
1825 /* global thread num*/ ThreadID,
1826 /* if expression */ Cond,
1827 /* number of threads */ NumThreadsArg,
1828 /* Proc bind */ Builder.getInt32(-1),
1829 /* outlined function */ &OutlinedFn,
1830 /* wrapper function */ WrapperFn,
1831 /* arguments of the outlined funciton*/ Args,
1832 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1833 /* strict for number of threads */ Builder.getInt32(0)};
1834
1835 FunctionCallee RTLFn =
1836 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1837
1838 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1839
1840 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1841 << *Builder.GetInsertBlock()->getParent() << "\n");
1842
1843 // Initialize the local TID stack location with the argument value.
1844 Builder.SetInsertPoint(PrivTID);
1845 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1846 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1847 PrivTIDAddr);
1848
1849 // Remove redundant call to the outlined function.
1850 CI->eraseFromParent();
1851
1852 for (Instruction *I : ToBeDeleted) {
1853 I->eraseFromParent();
1854 }
1855}
1856
1857// Callback used to create OpenMP runtime calls to support
1858// omp parallel clause for the host.
1859// We need to use this callback to replace call to the OutlinedFn in OuterFn
1860// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1861static void
1863 Function *OuterFn, Value *Ident, Value *IfCondition,
1864 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1865 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1866 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1867 FunctionCallee RTLFn;
1868 if (IfCondition) {
1869 RTLFn =
1870 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1871 } else {
1872 RTLFn =
1873 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1874 }
1875 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1876 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1877 LLVMContext &Ctx = F->getContext();
1878 MDBuilder MDB(Ctx);
1879 // Annotate the callback behavior of the __kmpc_fork_call:
1880 // - The callback callee is argument number 2 (microtask).
1881 // - The first two arguments of the callback callee are unknown (-1).
1882 // - All variadic arguments to the __kmpc_fork_call are passed to the
1883 // callback callee.
1884 F->addMetadata(LLVMContext::MD_callback,
1886 2, {-1, -1},
1887 /* VarArgsArePassed */ true)}));
1888 }
1889 }
1890 // Add some known attributes.
1891 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1892 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1893 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1894
1895 assert(OutlinedFn.arg_size() >= 2 &&
1896 "Expected at least tid and bounded tid as arguments");
1897 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1898
1899 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1900
1901 // CodeExtractor creates an alloca aggregate struct containing pointers to all
1902 // the captured values. The address of this struct is then passed as a
1903 // parameter to the outlined function. This parameter of the outlined function
1904 // can be marked noalias because CodeExractor + this host OpenMP lowering
1905 // maintains the following invariants:
1906 // - The caller initializes the fields before starting any callbacks.
1907 // - Neither the caller, OpenMP runtime, nor callbacks modify the structure
1908 // while callbacks are executing.
1909 // - The callbacks only load captured values from the structure; they do not
1910 // use it to write results back to the caller. This is generated code; the
1911 // user has no way to modify the structure directly.
1912 //
1913 // Multiple callbacks may read the same structure. This is allowed by noalias,
1914 // which permits aliasing accesses to memory that is not modified during the
1915 // call.
1916 //
1917 // The synchronous fork keeps the structure alive until all callbacks return,
1918 // which justifies nofreeobj. Neither attribute extends to the objects pointed
1919 // to by the captured fields.
1920 if (NumCapturedVars) {
1921 assert(NumCapturedVars == 1 && "Expected an aggregate capture argument");
1924 ->getAllocatedType()
1925 ->isStructTy() &&
1926 "Expected a stack-allocated capture structure");
1927
1928 OutlinedFn.addParamAttr(2, Attribute::NoAlias);
1929 OutlinedFn.addParamAttr(2, Attribute::NoFreeObj);
1930 }
1931
1932 CI->getParent()->setName("omp_parallel");
1933 Builder.SetInsertPoint(CI);
1934
1935 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1936 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1937 &OutlinedFn};
1938
1939 SmallVector<Value *, 16> RealArgs;
1940 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1941 if (IfCondition) {
1942 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1943 RealArgs.push_back(Cond);
1944 }
1945 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1946
1947 // __kmpc_fork_call_if always expects a void ptr as the last argument
1948 // If there are no arguments, pass a null pointer.
1949 auto PtrTy = OMPIRBuilder->VoidPtr;
1950 if (IfCondition && NumCapturedVars == 0) {
1951 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1952 RealArgs.push_back(NullPtrValue);
1953 }
1954
1955 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1956
1957 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1958 << *Builder.GetInsertBlock()->getParent() << "\n");
1959
1960 // Initialize the local TID stack location with the argument value.
1961 Builder.SetInsertPoint(PrivTID);
1962 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1963 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1964 PrivTIDAddr);
1965
1966 // Remove redundant call to the outlined function.
1967 CI->eraseFromParent();
1968
1969 for (Instruction *I : ToBeDeleted) {
1970 I->eraseFromParent();
1971 }
1972}
1973
1975 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1976 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1977 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1978 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1979 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1980
1981 if (!updateToLocation(Loc))
1982 return Loc.IP;
1983
1984 uint32_t SrcLocStrSize;
1985 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1986 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1987 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1988 (ProcBind != OMP_PROC_BIND_default);
1989 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1990 // If we generate code for the target device, we need to allocate
1991 // struct for aggregate params in the device default alloca address space.
1992 // OpenMP runtime requires that the params of the extracted functions are
1993 // passed as zero address space pointers. This flag ensures that extracted
1994 // function arguments are declared in zero address space
1995 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1996
1997 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1998 // only if we compile for host side.
1999 if (NumThreads && !Config.isTargetDevice()) {
2000 Value *Args[] = {
2001 Ident, ThreadID,
2002 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
2004 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
2005 }
2006
2007 if (ProcBind != OMP_PROC_BIND_default) {
2008 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
2009 Value *Args[] = {
2010 Ident, ThreadID,
2011 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
2013 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
2014 }
2015
2016 BasicBlock *InsertBB = Builder.GetInsertBlock();
2017 Function *OuterFn = InsertBB->getParent();
2018
2019 // Save the outer alloca block because the insertion iterator may get
2020 // invalidated and we still need this later.
2021 BasicBlock *OuterAllocaBlock = OuterAllocIP.getNodeParent();
2022
2023 // Vector to remember instructions we used only during the modeling but which
2024 // we want to delete at the end.
2026
2027 // Change the location to the outer alloca insertion point to create and
2028 // initialize the allocas we pass into the parallel region.
2029 InsertPointTy NewOuter(OuterAllocaBlock->begin());
2030 Builder.restoreIP(NewOuter);
2031 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
2032 AllocaInst *ZeroAddrAlloca =
2033 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
2034 Instruction *TIDAddr = TIDAddrAlloca;
2035 Instruction *ZeroAddr = ZeroAddrAlloca;
2036 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
2037 // Add additional casts to enforce pointers in zero address space
2038 TIDAddr = new AddrSpaceCastInst(
2039 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2040 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2041 ToBeDeleted.push_back(TIDAddr);
2042 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2043 PointerType ::get(M.getContext(), 0),
2044 "zero.addr.ascast");
2045 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2046 ToBeDeleted.push_back(ZeroAddr);
2047 }
2048
2049 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2050 // associated arguments in the outlined function, so we delete them later.
2051 ToBeDeleted.push_back(TIDAddrAlloca);
2052 ToBeDeleted.push_back(ZeroAddrAlloca);
2053
2054 // Create an artificial insertion point that will also ensure the blocks we
2055 // are about to split are not degenerated.
2056 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2057 UI->setDebugLoc(Loc.DL);
2058
2059 BasicBlock *EntryBB = UI->getParent();
2060 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2061 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2062 BasicBlock *PRegPreFiniBB =
2063 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2064 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2065
2066 auto FiniCBWrapper = [&](InsertPointTy IP) {
2067 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2068 // target to the region exit block.
2069 if (IP == IP.getNodeParent()->end()) {
2071 Builder.restoreIP(IP);
2072 Instruction *I = Builder.CreateBr(PRegExitBB);
2073 IP = I->getIterator();
2074 }
2075 assert(IP.getNodeParent()->getTerminator()->getNumSuccessors() == 1 &&
2076 IP.getNodeParent()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2077 "Unexpected insertion point for finalization call!");
2078 return FiniCB(IP);
2079 };
2080
2081 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2082
2083 // Generate the privatization allocas in the block that will become the entry
2084 // of the outlined function.
2085 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2086 InsertPointTy InnerAllocaIP = Builder.saveIP();
2087
2088 AllocaInst *PrivTIDAddr =
2089 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2090 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2091
2092 // Add some fake uses for OpenMP provided arguments.
2093 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2094 Instruction *ZeroAddrUse =
2095 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2096 ToBeDeleted.push_back(ZeroAddrUse);
2097
2098 // EntryBB
2099 // |
2100 // V
2101 // PRegionEntryBB <- Privatization allocas are placed here.
2102 // |
2103 // V
2104 // PRegionBodyBB <- BodeGen is invoked here.
2105 // |
2106 // V
2107 // PRegPreFiniBB <- The block we will start finalization from.
2108 // |
2109 // V
2110 // PRegionExitBB <- A common exit to simplify block collection.
2111 //
2112
2113 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2114
2115 // Let the caller create the body.
2116 assert(BodyGenCB && "Expected body generation callback!");
2117 InsertPointTy CodeGenIP(PRegBodyBB->begin());
2118 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2119 return Err;
2120
2121 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2122
2123 // If OuterFn is a Generic kernel, we need to use device shared memory to
2124 // allocate argument structures. Otherwise, we use stack allocations as usual.
2125 bool UsesDeviceSharedMemory =
2126 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2127 std::unique_ptr<OutlineInfo> OI =
2128 UsesDeviceSharedMemory
2129 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2130 : std::make_unique<OutlineInfo>();
2131
2132 if (Config.isTargetDevice()) {
2133 // Generate OpenMP target specific runtime call
2134 OI->PostOutlineCB = [=, ToBeDeletedVec =
2135 std::move(ToBeDeleted)](Function &OutlinedFn) {
2136 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2137 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2138 ThreadID, ToBeDeletedVec);
2139 };
2140 } else {
2141 // Generate OpenMP host runtime call
2142 OI->PostOutlineCB = [=, ToBeDeletedVec =
2143 std::move(ToBeDeleted)](Function &OutlinedFn) {
2144 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2145 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2146 };
2147 }
2148
2149 OI->FixUpNonEntryAllocas = true;
2150 OI->OuterAllocBB = OuterAllocaBlock;
2151 OI->EntryBB = PRegEntryBB;
2152 OI->ExitBB = PRegExitBB;
2153 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2154 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2155
2156 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2158 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2159
2160 CodeExtractorAnalysisCache CEAC(*OuterFn);
2161 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2162 /* AggregateArgs */ false,
2163 /* BlockFrequencyInfo */ nullptr,
2164 /* BranchProbabilityInfo */ nullptr,
2165 /* AssumptionCache */ nullptr,
2166 /* AllowVarArgs */ true,
2167 /* AllowAlloca */ true,
2168 /* AllocationBlock */ OuterAllocaBlock,
2169 /* DeallocationBlocks */ {},
2170 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2171
2172 // Find inputs to, outputs from the code region.
2173 BasicBlock *CommonExit = nullptr;
2174 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2175 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2176
2177 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2178 /*CollectGlobalInputs=*/true);
2179
2180 Inputs.remove_if([&](Value *I) {
2182 return GV->getValueType() == OpenMPIRBuilder::Ident;
2183
2184 return false;
2185 });
2186
2187 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2188
2189 FunctionCallee TIDRTLFn =
2190 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2191
2192 auto PrivHelper = [&](Value &V) -> Error {
2193 if (&V == TIDAddr || &V == ZeroAddr) {
2194 OI->ExcludeArgsFromAggregate.push_back(&V);
2195 return Error::success();
2196 }
2197
2199 for (Use &U : V.uses())
2200 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2201 if (ParallelRegionBlockSet.count(UserI->getParent()))
2202 Uses.insert(&U);
2203
2204 // __kmpc_fork_call expects extra arguments as pointers. If the input
2205 // already has a pointer type, everything is fine. Otherwise, store the
2206 // value onto stack and load it back inside the to-be-outlined region. This
2207 // will ensure only the pointer will be passed to the function.
2208 // FIXME: if there are more than 15 trailing arguments, they must be
2209 // additionally packed in a struct.
2210 Value *Inner = &V;
2211 if (!V.getType()->isPointerTy()) {
2213 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2214
2215 Builder.restoreIP(OuterAllocIP);
2216 Value *Ptr;
2217 if (UsesDeviceSharedMemory) {
2218 // Use device shared memory instead, if needed.
2219 Ptr = createOMPAllocShared(Builder, V.getType(),
2220 V.getName() + ".reloaded");
2221 for (BasicBlock *DeallocBlock : OuterDeallocBlocks) {
2222 assert(DeallocBlock->getParent() ==
2223 OuterAllocIP.getNodeParent()->getParent() &&
2224 "Dealloc block must be in the allocation's function to reuse "
2225 "its debug location");
2226 createOMPFreeShared({DeallocBlock->getFirstInsertionPt(),
2227 Builder.getCurrentDebugLocation()},
2228 Ptr, V.getType());
2229 }
2230 } else {
2231 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2232 V.getName() + ".reloaded");
2233 }
2234
2235 // Store to stack at end of the block that currently branches to the entry
2236 // block of the to-be-outlined region.
2237 Builder.SetInsertPoint(InsertBB->getTerminator()->getIterator());
2238 Builder.CreateStore(&V, Ptr);
2239
2240 // Load back next to allocations in the to-be-outlined region.
2241 Builder.restoreIP(InnerAllocaIP);
2242 Inner = Builder.CreateLoad(V.getType(), Ptr);
2243 }
2244
2245 Value *ReplacementValue = nullptr;
2246 CallInst *CI = dyn_cast<CallInst>(&V);
2247 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2248 ReplacementValue = PrivTID;
2249 } else {
2250 InsertPointOrErrorTy AfterIP =
2251 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2252 if (!AfterIP)
2253 return AfterIP.takeError();
2254 Builder.restoreIP(*AfterIP);
2255 InnerAllocaIP =
2256 InnerAllocaIP.getNodeParent()->getTerminator()->getIterator();
2257
2258 assert(ReplacementValue &&
2259 "Expected copy/create callback to set replacement value!");
2260 if (ReplacementValue == &V)
2261 return Error::success();
2262 }
2263
2264 for (Use *UPtr : Uses)
2265 UPtr->set(ReplacementValue);
2266
2267 return Error::success();
2268 };
2269
2270 // Reset the inner alloca insertion as it will be used for loading the values
2271 // wrapped into pointers before passing them into the to-be-outlined region.
2272 // Configure it to insert immediately after the fake use of zero address so
2273 // that they are available in the generated body and so that the
2274 // OpenMP-related values (thread ID and zero address pointers) remain leading
2275 // in the argument list.
2276 InnerAllocaIP = ZeroAddrUse->getNextNode()->getIterator();
2277
2278 // Reset the outer alloca insertion point to the entry of the relevant block
2279 // in case it was invalidated.
2280 OuterAllocIP = OuterAllocaBlock->getFirstInsertionPt();
2281
2282 for (Value *Input : Inputs) {
2283 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2284 if (Error Err = PrivHelper(*Input))
2285 return Err;
2286 }
2287 LLVM_DEBUG({
2288 for (Value *Output : Outputs)
2289 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2290 });
2291 assert(Outputs.empty() &&
2292 "OpenMP outlining should not produce live-out values!");
2293
2294 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2295 LLVM_DEBUG({
2296 for (auto *BB : Blocks)
2297 dbgs() << " PBR: " << BB->getName() << "\n";
2298 });
2299
2300 // Adjust the finalization stack, verify the adjustment, and call the
2301 // finalize function a last time to finalize values between the pre-fini
2302 // block and the exit block if we left the parallel "the normal way".
2303 auto FiniInfo = FinalizationStack.pop_back_val();
2304 (void)FiniInfo;
2305 assert(FiniInfo.DK == OMPD_parallel &&
2306 "Unexpected finalization stack state!");
2307
2308 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2309
2310 InsertPointTy PreFiniIP(PRegPreFiniTI->getIterator());
2311 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2312 if (!FiniBBOrErr)
2313 return FiniBBOrErr.takeError();
2314 {
2316 Builder.restoreIP(PreFiniIP);
2317 Builder.CreateBr(*FiniBBOrErr);
2318 // There's currently a branch to omp.par.exit. Delete it. We will get there
2319 // via the fini block
2320 Builder.GetInsertBlock()->getTerminator()->eraseFromParent();
2321 }
2322
2323 // Register the outlined info.
2324 addOutlineInfo(std::move(OI));
2325
2326 InsertPointTy AfterIP(UI->getParent()->end());
2327 UI->eraseFromParent();
2328
2329 return AfterIP;
2330}
2331
2333 // Build call void __kmpc_flush(ident_t *loc)
2334 uint32_t SrcLocStrSize;
2335 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2336 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2337
2339 Args);
2340}
2341
2343 if (!updateToLocation(Loc))
2344 return;
2345 emitFlush(Loc);
2346}
2347
2349 Value *Message) {
2350 if (!updateToLocation(Loc))
2351 return;
2352
2353 // Build call void __kmpc_error(ident_t *loc, int severity,
2354 // const char *message)
2355 uint32_t SrcLocStrSize;
2356 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2357 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2358 // Severity: 1 = warning, 2 = fatal.
2359 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2360 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2361 Value *Args[] = {Ident, Severity, MessageArg};
2362
2364 Args);
2365}
2366
2368 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2369 uint32_t SrcLocStrSize;
2370 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2371 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2372 Constant *I32Null = ConstantInt::getNullValue(Int32);
2373 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2374
2376 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2377}
2378
2384
2386 const DependData &Dep) {
2387 // Store the pointer to the variable
2388 Value *Addr = Builder.CreateStructGEP(
2389 DependInfo, Entry,
2390 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2391 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2392 Builder.CreateStore(DepValPtr, Addr);
2393 // Store the size of the variable
2394 Value *Size = Builder.CreateStructGEP(
2395 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2396 Builder.CreateStore(
2397 ConstantInt::get(SizeTy,
2398 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2399 Size);
2400 // Store the dependency kind
2401 Value *Flags = Builder.CreateStructGEP(
2402 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2403 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2404 static_cast<unsigned int>(Dep.DepKind)),
2405 Flags);
2406}
2407
2408// Processes the dependencies in Dependencies and does the following
2409// - Allocates space on the stack of an array of DependInfo objects
2410// - Populates each DependInfo object with relevant information of
2411// the corresponding dependence.
2412// - All code is inserted in the entry block of the current function.
2414 OpenMPIRBuilder &OMPBuilder,
2416 // Early return if we have no dependencies to process
2417 if (Dependencies.empty())
2418 return nullptr;
2419
2420 // Given a vector of DependData objects, in this function we create an
2421 // array on the stack that holds kmp_depend_info objects corresponding
2422 // to each dependency. This is then passed to the OpenMP runtime.
2423 // For example, if there are 'n' dependencies then the following psedo
2424 // code is generated. Assume the first dependence is on a variable 'a'
2425 //
2426 // \code{c}
2427 // DepArray = alloc(n x sizeof(kmp_depend_info);
2428 // idx = 0;
2429 // DepArray[idx].base_addr = ptrtoint(&a);
2430 // DepArray[idx].len = 8;
2431 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2432 // ++idx;
2433 // DepArray[idx].base_addr = ...;
2434 // \endcode
2435
2436 IRBuilderBase &Builder = OMPBuilder.Builder;
2437 Type *DependInfo = OMPBuilder.DependInfo;
2438
2439 Value *DepArray = nullptr;
2440 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2441 {
2442 // Use a InsertPointGuard to restore the location back along with the
2443 // insertion point.
2444 IRBuilderBase::InsertPointGuard IPGuard(Builder);
2445 Builder.SetInsertPoint(
2446 Builder.GetInsertBlock()->getParent()->getEntryBlock().getTerminator());
2447 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2448 }
2449
2450 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2451 Value *Base =
2452 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2453 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2454 }
2455 return DepArray;
2456}
2457
2459 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2460 // global_tid);
2461 uint32_t SrcLocStrSize;
2462 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2463 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2464 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2465
2466 // Ignore return result until untied tasks are supported.
2468 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2469}
2470
2472 DependenciesInfo Dependencies,
2473 bool IsNowait) {
2474 if (!updateToLocation(Loc))
2475 return;
2476
2477 Value *DepArray = nullptr;
2478 Type *DepArrayTy = nullptr;
2479 Value *NumDeps = nullptr;
2480 if (Dependencies.DepArray) {
2481 DepArray = Dependencies.DepArray;
2482 NumDeps = Dependencies.NumDeps;
2483 } else if (!Dependencies.Deps.empty()) {
2484 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2485 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2486 {
2488 BasicBlock &entryBB =
2489 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2490 Builder.SetInsertPoint(entryBB.getFirstInsertionPt());
2491 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2492 }
2493
2494 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2495 Value *Base =
2496 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2497 this->emitTaskDependency(Builder, Base, Dep);
2498 }
2499 }
2500
2501 if (DepArray) {
2502 uint32_t SrcLocStrSize;
2503 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2504 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2505 Value *Args[] = {
2506 Ident,
2507 getOrCreateThreadID(Ident),
2508 NumDeps,
2509 DepArray,
2510 ConstantInt::get(Builder.getInt32Ty(), 0),
2512 ConstantInt::get(Builder.getInt32Ty(), IsNowait)};
2515 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2516 Args);
2517 } else {
2519 }
2520}
2521
2522/// Create the task duplication function passed to kmpc_taskloop.
2523Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2524 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2525 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2526 if (!DupCB)
2528 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2529
2530 // From OpenMP Runtime p_task_dup_t:
2531 // Routine optionally generated by the compiler for setting the lastprivate
2532 // flag and calling needed constructors for private/firstprivate objects (used
2533 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2534 // lastprivate flag.
2535 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2536
2537 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2538
2539 FunctionType *DupFuncTy = FunctionType::get(
2540 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2541 /*isVarArg=*/false);
2542
2543 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2544 "omp_taskloop_dup", M);
2545 Value *DestTaskArg = DupFunction->getArg(0);
2546 Value *SrcTaskArg = DupFunction->getArg(1);
2547 Value *LastprivateFlagArg = DupFunction->getArg(2);
2548 DestTaskArg->setName("dest_task");
2549 SrcTaskArg->setName("src_task");
2550 LastprivateFlagArg->setName("lastprivate_flag");
2551
2552 IRBuilderBase::InsertPointGuard Guard(Builder);
2553 Builder.SetInsertPoint(
2554 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2555
2556 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2557 Type *TaskWithPrivatesTy =
2558 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2559 Value *TaskPrivates = Builder.CreateGEP(
2560 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2561 Value *ContextPtr = Builder.CreateGEP(
2562 PrivatesTy, TaskPrivates,
2563 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2564 return ContextPtr;
2565 };
2566
2567 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2568 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2569
2570 DestTaskContextPtr->setName("destPtr");
2571 SrcTaskContextPtr->setName("srcPtr");
2572
2573 InsertPointTy AllocaIP(DupFunction->getEntryBlock().begin());
2574 InsertPointTy CodeGenIP = Builder.saveIP();
2575 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2576 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2577 if (!AfterIPOrError)
2578 return AfterIPOrError.takeError();
2579 Builder.restoreIP(*AfterIPOrError);
2580
2581 Builder.CreateRetVoid();
2582
2583 return DupFunction;
2584}
2585
2586OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2587 const LocationDescription &Loc, InsertPointTy AllocaIP,
2588 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2589 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2590 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2591 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2592 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2593 Value *TaskContextStructPtrVal, bool FreeAgent) {
2594
2595 if (!updateToLocation(Loc))
2596 return InsertPointTy();
2597
2598 uint32_t SrcLocStrSize;
2599 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2600 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2601
2602 BasicBlock *TaskloopExitBB =
2603 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2604 BasicBlock *TaskloopBodyBB =
2605 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2606 BasicBlock *TaskloopAllocaBB =
2607 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2608
2609 InsertPointTy TaskloopAllocaIP = TaskloopAllocaBB->begin();
2610 InsertPointTy TaskloopBodyIP = TaskloopBodyBB->begin();
2611
2612 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2613 return Err;
2614
2615 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2616 if (!result) {
2617 return result.takeError();
2618 }
2619
2620 llvm::CanonicalLoopInfo *CLI = result.get();
2621 if (CLI->VirtualCollapse)
2622 CLI->reportUnsupportedVirtualCollapse();
2623 auto OI = std::make_unique<OutlineInfo>();
2624 OI->EntryBB = TaskloopAllocaBB;
2625 OI->OuterAllocBB = AllocaIP.getNodeParent();
2626 OI->ExitBB = TaskloopExitBB;
2627 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2628 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2629
2630 // Add the thread ID argument.
2631 SmallVector<Instruction *> ToBeDeleted;
2632 // dummy instruction to be used as a fake argument
2633 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2634 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2635 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2636 TaskloopAllocaIP, "lb", false, true);
2637 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2638 TaskloopAllocaIP, "ub", false, true);
2639 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2640 TaskloopAllocaIP, "step", false, true);
2641 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2642 // aggregate struct
2643 OI->Inputs.insert(FakeLB);
2644 OI->Inputs.insert(FakeUB);
2645 OI->Inputs.insert(FakeStep);
2646 if (TaskContextStructPtrVal)
2647 OI->Inputs.insert(TaskContextStructPtrVal);
2648 assert(((TaskContextStructPtrVal && DupCB) ||
2649 (!TaskContextStructPtrVal && !DupCB)) &&
2650 "Task context struct ptr and duplication callback must be both set "
2651 "or both null");
2652
2653 // It isn't safe to run the duplication bodygen callback inside the post
2654 // outlining callback so this has to be run now before we know the real task
2655 // shareds structure type.
2656 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2657 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2658 Type *FakeSharedsTy = StructType::get(
2659 Builder.getContext(),
2660 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2661 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2662 FakeSharedsTy,
2663 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2664 if (!TaskDupFnOrErr) {
2665 return TaskDupFnOrErr.takeError();
2666 }
2667 Value *TaskDupFn = *TaskDupFnOrErr;
2668
2669 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2670 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2671 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2672 FakeSharedsTy, Final, Mergeable, Priority,
2673 NumOfCollapseLoops,
2674 FreeAgent](Function &OutlinedFn) mutable {
2675 // Replace the Stale CI by appropriate RTL function call.
2676 assert(OutlinedFn.hasOneUse() &&
2677 "there must be a single user for the outlined function");
2678 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2679
2680 /* Create the casting for the Bounds Values that can be used when outlining
2681 * to replace the uses of the fakes with real values */
2682 BasicBlock *CodeReplBB = StaleCI->getParent();
2683 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2684 Value *CastedLBVal =
2685 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2686 Value *CastedUBVal =
2687 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2688 Value *CastedStepVal =
2689 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2690
2691 Builder.SetInsertPoint(StaleCI);
2692
2693 // Gather the arguments for emitting the runtime call for
2694 // @__kmpc_omp_task_alloc
2695 Function *TaskAllocFn =
2696 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2697
2698 Value *ThreadID = getOrCreateThreadID(Ident);
2699
2700 if (!NoGroup) {
2701 // Emit runtime call for @__kmpc_taskgroup
2702 Function *TaskgroupFn =
2703 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2704 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2705 }
2706
2707 // `flags` Argument Configuration
2708 // Task is tied if (Flags & 1) == 1.
2709 // Task is untied if (Flags & 1) == 0.
2710 // Task is final if (Flags & 2) == 2.
2711 // Task is not final if (Flags & 2) == 0.
2712 // Task is mergeable if (Flags & 4) == 4.
2713 // Task is not mergeable if (Flags & 4) == 0.
2714 // Task is priority if (Flags & 32) == 32.
2715 // Task is not priority if (Flags & 32) == 0.
2716 // Task is free-agent eligible if (Flags & 128) == 128.
2717 // Task is not free-agent eligible if (Flags & 128) == 0.
2718 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2719 if (Final)
2720 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2721 if (Mergeable)
2722 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2723 if (Priority)
2724 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2725 if (FreeAgent)
2726 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
2727
2728 Value *TaskSize = Builder.getInt64(
2729 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2730
2731 AllocaInst *ArgStructAlloca =
2733 assert(ArgStructAlloca &&
2734 "Unable to find the alloca instruction corresponding to arguments "
2735 "for extracted function");
2736 std::optional<TypeSize> ArgAllocSize =
2737 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2738 assert(ArgAllocSize &&
2739 "Unable to determine size of arguments for extracted function");
2740 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2741
2742 // Emit the @__kmpc_omp_task_alloc runtime call
2743 // The runtime call returns a pointer to an area where the task captured
2744 // variables must be copied before the task is run (TaskData)
2745 CallInst *TaskData = Builder.CreateCall(
2746 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2747 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2748 /*task_func=*/&OutlinedFn});
2749
2750 Value *Shareds = StaleCI->getArgOperand(1);
2751 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2752 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2753 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2754 SharedsSize);
2755 // Get the pointer to loop lb, ub, step from task ptr
2756 // and set up the lowerbound,upperbound and step values
2757 llvm::Value *Lb = Builder.CreateGEP(
2758 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2759
2760 llvm::Value *Ub = Builder.CreateGEP(
2761 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2762
2763 llvm::Value *Step = Builder.CreateGEP(
2764 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2765 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2766
2767 // set up the arguments for emitting kmpc_taskloop runtime call
2768 // setting values for ifval, nogroup, sched, grainsize, task_dup
2769 Value *IfCondVal =
2770 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2771 : Builder.getInt32(1);
2772 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2773 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2774 Value *NoGroupVal = Builder.getInt32(1);
2775 Value *SchedVal = Builder.getInt32(Sched);
2776 Value *GrainSizeVal =
2777 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2778 : Builder.getInt64(0);
2779 Value *TaskDup = TaskDupFn;
2780
2781 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2782 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2783
2784 // taskloop runtime call
2785 Function *TaskloopFn =
2786 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2787 Builder.CreateCall(TaskloopFn, Args);
2788
2789 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2790 // nogroup is not defined
2791 if (!NoGroup) {
2792 Function *EndTaskgroupFn =
2793 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2794 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2795 }
2796
2797 StaleCI->eraseFromParent();
2798
2799 Builder.SetInsertPoint(TaskloopAllocaBB->begin());
2800
2801 LoadInst *SharedsOutlined =
2802 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2803 OutlinedFn.getArg(1)->replaceUsesWithIf(
2804 SharedsOutlined,
2805 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2806
2807 Value *IV = CLI->getIndVar();
2808 Type *IVTy = IV->getType();
2809 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2810
2811 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2812 // UpperBound. These GEP's can be reused for loading the tasks respective
2813 // bounds.
2814 Value *TaskLB = nullptr;
2815 Value *TaskUB = nullptr;
2816 Value *TaskStep = nullptr;
2817 Value *LoadTaskLB = nullptr;
2818 Value *LoadTaskUB = nullptr;
2819 Value *LoadTaskStep = nullptr;
2820 for (Instruction &I : *TaskloopAllocaBB) {
2821 if (I.getOpcode() == Instruction::GetElementPtr) {
2822 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2823 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2824 switch (CI->getZExtValue()) {
2825 case 0:
2826 TaskLB = &I;
2827 break;
2828 case 1:
2829 TaskUB = &I;
2830 break;
2831 case 2:
2832 TaskStep = &I;
2833 break;
2834 }
2835 }
2836 } else if (I.getOpcode() == Instruction::Load) {
2837 LoadInst &Load = cast<LoadInst>(I);
2838 if (Load.getPointerOperand() == TaskLB) {
2839 assert(TaskLB != nullptr && "Expected value for TaskLB");
2840 LoadTaskLB = &I;
2841 } else if (Load.getPointerOperand() == TaskUB) {
2842 assert(TaskUB != nullptr && "Expected value for TaskUB");
2843 LoadTaskUB = &I;
2844 } else if (Load.getPointerOperand() == TaskStep) {
2845 assert(TaskStep != nullptr && "Expected value for TaskStep");
2846 LoadTaskStep = &I;
2847 }
2848 }
2849 }
2850
2851 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2852
2853 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2854 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2855 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2856 Value *TripCountMinusOne = Builder.CreateSDiv(
2857 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2858 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2859 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2860 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2861 // set the trip count in the CLI
2862 CLI->setTripCount(CastedTripCount);
2863
2864 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
2865
2866 if (NumOfCollapseLoops > 1) {
2867 llvm::SmallVector<User *> UsersToReplace;
2868 // When using the collapse clause, the bounds of the loop have to be
2869 // adjusted to properly represent the iterator of the outer loop.
2870 Value *IVPlusTaskLB = Builder.CreateAdd(
2871 CLI->getIndVar(),
2872 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2873 // To ensure every Use is correctly captured, we first want to record
2874 // which users to replace the value in, and then replace the value.
2875 for (auto IVUse = CLI->getIndVar()->uses().begin();
2876 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2877 User *IVUser = IVUse->getUser();
2878 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2879 if (Op->getOpcode() == Instruction::URem ||
2880 Op->getOpcode() == Instruction::UDiv) {
2881 UsersToReplace.push_back(IVUser);
2882 }
2883 }
2884 }
2885 for (User *User : UsersToReplace) {
2886 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2887 }
2888 } else {
2889 // The canonical loop is generated with a fixed lower bound. We need to
2890 // update the index calculation code to use the task's lower bound. The
2891 // generated code looks like this:
2892 // %omp_loop.iv = phi ...
2893 // ...
2894 // %tmp = mul [type] %omp_loop.iv, step
2895 // %user_index = add [type] tmp, lb
2896 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2897 // of the normalised induction variable:
2898 // 1. This one: converting the normalised IV to the user IV
2899 // 2. The increment (add)
2900 // 3. The comparison against the trip count (icmp)
2901 // (1) is the only use that is a mul followed by an add so this cannot
2902 // match other IR.
2903 assert(CLI->getIndVar()->getNumUses() == 3 &&
2904 "Canonical loop should have exactly three uses of the ind var");
2905 for (User *IVUser : CLI->getIndVar()->users()) {
2906 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2907 if (Mul->getOpcode() == Instruction::Mul) {
2908 for (User *MulUser : Mul->users()) {
2909 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2910 if (Add->getOpcode() == Instruction::Add) {
2911 Add->setOperand(1, CastedTaskLB);
2912 }
2913 }
2914 }
2915 }
2916 }
2917 }
2918 }
2919
2920 FakeLB->replaceAllUsesWith(CastedLBVal);
2921 FakeUB->replaceAllUsesWith(CastedUBVal);
2922 FakeStep->replaceAllUsesWith(CastedStepVal);
2923 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2924 I->eraseFromParent();
2925 }
2926 };
2927
2928 addOutlineInfo(std::move(OI));
2929 Builder.SetInsertPoint(TaskloopExitBB->begin());
2930 return Builder.saveIP();
2931}
2932
2935 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2937 llvm::Type::getInt32Ty(M.getContext()));
2938}
2939
2941 const LocationDescription &Loc, InsertPointTy AllocaIP,
2942 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2943 bool Tied, Value *Final, Value *IfCondition,
2944 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2945 bool Mergeable, Value *EventHandle, Value *Priority, bool FreeAgent) {
2946
2947 if (!updateToLocation(Loc))
2948 return InsertPointTy();
2949
2950 uint32_t SrcLocStrSize;
2951 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2952 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2953 // The current basic block is split into four basic blocks. After outlining,
2954 // they will be mapped as follows:
2955 // ```
2956 // def current_fn() {
2957 // current_basic_block:
2958 // br label %task.exit
2959 // task.exit:
2960 // ; instructions after task
2961 // }
2962 // def outlined_fn() {
2963 // task.alloca:
2964 // br label %task.body
2965 // task.body:
2966 // ret void
2967 // }
2968 // ```
2969 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2970 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2971 BasicBlock *TaskAllocaBB =
2972 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2973
2974 InsertPointTy TaskAllocaIP = TaskAllocaBB->begin();
2975 InsertPointTy TaskBodyIP = TaskBodyBB->begin();
2976 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2977 return Err;
2978
2979 auto OI = std::make_unique<OutlineInfo>();
2980 OI->EntryBB = TaskAllocaBB;
2981 OI->OuterAllocBB = AllocaIP.getNodeParent();
2982 OI->ExitBB = TaskExitBB;
2983 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2984 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2985
2986 // Add the thread ID argument.
2988 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2989 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2990
2991 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2992 Affinities, Mergeable, Priority, EventHandle, FreeAgent,
2993 TaskAllocaBB,
2994 ToBeDeleted](Function &OutlinedFn) mutable {
2995 // Replace the Stale CI by appropriate RTL function call.
2996 assert(OutlinedFn.hasOneUse() &&
2997 "there must be a single user for the outlined function");
2998 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2999
3000 // HasShareds is true if any variables are captured in the outlined region,
3001 // false otherwise.
3002 bool HasShareds = StaleCI->arg_size() > 1;
3003 Builder.SetInsertPoint(StaleCI);
3004
3005 // Gather the arguments for emitting the runtime call for
3006 // @__kmpc_omp_task_alloc
3007 Function *TaskAllocFn =
3008 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
3009
3010 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
3011 // call.
3012 Value *ThreadID = getOrCreateThreadID(Ident);
3013
3014 // Argument - `flags`
3015 // Task is tied iff (Flags & 1) == 1.
3016 // Task is untied iff (Flags & 1) == 0.
3017 // Task is final iff (Flags & 2) == 2.
3018 // Task is not final iff (Flags & 2) == 0.
3019 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
3020 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
3021 // Task is detachable iff (Flags & 64) == 64.
3022 // Task is not detachable iff (Flags & 64) == 0.
3023 // Task is priority iff (Flags & 32) == 32.
3024 // Task is not priority iff (Flags & 32) == 0.
3025 // Task is free-agent eligible iff (Flags & 128) == 128.
3026 // Task is not free-agent eligible iff (Flags & 128) == 0.
3027 // TODO: Handle the other flags.
3028 Value *Flags = Builder.getInt32(Tied);
3029 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
3030 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
3031 if (Final) {
3032 Value *FinalFlag =
3033 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
3034 Flags = Builder.CreateOr(FinalFlag, Flags);
3035 }
3036
3037 if (Mergeable || UseMergedIf0Path)
3038 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
3039 if (EventHandle)
3040 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
3041 if (Priority)
3042 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
3043 if (FreeAgent)
3044 Flags = Builder.CreateOr(Builder.getInt32(128), Flags);
3045
3046 // Argument - `sizeof_kmp_task_t` (TaskSize)
3047 // Tasksize refers to the size in bytes of kmp_task_t data structure
3048 // including private vars accessed in task.
3049 // TODO: add kmp_task_t_with_privates (privates)
3050 Value *TaskSize = Builder.getInt64(
3051 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3052
3053 // Argument - `sizeof_shareds` (SharedsSize)
3054 // SharedsSize refers to the shareds array size in the kmp_task_t data
3055 // structure.
3056 Value *SharedsSize = Builder.getInt64(0);
3057 if (HasShareds) {
3058 AllocaInst *ArgStructAlloca =
3060 assert(ArgStructAlloca &&
3061 "Unable to find the alloca instruction corresponding to arguments "
3062 "for extracted function");
3063 std::optional<TypeSize> ArgAllocSize =
3064 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3065 assert(ArgAllocSize &&
3066 "Unable to determine size of arguments for extracted function");
3067 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3068 }
3069 // Emit the @__kmpc_omp_task_alloc runtime call
3070 // The runtime call returns a pointer to an area where the task captured
3071 // variables must be copied before the task is run (TaskData)
3073 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3074 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3075 /*task_func=*/&OutlinedFn});
3076
3077 if (Affinities.Count && Affinities.Info) {
3079 OMPRTL___kmpc_omp_reg_task_with_affinity);
3080
3081 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3082 Affinities.Count, Affinities.Info});
3083 }
3084
3085 // Emit detach clause initialization.
3086 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3087 // task_descriptor);
3088 if (EventHandle) {
3090 OMPRTL___kmpc_task_allow_completion_event);
3091 llvm::Value *EventVal =
3092 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3093 llvm::Value *EventHandleAddr =
3094 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3095 Builder.getPtrTy(0));
3096 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3097 Builder.CreateStore(EventVal, EventHandleAddr);
3098 }
3099 // Copy the arguments for outlined function
3100 if (HasShareds) {
3101 Value *Shareds = StaleCI->getArgOperand(1);
3102 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3103 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3104 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3105 SharedsSize);
3106 }
3107
3108 if (Priority) {
3109 //
3110 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3111 // we populate the priority information into the "kmp_task_t" here
3112 //
3113 // The struct "kmp_task_t" definition is available in kmp.h
3114 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3115 // data2 is used for priority
3116 //
3117 Type *Int32Ty = Builder.getInt32Ty();
3118 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3119 // kmp_task_t* => { ptr }
3120 Type *TaskPtr = StructType::get(VoidPtr);
3121 Value *TaskGEP =
3122 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3123 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3124 Type *TaskStructType = StructType::get(
3125 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3126 Value *PriorityData = Builder.CreateInBoundsGEP(
3127 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3128 // kmp_cmplrdata_t => { ptr, ptr }
3129 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3130 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3131 PriorityData, {Zero, Zero});
3132 Builder.CreateStore(Priority, CmplrData);
3133 }
3134
3135 Value *DepArray = nullptr;
3136 Value *NumDeps = nullptr;
3137 if (Dependencies.DepArray) {
3138 DepArray = Dependencies.DepArray;
3139 NumDeps = Dependencies.NumDeps;
3140 } else if (!Dependencies.Deps.empty()) {
3141 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3142 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3143 }
3144
3145 // In the presence of the `if` clause, the following IR is generated:
3146 // ...
3147 // %data = call @__kmpc_omp_task_alloc(...)
3148 // br i1 %if_condition, label %then, label %else
3149 // then:
3150 // call @__kmpc_omp_task(...)
3151 // br label %exit
3152 // else:
3153 // ;; Wait for resolution of dependencies, if any, before
3154 // ;; beginning the task
3155 // call @__kmpc_omp_wait_deps(...)
3156 // call @__kmpc_omp_task_begin_if0(...)
3157 // call @outlined_fn(...)
3158 // call @__kmpc_omp_task_complete_if0(...)
3159 // br label %exit
3160 // exit:
3161 // ...
3162 if (IfCondition && !UseMergedIf0Path) {
3163 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3164 // terminator.
3165 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3166 Instruction *IfTerminator =
3167 Builder.GetInsertPoint()->getParent()->getTerminator();
3168 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3169 Builder.SetInsertPoint(IfTerminator);
3170 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3171 &ElseTI);
3172 Builder.SetInsertPoint(ElseTI);
3173
3174 if (DepArray) {
3175 Function *TaskWaitFn =
3176 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3178 TaskWaitFn,
3179 {Ident, ThreadID, NumDeps, DepArray,
3180 ConstantInt::get(Builder.getInt32Ty(), 0),
3182 }
3183 Function *TaskBeginFn =
3184 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3185 Function *TaskCompleteFn =
3186 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3187 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3188 CallInst *CI = nullptr;
3189 if (HasShareds)
3190 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3191 else
3192 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3193 CI->setDebugLoc(StaleCI->getDebugLoc());
3194 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3195 Builder.SetInsertPoint(ThenTI);
3196 }
3197
3198 if (DepArray) {
3199 Function *TaskFn =
3200 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3202 TaskFn,
3203 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3204 ConstantInt::get(Builder.getInt32Ty(), 0),
3206
3207 } else {
3208 // Emit the @__kmpc_omp_task runtime call to spawn the task
3209 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3210 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3211 }
3212
3213 StaleCI->eraseFromParent();
3214
3215 Builder.SetInsertPoint(TaskAllocaBB->begin());
3216 if (HasShareds) {
3217 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3218 OutlinedFn.getArg(1)->replaceUsesWithIf(
3219 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3220 }
3221
3222 // The insert point may refer to one of the instructions about to be
3223 // deleted. It is not needed anymore so clear it instead of leaving it
3224 // dangling.
3225 Builder.ClearInsertionPoint();
3226 for (Instruction *I : llvm::reverse(ToBeDeleted))
3227 I->eraseFromParent();
3228 };
3229
3230 addOutlineInfo(std::move(OI));
3231 Builder.SetInsertPoint(TaskExitBB->begin());
3232
3233 return Builder.saveIP();
3234}
3235
3237 const LocationDescription &Loc, InsertPointTy AllocaIP,
3238 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3239 if (!updateToLocation(Loc))
3240 return InsertPointTy();
3241
3242 uint32_t SrcLocStrSize;
3243 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3244 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3245 Value *ThreadID = getOrCreateThreadID(Ident);
3246
3247 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3248 Function *TaskgroupFn =
3249 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3250 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3251
3252 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3253 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3254 return Err;
3255
3256 Builder.SetInsertPoint(TaskgroupExitBB);
3257 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3258 Function *EndTaskgroupFn =
3259 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3260 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3261
3262 return Builder.saveIP();
3263}
3264
3266 const LocationDescription &Loc, InsertPointTy AllocaIP,
3268 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3269 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3270
3271 if (!updateToLocation(Loc))
3272 return Loc.IP;
3273
3274 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3275
3276 // Each section is emitted as a switch case
3277 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3278 // -> OMP.createSection() which generates the IR for each section
3279 // Iterate through all sections and emit a switch construct:
3280 // switch (IV) {
3281 // case 0:
3282 // <SectionStmt[0]>;
3283 // break;
3284 // ...
3285 // case <NumSection> - 1:
3286 // <SectionStmt[<NumSection> - 1]>;
3287 // break;
3288 // }
3289 // ...
3290 // section_loop.after:
3291 // <FiniCB>;
3292 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3293 Builder.restoreIP(CodeGenIP);
3295 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3296 Function *CurFn = Continue->getParent();
3297 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3298
3299 unsigned CaseNumber = 0;
3300 for (auto SectionCB : SectionCBs) {
3302 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3303 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3304 Builder.SetInsertPoint(CaseBB);
3305 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3306 if (Error Err = SectionCB(InsertPointTy(), CaseEndBr->getIterator(), {}))
3307 return Err;
3308 CaseNumber++;
3309 }
3310 // remove the existing terminator from body BB since there can be no
3311 // terminators after switch/case
3312 return Error::success();
3313 };
3314 // Loop body ends here
3315 // LowerBound, UpperBound, and STride for createCanonicalLoop
3316 Type *I32Ty = Type::getInt32Ty(M.getContext());
3317 Value *LB = ConstantInt::get(I32Ty, 0);
3318 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3319 Value *ST = ConstantInt::get(I32Ty, 1);
3321 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3322 if (!LoopInfo)
3323 return LoopInfo.takeError();
3324
3325 InsertPointOrErrorTy WsloopIP =
3326 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3327 WorksharingLoopType::ForStaticLoop, !IsNowait);
3328 if (!WsloopIP)
3329 return WsloopIP.takeError();
3330 InsertPointTy AfterIP = *WsloopIP;
3331
3332 BasicBlock *LoopFini = AfterIP.getNodeParent()->getSinglePredecessor();
3333 assert(LoopFini && "Bad structure of static workshare loop finalization");
3334
3335 // Apply the finalization callback in LoopAfterBB
3336 auto FiniInfo = FinalizationStack.pop_back_val();
3337 assert(FiniInfo.DK == OMPD_sections &&
3338 "Unexpected finalization stack state!");
3339 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3340 return Err;
3341
3342 return AfterIP;
3343}
3344
3347 BodyGenCallbackTy BodyGenCB,
3348 FinalizeCallbackTy FiniCB) {
3349 if (!updateToLocation(Loc))
3350 return Loc.IP;
3351
3352 auto FiniCBWrapper = [&](InsertPointTy IP) {
3353 if (IP != IP.getNodeParent()->end())
3354 return FiniCB(IP);
3355 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3356 // will fail because that function requires the Finalization Basic Block to
3357 // have a terminator, which is already removed by EmitOMPRegionBody.
3358 // IP is currently at cancelation block.
3359 // We need to backtrack to the condition block to fetch
3360 // the exit block and create a branch from cancelation
3361 // to exit block.
3363 Builder.restoreIP(IP);
3364 auto *CaseBB = Loc.IP.getNodeParent();
3365 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3366 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3367 Instruction *I = Builder.CreateBr(ExitBB);
3368 IP = I->getIterator();
3369 return FiniCB(IP);
3370 };
3371
3372 Directive OMPD = Directive::OMPD_sections;
3373 // Since we are using Finalization Callback here, HasFinalize
3374 // and IsCancellable have to be true
3375 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3376 /*Conditional*/ false, /*hasFinalize*/ true,
3377 /*IsCancellable*/ true);
3378}
3379
3385
3386Value *OpenMPIRBuilder::getGPUThreadID() {
3389 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3390 {});
3391}
3392
3393Value *OpenMPIRBuilder::getGPUWarpSize() {
3395 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3396}
3397
3398Value *OpenMPIRBuilder::getNVPTXWarpID() {
3399 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3400 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3401}
3402
3403Value *OpenMPIRBuilder::getNVPTXLaneID() {
3404 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3405 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3406 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3407 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3408 "nvptx_lane_id");
3409}
3410
3411Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3412 Type *ToType) {
3413 Type *FromType = From->getType();
3414 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3415 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3416 assert(FromSize > 0 && "From size must be greater than zero");
3417 assert(ToSize > 0 && "To size must be greater than zero");
3418 if (FromType == ToType)
3419 return From;
3420 if (FromSize == ToSize)
3421 return Builder.CreateBitCast(From, ToType);
3422 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3423 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3424 InsertPointTy SaveIP = Builder.saveIP();
3425 Builder.restoreIP(AllocaIP);
3426 Value *CastItem = Builder.CreateAlloca(ToType);
3427 Builder.restoreIP(SaveIP);
3428
3429 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3430 CastItem, Builder.getPtrTy(0));
3431 Builder.CreateStore(From, ValCastItem);
3432 return Builder.CreateLoad(ToType, CastItem);
3433}
3434
3435Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3436 Value *Element,
3437 Type *ElementType,
3438 Value *Offset) {
3439 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3440 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3441
3442 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3443 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3444 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3445 Value *WarpSize =
3446 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3448 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3449 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3450 Value *WarpSizeCast =
3451 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3452 Value *ShuffleCall =
3453 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3454 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3455 // down to the requested element type, otherwise storing the result would
3456 // write past the end of an element narrower than the shuffle width.
3457 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3458}
3459
3460void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3461 Value *DstAddr, Type *ElemType,
3462 Value *Offset, Type *ReductionArrayTy,
3463 bool IsByRefElem) {
3464 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3465 // Create the loop over the big sized data.
3466 // ptr = (void*)Elem;
3467 // ptrEnd = (void*) Elem + 1;
3468 // Step = 8;
3469 // while (ptr + Step < ptrEnd)
3470 // shuffle((int64_t)*ptr);
3471 // Step = 4;
3472 // while (ptr + Step < ptrEnd)
3473 // shuffle((int32_t)*ptr);
3474 // ...
3475 Type *IndexTy = Builder.getIndexTy(
3476 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3477 Value *ElemPtr = DstAddr;
3478 Value *Ptr = SrcAddr;
3479 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3480 if (Size < IntSize)
3481 continue;
3482 Type *IntType = Builder.getIntNTy(IntSize * 8);
3483 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3484 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3485 Value *SrcAddrGEP =
3486 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3487 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3488 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3489
3490 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3491 if ((Size / IntSize) > 1) {
3492 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3493 SrcAddrGEP, Builder.getPtrTy());
3494 BasicBlock *PreCondBB =
3495 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3496 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3497 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3498 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3499 emitBlock(PreCondBB, CurFunc);
3500 PHINode *PhiSrc =
3501 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3502 PhiSrc->addIncoming(Ptr, CurrentBB);
3503 PHINode *PhiDest =
3504 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3505 PhiDest->addIncoming(ElemPtr, CurrentBB);
3506 Ptr = PhiSrc;
3507 ElemPtr = PhiDest;
3508 Value *PtrDiff = Builder.CreatePtrDiff(
3509 Builder.getInt8Ty(), PtrEnd,
3510 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3511 Builder.CreateCondBr(
3512 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3513 ExitBB);
3514 emitBlock(ThenBB, CurFunc);
3515 Value *Res = createRuntimeShuffleFunction(
3516 AllocaIP,
3517 Builder.CreateAlignedLoad(
3518 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3519 IntType, Offset);
3520 Builder.CreateAlignedStore(Res, ElemPtr,
3521 M.getDataLayout().getPrefTypeAlign(ElemType));
3522 Value *LocalPtr =
3523 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3524 Value *LocalElemPtr =
3525 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3526 PhiSrc->addIncoming(LocalPtr, ThenBB);
3527 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3528 emitBranch(PreCondBB);
3529 emitBlock(ExitBB, CurFunc);
3530 } else {
3531 // The shuffled value comes back as the chunk's integer type, so the
3532 // store covers exactly this chunk regardless of what ElemType is.
3533 Value *Res = createRuntimeShuffleFunction(
3534 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3535 Builder.CreateStore(Res, ElemPtr);
3536 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3537 ElemPtr =
3538 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3539 }
3540 Size = Size % IntSize;
3541 }
3542}
3543
3544Error OpenMPIRBuilder::emitReductionListCopy(
3545 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3546 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3547 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3548 Type *IndexTy = Builder.getIndexTy(
3549 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3550 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3551
3552 // Iterates, element-by-element, through the source Reduce list and
3553 // make a copy.
3554 for (auto En : enumerate(ReductionInfos)) {
3555 const ReductionInfo &RI = En.value();
3556 Value *SrcElementAddr = nullptr;
3557 AllocaInst *DestAlloca = nullptr;
3558 Value *DestElementAddr = nullptr;
3559 Value *DestElementPtrAddr = nullptr;
3560 // Should we shuffle in an element from a remote lane?
3561 bool ShuffleInElement = false;
3562 // Set to true to update the pointer in the dest Reduce list to a
3563 // newly created element.
3564 bool UpdateDestListPtr = false;
3565
3566 // Step 1.1: Get the address for the src element in the Reduce list.
3567 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3568 ReductionArrayTy, SrcBase,
3569 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3570 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3571
3572 // Step 1.2: Create a temporary to store the element in the destination
3573 // Reduce list.
3574 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3575 ReductionArrayTy, DestBase,
3576 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3577 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3578 switch (Action) {
3580 InsertPointTy CurIP = Builder.saveIP();
3581 Builder.restoreIP(AllocaIP);
3582
3583 Type *DestAllocaType =
3584 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3585 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3586 ".omp.reduction.element");
3587 DestAlloca->setAlignment(
3588 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3589 DestElementAddr = DestAlloca;
3590 DestElementAddr =
3591 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3592 DestElementAddr->getName() + ".ascast");
3593 Builder.restoreIP(CurIP);
3594 ShuffleInElement = true;
3595 UpdateDestListPtr = true;
3596 break;
3597 }
3599 DestElementAddr =
3600 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3601 break;
3602 }
3603 }
3604
3605 // Now that all active lanes have read the element in the
3606 // Reduce list, shuffle over the value from the remote lane.
3607 if (ShuffleInElement) {
3608 Type *ShuffleType = RI.ElementType;
3609 Value *ShuffleSrcAddr = SrcElementAddr;
3610 Value *ShuffleDestAddr = DestElementAddr;
3611 AllocaInst *LocalStorage = nullptr;
3612
3613 if (IsByRefElem) {
3614 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3615 assert(RI.ByRefAllocatedType &&
3616 "Expected by-ref allocated type to be set");
3617 // For by-ref reductions, we need to copy from the remote lane the
3618 // actual value of the partial reduction computed by that remote lane;
3619 // rather than, for example, a pointer to that data or, even worse, a
3620 // pointer to the descriptor of the by-ref reduction element.
3621 ShuffleType = RI.ByRefElementType;
3622
3623 if (RI.DataPtrPtrGen) {
3624 // Descriptor-based by-ref: extract data pointer from descriptor.
3625 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3626 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3627
3628 if (!GenResult)
3629 return GenResult.takeError();
3630
3631 ShuffleSrcAddr =
3632 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3633
3634 {
3635 InsertPointTy OldIP = Builder.saveIP();
3636 Builder.restoreIP(AllocaIP);
3637
3638 LocalStorage = Builder.CreateAlloca(ShuffleType);
3639 Builder.restoreIP(OldIP);
3640 ShuffleDestAddr = LocalStorage;
3641 }
3642 } else {
3643 // Non-descriptor by-ref: the pointer already references data
3644 // directly. Shuffle into the destination alloca.
3645 ShuffleDestAddr = DestElementAddr;
3646 }
3647 }
3648
3649 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3650 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3651
3652 if (IsByRefElem && RI.DataPtrPtrGen) {
3653 // Copy descriptor from source and update base_ptr to shuffled data
3654 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3655 DestAlloca, Builder.getPtrTy(), ".ascast");
3656
3657 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3658 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3659 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3660
3661 if (!GenResult)
3662 return GenResult.takeError();
3663 }
3664 } else {
3665 switch (RI.EvaluationKind) {
3666 case EvalKind::Scalar: {
3667 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3668 // Store the source element value to the dest element address.
3669 Builder.CreateStore(Elem, DestElementAddr);
3670 break;
3671 }
3672 case EvalKind::Complex: {
3673 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3674 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3675 Value *SrcReal = Builder.CreateLoad(
3676 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3677 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3678 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3679 Value *SrcImg = Builder.CreateLoad(
3680 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3681
3682 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3683 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3684 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3685 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3686 Builder.CreateStore(SrcReal, DestRealPtr);
3687 Builder.CreateStore(SrcImg, DestImgPtr);
3688 break;
3689 }
3690 case EvalKind::Aggregate: {
3691 Value *SizeVal = Builder.getInt64(
3692 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3693 Builder.CreateMemCpy(
3694 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3695 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3696 SizeVal, false);
3697 break;
3698 }
3699 };
3700 }
3701
3702 // Step 3.1: Modify reference in dest Reduce list as needed.
3703 // Modifying the reference in Reduce list to point to the newly
3704 // created element. The element is live in the current function
3705 // scope and that of functions it invokes (i.e., reduce_function).
3706 // RemoteReduceData[i] = (void*)&RemoteElem
3707 if (UpdateDestListPtr) {
3708 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3709 DestElementAddr, Builder.getPtrTy(),
3710 DestElementAddr->getName() + ".ascast");
3711 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3712 }
3713 }
3714
3715 return Error::success();
3716}
3717
3718Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3719 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3720 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3721 IRBuilder<>::InsertPointGuard IPG(Builder);
3722 LLVMContext &Ctx = M.getContext();
3723 FunctionType *FuncTy = FunctionType::get(
3724 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3725 /* IsVarArg */ false);
3726 Function *WcFunc =
3728 "_omp_reduction_inter_warp_copy_func", &M);
3729 WcFunc->setCallingConv(Config.getRuntimeCC());
3730 WcFunc->setAttributes(FuncAttrs);
3731 WcFunc->addParamAttr(0, Attribute::NoUndef);
3732 WcFunc->addParamAttr(1, Attribute::NoUndef);
3733 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3734 Builder.SetInsertPoint(EntryBB);
3735 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3736
3737 // ReduceList: thread local Reduce list.
3738 // At the stage of the computation when this function is called, partially
3739 // aggregated values reside in the first lane of every active warp.
3740 Argument *ReduceListArg = WcFunc->getArg(0);
3741 // NumWarps: number of warps active in the parallel region. This could
3742 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3743 Argument *NumWarpsArg = WcFunc->getArg(1);
3744
3745 // This array is used as a medium to transfer, one reduce element at a time,
3746 // the data from the first lane of every warp to lanes in the first warp
3747 // in order to perform the final step of a reduction in a parallel region
3748 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3749 // for reduced latency, as well as to have a distinct copy for concurrently
3750 // executing target regions. The array is declared with common linkage so
3751 // as to be shared across compilation units.
3752 StringRef TransferMediumName =
3753 "__openmp_nvptx_data_transfer_temporary_storage";
3754 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3755 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3756 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3757 if (!TransferMedium) {
3758 TransferMedium = new GlobalVariable(
3759 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3760 UndefValue::get(ArrayTy), TransferMediumName,
3761 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3762 /*AddressSpace=*/3);
3763 }
3764
3765 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3766 Value *GPUThreadID = getGPUThreadID();
3767 // nvptx_lane_id = nvptx_id % warpsize
3768 Value *LaneID = getNVPTXLaneID();
3769 // nvptx_warp_id = nvptx_id / warpsize
3770 Value *WarpID = getNVPTXWarpID();
3771
3772 InsertPointTy AllocaIP = Builder.GetInsertBlock()->getFirstInsertionPt();
3773 Type *Arg0Type = ReduceListArg->getType();
3774 Type *Arg1Type = NumWarpsArg->getType();
3775 Builder.restoreIP(AllocaIP);
3776 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3777 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3778 AllocaInst *NumWarpsAlloca =
3779 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3780 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3781 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3782 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3783 NumWarpsAlloca, Builder.getPtrTy(0),
3784 NumWarpsAlloca->getName() + ".ascast");
3785 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3786 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3787 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3788 InsertPointTy CodeGenIP =
3789 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3790 Builder.restoreIP(CodeGenIP);
3791
3792 Value *ReduceList =
3793 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3794
3795 for (auto En : enumerate(ReductionInfos)) {
3796 //
3797 // Warp master copies reduce element to transfer medium in __shared__
3798 // memory.
3799 //
3800 const ReductionInfo &RI = En.value();
3801 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3802 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3803 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3804 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3805 Type *CType = Builder.getIntNTy(TySize * 8);
3806
3807 unsigned NumIters = RealTySize / TySize;
3808 if (NumIters == 0)
3809 continue;
3810 Value *Cnt = nullptr;
3811 Value *CntAddr = nullptr;
3812 BasicBlock *PrecondBB = nullptr;
3813 BasicBlock *ExitBB = nullptr;
3814 if (NumIters > 1) {
3815 CodeGenIP = Builder.saveIP();
3816 Builder.restoreIP(AllocaIP);
3817 CntAddr =
3818 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3819
3820 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3821 CntAddr->getName() + ".ascast");
3822 Builder.restoreIP(CodeGenIP);
3823 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3824 CntAddr,
3825 /*Volatile=*/false);
3826 PrecondBB = BasicBlock::Create(Ctx, "precond");
3827 ExitBB = BasicBlock::Create(Ctx, "exit");
3828 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3829 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3830 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3831 /*Volatile=*/false);
3832 Value *Cmp = Builder.CreateICmpULT(
3833 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3834 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3835 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3836 }
3837
3838 // kmpc_barrier.
3839 InsertPointOrErrorTy BarrierIP1 =
3841 omp::Directive::OMPD_unknown,
3842 /* ForceSimpleCall */ false,
3843 /* CheckCancelFlag */ true);
3844 if (!BarrierIP1)
3845 return BarrierIP1.takeError();
3846 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3847 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3848 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3849
3850 // if (lane_id == 0)
3851 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3852 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3853 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3854
3855 // Reduce element = LocalReduceList[i]
3856 auto *RedListArrayTy =
3857 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3858 Type *IndexTy = Builder.getIndexTy(
3859 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3860 Value *ElemPtrPtr =
3861 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3862 {ConstantInt::get(IndexTy, 0),
3863 ConstantInt::get(IndexTy, En.index())});
3864 // elemptr = ((CopyType*)(elemptrptr)) + I
3865 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3866
3867 if (IsByRefElem && RI.DataPtrPtrGen) {
3868 InsertPointOrErrorTy GenRes =
3869 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3870
3871 if (!GenRes)
3872 return GenRes.takeError();
3873
3874 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3875 }
3876
3877 if (NumIters > 1)
3878 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3879
3880 // Get pointer to location in transfer medium.
3881 // MediumPtr = &medium[warp_id]
3882 Value *MediumPtr = Builder.CreateInBoundsGEP(
3883 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3884 // elem = *elemptr
3885 //*MediumPtr = elem
3886 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3887 // Store the source element value to the dest element address.
3888 Builder.CreateStore(Elem, MediumPtr,
3889 /*IsVolatile*/ true);
3890 Builder.CreateBr(MergeBB);
3891
3892 // else
3893 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3894 Builder.CreateBr(MergeBB);
3895
3896 // endif
3897 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3898 InsertPointOrErrorTy BarrierIP2 =
3900 omp::Directive::OMPD_unknown,
3901 /* ForceSimpleCall */ false,
3902 /* CheckCancelFlag */ true);
3903 if (!BarrierIP2)
3904 return BarrierIP2.takeError();
3905
3906 // Warp 0 copies reduce element from transfer medium
3907 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3908 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3909 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3910
3911 Value *NumWarpsVal =
3912 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3913 // Up to 32 threads in warp 0 are active.
3914 Value *IsActiveThread =
3915 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3916 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3917
3918 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3919
3920 // SecMediumPtr = &medium[tid]
3921 // SrcMediumVal = *SrcMediumPtr
3922 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3923 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3924 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3925 Value *TargetElemPtrPtr =
3926 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3927 {ConstantInt::get(IndexTy, 0),
3928 ConstantInt::get(IndexTy, En.index())});
3929 Value *TargetElemPtrVal =
3930 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3931 Value *TargetElemPtr = TargetElemPtrVal;
3932
3933 if (IsByRefElem && RI.DataPtrPtrGen) {
3934 InsertPointOrErrorTy GenRes =
3935 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3936
3937 if (!GenRes)
3938 return GenRes.takeError();
3939
3940 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3941 }
3942
3943 if (NumIters > 1)
3944 TargetElemPtr =
3945 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3946
3947 // *TargetElemPtr = SrcMediumVal;
3948 Value *SrcMediumValue =
3949 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3950 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3951 Builder.CreateBr(W0MergeBB);
3952
3953 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3954 Builder.CreateBr(W0MergeBB);
3955
3956 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3957
3958 if (NumIters > 1) {
3959 Cnt = Builder.CreateNSWAdd(
3960 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3961 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3962
3963 auto *CurFn = Builder.GetInsertBlock()->getParent();
3964 emitBranch(PrecondBB);
3965 emitBlock(ExitBB, CurFn);
3966 }
3967 RealTySize %= TySize;
3968 }
3969 }
3970
3971 Builder.CreateRetVoid();
3972
3973 return WcFunc;
3974}
3975
3976Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3977 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3978 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3979 LLVMContext &Ctx = M.getContext();
3980 IRBuilder<>::InsertPointGuard IPG(Builder);
3981 FunctionType *FuncTy =
3982 FunctionType::get(Builder.getVoidTy(),
3983 {Builder.getPtrTy(), Builder.getInt16Ty(),
3984 Builder.getInt16Ty(), Builder.getInt16Ty()},
3985 /* IsVarArg */ false);
3986 Function *SarFunc =
3988 "_omp_reduction_shuffle_and_reduce_func", &M);
3989 SarFunc->setCallingConv(Config.getRuntimeCC());
3990 SarFunc->setAttributes(FuncAttrs);
3991 SarFunc->addParamAttr(0, Attribute::NoUndef);
3992 SarFunc->addParamAttr(1, Attribute::NoUndef);
3993 SarFunc->addParamAttr(2, Attribute::NoUndef);
3994 SarFunc->addParamAttr(3, Attribute::NoUndef);
3995 SarFunc->addParamAttr(1, Attribute::SExt);
3996 SarFunc->addParamAttr(2, Attribute::SExt);
3997 SarFunc->addParamAttr(3, Attribute::SExt);
3998 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3999 Builder.SetInsertPoint(EntryBB);
4000 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4001
4002 // Thread local Reduce list used to host the values of data to be reduced.
4003 Argument *ReduceListArg = SarFunc->getArg(0);
4004 // Current lane id; could be logical.
4005 Argument *LaneIDArg = SarFunc->getArg(1);
4006 // Offset of the remote source lane relative to the current lane.
4007 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
4008 // Algorithm version. This is expected to be known at compile time.
4009 Argument *AlgoVerArg = SarFunc->getArg(3);
4010
4011 Type *ReduceListArgType = ReduceListArg->getType();
4012 Type *LaneIDArgType = LaneIDArg->getType();
4013 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
4014 Value *ReduceListAlloca = Builder.CreateAlloca(
4015 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
4016 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4017 LaneIDArg->getName() + ".addr");
4018 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
4019 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
4020 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
4021 AlgoVerArg->getName() + ".addr");
4022 ArrayType *RedListArrayTy =
4023 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4024
4025 // Create a local thread-private variable to host the Reduce list
4026 // from a remote lane.
4027 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
4028 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
4029
4030 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4031 ReduceListAlloca, ReduceListArgType,
4032 ReduceListAlloca->getName() + ".ascast");
4033 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4034 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
4035 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4036 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
4037 RemoteLaneOffsetAlloca->getName() + ".ascast");
4038 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4039 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
4040 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4041 RemoteReductionListAlloca, Builder.getPtrTy(),
4042 RemoteReductionListAlloca->getName() + ".ascast");
4043
4044 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4045 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4046 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4047 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4048
4049 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4050 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4051 Value *RemoteLaneOffset =
4052 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4053 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4054
4055 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4056
4057 // This loop iterates through the list of reduce elements and copies,
4058 // element by element, from a remote lane in the warp to RemoteReduceList,
4059 // hosted on the thread's stack.
4060 Error EmitRedLsCpRes = emitReductionListCopy(
4061 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4062 ReduceList, RemoteListAddrCast, IsByRef,
4063 {RemoteLaneOffset, nullptr, nullptr});
4064
4065 if (EmitRedLsCpRes)
4066 return EmitRedLsCpRes;
4067
4068 // The actions to be performed on the Remote Reduce list is dependent
4069 // on the algorithm version.
4070 //
4071 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4072 // LaneId % 2 == 0 && Offset > 0):
4073 // do the reduction value aggregation
4074 //
4075 // The thread local variable Reduce list is mutated in place to host the
4076 // reduced data, which is the aggregated value produced from local and
4077 // remote lanes.
4078 //
4079 // Note that AlgoVer is expected to be a constant integer known at compile
4080 // time.
4081 // When AlgoVer==0, the first conjunction evaluates to true, making
4082 // the entire predicate true during compile time.
4083 // When AlgoVer==1, the second conjunction has only the second part to be
4084 // evaluated during runtime. Other conjunctions evaluates to false
4085 // during compile time.
4086 // When AlgoVer==2, the third conjunction has only the second part to be
4087 // evaluated during runtime. Other conjunctions evaluates to false
4088 // during compile time.
4089 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4090 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4091 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4092 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4093 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4094 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4095 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4096 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4097 Value *RemoteOffsetComp =
4098 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4099 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4100 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4101 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4102
4103 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4104 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4105 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4106
4107 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4108 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4109 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4110 ReduceList, Builder.getPtrTy());
4111 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4112 RemoteListAddrCast, Builder.getPtrTy());
4113 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4114 ->addFnAttr(Attribute::NoUnwind);
4115 Builder.CreateBr(MergeBB);
4116
4117 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4118 Builder.CreateBr(MergeBB);
4119
4120 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4121
4122 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4123 // Reduce list.
4124 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4125 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4126 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4127
4128 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4129 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4130 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4131 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4132
4133 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4134
4135 EmitRedLsCpRes = emitReductionListCopy(
4136 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4137 RemoteListAddrCast, ReduceList, IsByRef);
4138
4139 if (EmitRedLsCpRes)
4140 return EmitRedLsCpRes;
4141
4142 Builder.CreateBr(CpyMergeBB);
4143
4144 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4145 Builder.CreateBr(CpyMergeBB);
4146
4147 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4148
4149 Builder.CreateRetVoid();
4150
4151 return SarFunc;
4152}
4153
4155OpenMPIRBuilder::generateReductionDescriptor(
4156 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4157 Type *DescriptorType,
4158 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4159 DataPtrPtrGen) {
4160
4161 // Copy the source descriptor to preserve all metadata (rank, extents,
4162 // strides, etc.)
4163 Value *DescriptorSize =
4164 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4165 Builder.CreateMemCpy(
4166 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4167 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4168 DescriptorSize);
4169
4170 // Update the base pointer field to point to the local shuffled data
4171 Value *DataPtrField;
4172 InsertPointOrErrorTy GenResult =
4173 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4174
4175 if (!GenResult)
4176 return GenResult.takeError();
4177
4178 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4179 DataPtr, Builder.getPtrTy(), ".ascast"),
4180 DataPtrField);
4181
4182 return Builder.saveIP();
4183}
4184
4185Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4186 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4187 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4188 InsertPointTy OldIP = Builder.saveIP();
4189 Builder.restoreIP(AllocaIP);
4190
4191 AllocaInst *DescriptorAlloca =
4192 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4193 DescriptorAlloca->setAlignment(
4194 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4195 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4196 DescriptorAlloca, DescriptorPtrTy,
4197 DescriptorAlloca->getName() + ".ascast");
4198
4199 Builder.restoreIP(OldIP);
4200
4201 InsertPointOrErrorTy GenResult =
4202 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4203 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4204 if (!GenResult)
4205 return GenResult.takeError();
4206
4207 return DescriptorAddr;
4208}
4209
4210Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4211 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4212 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4213 IRBuilder<>::InsertPointGuard IPG(Builder);
4214 LLVMContext &Ctx = M.getContext();
4215 FunctionType *FuncTy = FunctionType::get(
4216 Builder.getVoidTy(),
4217 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4218 /* IsVarArg */ false);
4219 Function *LtGCFunc =
4221 "_omp_reduction_list_to_global_copy_func", &M);
4222 LtGCFunc->setAttributes(FuncAttrs);
4223 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4224 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4225 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4226
4227 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4228 Builder.SetInsertPoint(EntryBlock);
4229 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4230
4231 // Buffer: global reduction buffer.
4232 Argument *BufferArg = LtGCFunc->getArg(0);
4233 // Idx: index of the buffer.
4234 Argument *IdxArg = LtGCFunc->getArg(1);
4235 // ReduceList: thread local Reduce list.
4236 Argument *ReduceListArg = LtGCFunc->getArg(2);
4237
4238 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4239 BufferArg->getName() + ".addr");
4240 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4241 IdxArg->getName() + ".addr");
4242 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4243 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4244 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4245 BufferArgAlloca, Builder.getPtrTy(),
4246 BufferArgAlloca->getName() + ".ascast");
4247 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4248 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4249 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4250 ReduceListArgAlloca, Builder.getPtrTy(),
4251 ReduceListArgAlloca->getName() + ".ascast");
4252
4253 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4254 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4255 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4256
4257 Value *LocalReduceList =
4258 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4259 Value *BufferArgVal =
4260 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4261 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4262 Type *IndexTy = Builder.getIndexTy(
4263 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4264 for (auto En : enumerate(ReductionInfos)) {
4265 const ReductionInfo &RI = En.value();
4266 auto *RedListArrayTy =
4267 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4268 // Reduce element = LocalReduceList[i]
4269 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4270 RedListArrayTy, LocalReduceList,
4271 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4272 // elemptr = ((CopyType*)(elemptrptr)) + I
4273 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4274
4275 // Global = Buffer.VD[Idx];
4276 Value *BufferVD =
4277 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4278 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4279 ReductionsBufferTy, BufferVD, 0, En.index());
4280
4281 switch (RI.EvaluationKind) {
4282 case EvalKind::Scalar: {
4283 Value *TargetElement;
4284
4285 if (IsByRef.empty() || !IsByRef[En.index()]) {
4286 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4287 } else {
4288 if (RI.DataPtrPtrGen) {
4289 InsertPointOrErrorTy GenResult =
4290 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4291
4292 if (!GenResult)
4293 return GenResult.takeError();
4294
4295 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4296 }
4297 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4298 }
4299
4300 Builder.CreateStore(TargetElement, GlobVal);
4301 break;
4302 }
4303 case EvalKind::Complex: {
4304 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4305 RI.ElementType, ElemPtr, 0, 0, ".realp");
4306 Value *SrcReal = Builder.CreateLoad(
4307 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4308 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4309 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4310 Value *SrcImg = Builder.CreateLoad(
4311 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4312
4313 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4314 RI.ElementType, GlobVal, 0, 0, ".realp");
4315 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4316 RI.ElementType, GlobVal, 0, 1, ".imagp");
4317 Builder.CreateStore(SrcReal, DestRealPtr);
4318 Builder.CreateStore(SrcImg, DestImgPtr);
4319 break;
4320 }
4321 case EvalKind::Aggregate: {
4322 Value *SizeVal =
4323 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4324 Builder.CreateMemCpy(
4325 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4326 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4327 break;
4328 }
4329 }
4330 }
4331
4332 Builder.CreateRetVoid();
4333 return LtGCFunc;
4334}
4335
4336Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4337 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4338 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4339 IRBuilder<>::InsertPointGuard IPG(Builder);
4340 LLVMContext &Ctx = M.getContext();
4341 FunctionType *FuncTy = FunctionType::get(
4342 Builder.getVoidTy(),
4343 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4344 /* IsVarArg */ false);
4345 Function *LtGRFunc =
4347 "_omp_reduction_list_to_global_reduce_func", &M);
4348 LtGRFunc->setAttributes(FuncAttrs);
4349 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4350 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4351 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4352
4353 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4354 Builder.SetInsertPoint(EntryBlock);
4355 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4356
4357 // Buffer: global reduction buffer.
4358 Argument *BufferArg = LtGRFunc->getArg(0);
4359 // Idx: index of the buffer.
4360 Argument *IdxArg = LtGRFunc->getArg(1);
4361 // ReduceList: thread local Reduce list.
4362 Argument *ReduceListArg = LtGRFunc->getArg(2);
4363
4364 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4365 BufferArg->getName() + ".addr");
4366 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4367 IdxArg->getName() + ".addr");
4368 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4369 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4370 auto *RedListArrayTy =
4371 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4372
4373 // 1. Build a list of reduction variables.
4374 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4375 Value *LocalReduceList =
4376 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4377
4378 InsertPointTy AllocaIP(EntryBlock->begin());
4379
4380 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4381 BufferArgAlloca, Builder.getPtrTy(),
4382 BufferArgAlloca->getName() + ".ascast");
4383 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4384 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4385 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4386 ReduceListArgAlloca, Builder.getPtrTy(),
4387 ReduceListArgAlloca->getName() + ".ascast");
4388 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4389 LocalReduceList, Builder.getPtrTy(),
4390 LocalReduceList->getName() + ".ascast");
4391
4392 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4393 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4394 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4395
4396 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4397 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4398 Type *IndexTy = Builder.getIndexTy(
4399 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4400 for (auto En : enumerate(ReductionInfos)) {
4401 const ReductionInfo &RI = En.value();
4402
4403 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4404 RedListArrayTy, LocalReduceListAddrCast,
4405 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4406 Value *BufferVD =
4407 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4408 // Global = Buffer.VD[Idx];
4409 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4410 ReductionsBufferTy, BufferVD, 0, En.index());
4411
4412 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4413 // Get source descriptor from the reduce list argument
4414 Value *ReduceList =
4415 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4416 Value *SrcElementPtrPtr =
4417 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4418 {ConstantInt::get(IndexTy, 0),
4419 ConstantInt::get(IndexTy, En.index())});
4420 Value *SrcDescriptorAddr =
4421 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4422
4423 // Copy descriptor from source and update base_ptr to global buffer data
4424 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4425 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4426 if (!ByRefAlloc)
4427 return ByRefAlloc.takeError();
4428
4429 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4430 } else {
4431 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4432 }
4433 }
4434
4435 // Call reduce_function(GlobalReduceList, ReduceList)
4436 Value *ReduceList =
4437 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4438 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4439 ->addFnAttr(Attribute::NoUnwind);
4440 Builder.CreateRetVoid();
4441 return LtGRFunc;
4442}
4443
4444Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4445 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4446 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4447 IRBuilder<>::InsertPointGuard IPG(Builder);
4448 LLVMContext &Ctx = M.getContext();
4449 FunctionType *FuncTy = FunctionType::get(
4450 Builder.getVoidTy(),
4451 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4452 /* IsVarArg */ false);
4453 Function *GtLCFunc =
4455 "_omp_reduction_global_to_list_copy_func", &M);
4456 GtLCFunc->setAttributes(FuncAttrs);
4457 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4458 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4459 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4460
4461 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4462 Builder.SetInsertPoint(EntryBlock);
4463 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4464
4465 // Buffer: global reduction buffer.
4466 Argument *BufferArg = GtLCFunc->getArg(0);
4467 // Idx: index of the buffer.
4468 Argument *IdxArg = GtLCFunc->getArg(1);
4469 // ReduceList: thread local Reduce list.
4470 Argument *ReduceListArg = GtLCFunc->getArg(2);
4471
4472 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4473 BufferArg->getName() + ".addr");
4474 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4475 IdxArg->getName() + ".addr");
4476 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4477 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4478 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4479 BufferArgAlloca, Builder.getPtrTy(),
4480 BufferArgAlloca->getName() + ".ascast");
4481 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4482 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4483 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4484 ReduceListArgAlloca, Builder.getPtrTy(),
4485 ReduceListArgAlloca->getName() + ".ascast");
4486 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4487 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4488 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4489
4490 Value *LocalReduceList =
4491 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4492 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4493 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4494 Type *IndexTy = Builder.getIndexTy(
4495 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4496 for (auto En : enumerate(ReductionInfos)) {
4497 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4498 auto *RedListArrayTy =
4499 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4500 // Reduce element = LocalReduceList[i]
4501 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4502 RedListArrayTy, LocalReduceList,
4503 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4504 // elemptr = ((CopyType*)(elemptrptr)) + I
4505 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4506 // Global = Buffer.VD[Idx];
4507 Value *BufferVD =
4508 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4509 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4510 ReductionsBufferTy, BufferVD, 0, En.index());
4511
4512 switch (RI.EvaluationKind) {
4513 case EvalKind::Scalar: {
4514 Type *ElemType = RI.ElementType;
4515
4516 if (!IsByRef.empty() && IsByRef[En.index()]) {
4517 ElemType = RI.ByRefElementType;
4518 if (RI.DataPtrPtrGen) {
4519 InsertPointOrErrorTy GenResult =
4520 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4521
4522 if (!GenResult)
4523 return GenResult.takeError();
4524
4525 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4526 }
4527 }
4528
4529 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4530 Builder.CreateStore(TargetElement, ElemPtr);
4531 break;
4532 }
4533 case EvalKind::Complex: {
4534 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4535 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4536 Value *SrcReal = Builder.CreateLoad(
4537 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4538 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4539 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4540 Value *SrcImg = Builder.CreateLoad(
4541 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4542
4543 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4544 RI.ElementType, ElemPtr, 0, 0, ".realp");
4545 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4546 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4547 Builder.CreateStore(SrcReal, DestRealPtr);
4548 Builder.CreateStore(SrcImg, DestImgPtr);
4549 break;
4550 }
4551 case EvalKind::Aggregate: {
4552 Value *SizeVal =
4553 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4554 Builder.CreateMemCpy(
4555 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4556 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4557 SizeVal, false);
4558 break;
4559 }
4560 }
4561 }
4562
4563 Builder.CreateRetVoid();
4564 return GtLCFunc;
4565}
4566
4567Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4568 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4569 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4570 IRBuilder<>::InsertPointGuard IPG(Builder);
4571 LLVMContext &Ctx = M.getContext();
4572 auto *FuncTy = FunctionType::get(
4573 Builder.getVoidTy(),
4574 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4575 /* IsVarArg */ false);
4576 Function *GtLRFunc =
4578 "_omp_reduction_global_to_list_reduce_func", &M);
4579 GtLRFunc->setAttributes(FuncAttrs);
4580 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4581 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4582 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4583
4584 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4585 Builder.SetInsertPoint(EntryBlock);
4586 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4587
4588 // Buffer: global reduction buffer.
4589 Argument *BufferArg = GtLRFunc->getArg(0);
4590 // Idx: index of the buffer.
4591 Argument *IdxArg = GtLRFunc->getArg(1);
4592 // ReduceList: thread local Reduce list.
4593 Argument *ReduceListArg = GtLRFunc->getArg(2);
4594
4595 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4596 BufferArg->getName() + ".addr");
4597 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4598 IdxArg->getName() + ".addr");
4599 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4600 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4601 ArrayType *RedListArrayTy =
4602 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4603
4604 // 1. Build a list of reduction variables.
4605 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4606 Value *LocalReduceList =
4607 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4608
4609 InsertPointTy AllocaIP(EntryBlock->begin());
4610
4611 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4612 BufferArgAlloca, Builder.getPtrTy(),
4613 BufferArgAlloca->getName() + ".ascast");
4614 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4615 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4616 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4617 ReduceListArgAlloca, Builder.getPtrTy(),
4618 ReduceListArgAlloca->getName() + ".ascast");
4619 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4620 LocalReduceList, Builder.getPtrTy(),
4621 LocalReduceList->getName() + ".ascast");
4622
4623 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4624 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4625 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4626
4627 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4628 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4629 Type *IndexTy = Builder.getIndexTy(
4630 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4631 for (auto En : enumerate(ReductionInfos)) {
4632 const ReductionInfo &RI = En.value();
4633
4634 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4635 RedListArrayTy, ReductionList,
4636 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4637 // Global = Buffer.VD[Idx];
4638 Value *BufferVD =
4639 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4640 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4641 ReductionsBufferTy, BufferVD, 0, En.index());
4642
4643 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4644 // Get source descriptor from the reduce list
4645 Value *ReduceListVal =
4646 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4647 Value *SrcElementPtrPtr =
4648 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4649 {ConstantInt::get(IndexTy, 0),
4650 ConstantInt::get(IndexTy, En.index())});
4651 Value *SrcDescriptorAddr =
4652 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4653
4654 // Copy descriptor from source and update base_ptr to global buffer data
4655 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4656 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4657 if (!ByRefAlloc)
4658 return ByRefAlloc.takeError();
4659
4660 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4661 } else {
4662 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4663 }
4664 }
4665
4666 // Call reduce_function(ReduceList, GlobalReduceList)
4667 Value *ReduceList =
4668 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4669 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4670 ->addFnAttr(Attribute::NoUnwind);
4671 Builder.CreateRetVoid();
4672 return GtLRFunc;
4673}
4674
4675std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4676 std::string Suffix =
4677 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4678 return (Name + Suffix).str();
4679}
4680
4681Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4682 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4684 AttributeList FuncAttrs) {
4685 IRBuilder<>::InsertPointGuard IPG(Builder);
4686 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4687 {Builder.getPtrTy(), Builder.getPtrTy()},
4688 /* IsVarArg */ false);
4689 std::string Name = getReductionFuncName(ReducerName);
4690 Function *ReductionFunc =
4692 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4693 ReductionFunc->setAttributes(FuncAttrs);
4694 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4695 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4696 BasicBlock *EntryBB =
4697 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4698 Builder.SetInsertPoint(EntryBB);
4699 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4700
4701 // Need to alloca memory here and deal with the pointers before getting
4702 // LHS/RHS pointers out
4703 Value *LHSArrayPtr = nullptr;
4704 Value *RHSArrayPtr = nullptr;
4705 Argument *Arg0 = ReductionFunc->getArg(0);
4706 Argument *Arg1 = ReductionFunc->getArg(1);
4707 Type *Arg0Type = Arg0->getType();
4708 Type *Arg1Type = Arg1->getType();
4709
4710 Value *LHSAlloca =
4711 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4712 Value *RHSAlloca =
4713 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4714 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4715 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4716 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4717 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4718 Builder.CreateStore(Arg0, LHSAddrCast);
4719 Builder.CreateStore(Arg1, RHSAddrCast);
4720 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4721 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4722
4723 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4724 Type *IndexTy = Builder.getIndexTy(
4725 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4726 SmallVector<Value *> LHSPtrs, RHSPtrs;
4727 for (auto En : enumerate(ReductionInfos)) {
4728 const ReductionInfo &RI = En.value();
4729 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4730 RedArrayTy, RHSArrayPtr,
4731 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4732 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4733 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4734 RHSI8Ptr, RI.PrivateVariable->getType(),
4735 RHSI8Ptr->getName() + ".ascast");
4736
4737 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4738 RedArrayTy, LHSArrayPtr,
4739 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4740 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4741 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4742 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4743
4745 LHSPtrs.emplace_back(LHSPtr);
4746 RHSPtrs.emplace_back(RHSPtr);
4747 } else {
4748 Value *LHS = LHSPtr;
4749 Value *RHS = RHSPtr;
4750
4751 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4752 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4753 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4754 }
4755
4756 Value *Reduced;
4757 InsertPointOrErrorTy AfterIP =
4758 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4759 if (!AfterIP)
4760 return AfterIP.takeError();
4761 if (!Builder.GetInsertBlock())
4762 return ReductionFunc;
4763
4764 Builder.restoreIP(*AfterIP);
4765
4766 if (!IsByRef.empty() && !IsByRef[En.index()])
4767 Builder.CreateStore(Reduced, LHSPtr);
4768 }
4769 }
4770
4772 for (auto En : enumerate(ReductionInfos)) {
4773 unsigned Index = En.index();
4774 const ReductionInfo &RI = En.value();
4775 Value *LHSFixupPtr, *RHSFixupPtr;
4776 Builder.restoreIP(RI.ReductionGenClang(
4777 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4778
4779 // Fix the CallBack code genereated to use the correct Values for the LHS
4780 // and RHS
4781 LHSFixupPtr->replaceUsesWithIf(
4782 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4783 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4784 ReductionFunc;
4785 });
4786 RHSFixupPtr->replaceUsesWithIf(
4787 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4788 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4789 ReductionFunc;
4790 });
4791 }
4792
4793 Builder.CreateRetVoid();
4794 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4795 // to the entry block (this is dones for higher opt levels by later passes in
4796 // the pipeline). This has caused issues because non-entry `alloca`s force the
4797 // function to use dynamic stack allocations and we might run out of scratch
4798 // memory.
4799 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4800
4801 return ReductionFunc;
4802}
4803
4804static void
4806 bool IsGPU) {
4807 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4808 (void)RI;
4809 assert(RI.Variable && "expected non-null variable");
4810 assert(RI.PrivateVariable && "expected non-null private variable");
4811 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4812 "expected non-null reduction generator callback");
4813 if (!IsGPU) {
4814 assert(
4815 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4816 "expected variables and their private equivalents to have the same "
4817 "type");
4818 }
4819 assert(RI.Variable->getType()->isPointerTy() &&
4820 "expected variables to be pointers");
4821 }
4822}
4823
4824// The atomic cross-team reduction fast path applies when every reduction in the
4825// set can be represented by an atomicrmw. Clang only populates it for scalar
4826// reductions with a supported atomic operator.
4829 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4830 return static_cast<bool>(RI.AtomicReductionGen);
4831 });
4832}
4833
4835 const LocationDescription &Loc, InsertPointTy AllocaIP,
4836 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4837 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4838 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4839 Value *SrcLocInfo) {
4840 if (!updateToLocation(Loc))
4841 return InsertPointTy();
4842 Builder.restoreIP(CodeGenIP);
4843 Builder.SetCurrentDebugLocation(Loc.DL);
4844 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4845 LLVMContext &Ctx = M.getContext();
4846
4847 // Source location for the ident struct
4848 if (!SrcLocInfo) {
4849 uint32_t SrcLocStrSize;
4850 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4851 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4852 }
4853
4854 if (ReductionInfos.size() == 0)
4855 return Builder.saveIP();
4856
4857 BasicBlock *ContinuationBlock = nullptr;
4859 // Copied code from createReductions
4860 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
4861 ContinuationBlock = InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
4862 InsertBlock->getTerminator()->eraseFromParent();
4863 Builder.SetInsertPoint(InsertBlock->end());
4864 }
4865
4866 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4867 AttributeList FuncAttrs;
4868 AttrBuilder AttrBldr(Ctx);
4869 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4870 AttrBldr.addAttribute(Attr);
4871 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4872 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4873
4874 Expected<Function *> ReductionResult = createReductionFunction(
4875 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4876 ReductionGenCBKind, FuncAttrs);
4877 if (!ReductionResult)
4878 return ReductionResult.takeError();
4879 Function *ReductionFunc = *ReductionResult;
4880
4881 // Set the grid value in the config needed for lowering later on
4882 if (GridValue.has_value())
4883 Config.setGridValue(GridValue.value());
4884 else
4885 Config.setGridValue(getGridValue(T, ReductionFunc));
4886
4887 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4888 // RedList, shuffle_reduce_func, interwarp_copy_func);
4889 // or
4890 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4891 Value *Res;
4892
4893 // 1. Build a list of reduction variables.
4894 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4895 auto Size = ReductionInfos.size();
4896 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4897 Type *FuncPtrTy =
4898 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4899 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4900 Value *ReductionList;
4901 {
4903 Builder.restoreIP(AllocaIP);
4904 Value *ReductionListAlloca =
4905 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4906 ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4907 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4908 }
4909 Type *IndexTy = Builder.getIndexTy(
4910 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4911 for (auto En : enumerate(ReductionInfos)) {
4912 const ReductionInfo &RI = En.value();
4913 Value *ElemPtr = Builder.CreateInBoundsGEP(
4914 RedArrayTy, ReductionList,
4915 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4916
4917 Value *PrivateVar = RI.PrivateVariable;
4918 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4919 if (IsByRefElem)
4920 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4921
4922 Value *CastElem =
4923 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4924 Builder.CreateStore(CastElem, ElemPtr);
4925 }
4926 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4927 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4928
4929 if (!SarFunc)
4930 return SarFunc.takeError();
4931
4932 Expected<Function *> CopyResult =
4933 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4934 if (!CopyResult)
4935 return CopyResult.takeError();
4936 Function *WcFunc = *CopyResult;
4937
4938 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4939
4940 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4941 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4942 // not currently use it. It is computed here conservatively as max(element
4943 // sizes) * N rather than the exact sum, which over-calculates the size for
4944 // mixed reduction types but is harmless given the argument is unused.
4945 // TODO: Consider dropping this computation if the runtime API is ever revised
4946 // to remove the unused parameter.
4947 unsigned MaxDataSize = 0;
4948 SmallVector<Type *> ReductionTypeArgs;
4949 for (auto En : enumerate(ReductionInfos)) {
4950 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4951 // the actual data size stored in the global reduction buffer, consistent
4952 // with the ReductionsBufferTy struct used for GEP offsets below.
4953 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4954 ? En.value().ByRefElementType
4955 : En.value().ElementType;
4956 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4957 if (Size > MaxDataSize)
4958 MaxDataSize = Size;
4959 ReductionTypeArgs.emplace_back(RedTypeArg);
4960 }
4961 Value *ReductionDataSize =
4962 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4963
4964 // Helper function to copy thread-local data back to the original reduction
4965 // list.
4966 Function *CopyScratchToListFunc = nullptr;
4967 // Thread-local storage for the reduction variables.
4968 Value *ScratchForCopyBack = nullptr;
4969 // RL pointer to which the final value from the per-thread scratch should be
4970 // copied back. (Basically RL, appropriately casted if necessary.)
4971 Value *RLForCopyBack = RL;
4972
4973 bool IsAtomicReduction =
4974 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4975
4976 if (!IsTeamsReduction) {
4977 Value *SarFuncCast =
4978 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4979 Value *WcFuncCast =
4980 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4981 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4982 WcFuncCast};
4984 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4985 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4986 } else if (IsAtomicReduction) {
4987 // Atomic cross-team reduction fast path: determine the team's main thread
4988 // that is later to fold its value atomically into the mapped variable.
4989 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4990 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4991 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4992 } else {
4993 StructType *ReductionsBufferTy = StructType::create(
4994 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4995
4996 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4997 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4998 if (!LtGCFunc)
4999 return LtGCFunc.takeError();
5000
5001 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
5002 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
5003 if (!GtLCFunc)
5004 return GtLCFunc.takeError();
5005
5006 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
5007 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
5008 if (!GtLRFunc)
5009 return GtLRFunc.takeError();
5010
5011 // The runtime's cross-team final aggregate uses the storage pointed at by
5012 // its reduce-list argument as per-thread scratch. When the surrounding
5013 // kernel is already in SPMD execution mode, clang emitted each reduction
5014 // private as a per-thread `alloca addrspace(5)`, so the original red_list
5015 // (RL) is already per-thread and nothing else is needed.
5016 //
5017 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
5018 // Generic-mode globalization put the reduction private into team-shared
5019 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
5020 // point all threads of the last team would race on the shared LDS slot.
5021 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
5022 // value in, and hand the per-thread RL to the runtime instead. The writer
5023 // thread copies the final value from that per-thread scratch back to RL
5024 // before running the existing combine path below.
5025
5026 // Thread-local RL (might need localization below before being passed to the
5027 // runtime).
5028 Value *RuntimeRL = RL;
5029
5030 if (!IsSPMD) {
5031 Value *PerThreadScratch;
5032
5033 {
5035 Builder.restoreIP(AllocaIP);
5036 // Allocate thread-local buffer for the reduction variables.
5037 Value *PerThreadScratchAlloca =
5038 Builder.CreateAlloca(ReductionsBufferTy, /*ArraySize=*/nullptr,
5039 ".omp.reduction.scratch");
5040 PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
5041 PerThreadScratchAlloca, PtrTy,
5042 PerThreadScratchAlloca->getName() + ".ascast");
5043 // Allocate thread-local buffer for the pointers to the reduction
5044 // variables.
5045 Value *PerThreadRedListAlloca =
5046 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5047 ".omp.reduction.per_thread_red_list");
5048 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5049 PerThreadRedListAlloca, PtrTy,
5050 PerThreadRedListAlloca->getName() + ".ascast");
5051 }
5052
5053 // Iterate over the reduction variables and copy the team-local value to
5054 // the thread-local buffer.
5055 for (auto En : enumerate(ReductionInfos)) {
5056 const ReductionInfo &RI = En.value();
5057 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5058
5059 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5060 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5061 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5062 0, En.index());
5063
5064 Value *RuntimeListEntry = FieldPtr;
5065 if (IsByRefElem && RI.DataPtrPtrGen) {
5066 Value *SrcDescriptor =
5067 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5068 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5069 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5070 if (!Descriptor)
5071 return Descriptor.takeError();
5072 RuntimeListEntry = *Descriptor;
5073 }
5074 Builder.CreateStore(RuntimeListEntry, Slot);
5075 }
5076 // The copy helpers were emitted with default-AS (AS 0) pointer params
5077 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5078 // but PerThreadScratch and RL live in the target's default AS, which
5079 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5080 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5081 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5082 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5083 PerThreadScratch, CopyArg0Ty);
5084 RLForCopyBack =
5085 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5086 // Use index 0 because there is no array of target values to index into,
5087 // there is only one thread-local memory slot.
5088 // restoreIP above left a stale/empty debug location; this inlinable call
5089 // to a debug-info-bearing helper needs one or the verifier rejects the
5090 // module ("!dbg attachment points at wrong subprogram") after inlining.
5091 Builder.SetCurrentDebugLocation(Loc.DL);
5092 Builder.CreateCall(
5093 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5094 CopyScratchToListFunc = *GtLCFunc;
5095 }
5096
5097 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5098 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5099
5100 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5101 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5102 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5103 }
5104
5105 // 5. Build if (res == 1)
5106 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5107 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5108 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5109 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5110
5111 // 6. Build then branch: where we have reduced values in the master
5112 // thread in each team.
5113 // __kmpc_end_reduce{_nowait}(<gtid>);
5114 // break;
5115 emitBlock(ThenBB, CurFunc);
5116
5117 // Copy the writer thread's per-thread scratch result back into the original
5118 // red-list storage before the existing combine path reads RI.PrivateVariable.
5119 // Set a debug location: this inlinable call to a debug-info-bearing helper
5120 // needs one or the verifier rejects the module after inlining.
5121 if (ScratchForCopyBack) {
5122 Builder.SetCurrentDebugLocation(Loc.DL);
5123 Builder.CreateCall(
5124 CopyScratchToListFunc,
5125 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5126 }
5127
5128 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5129 for (auto En : enumerate(ReductionInfos)) {
5130 const ReductionInfo &RI = En.value();
5131
5132 // Atomic cross-team fast path: each team's main thread folds its
5133 // team-reduced value directly into the mapped reduction variable with a
5134 // single atomicrmw.
5135 if (IsAtomicReduction) {
5137 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5138 if (!AfterIP)
5139 return AfterIP.takeError();
5140 Builder.restoreIP(*AfterIP);
5141 continue;
5142 }
5143
5145 Value *RedValue = RI.Variable;
5146
5147 Value *RHS =
5148 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5149
5151 Value *LHSPtr, *RHSPtr;
5152 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5153 &LHSPtr, &RHSPtr, CurFunc));
5154
5155 // Fix the CallBack code genereated to use the correct Values for the LHS
5156 // and RHS. Cast to match types before replacing (necessary to handle
5157 // different address spaces).
5158 if (LHSPtr->getType() != RedValue->getType())
5159 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5160 RedValue, LHSPtr->getType());
5161 if (RHSPtr->getType() != RHS->getType())
5162 RHS =
5163 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5164
5165 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5166 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5167 ReductionFunc;
5168 });
5169 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5170 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5171 ReductionFunc;
5172 });
5173 } else {
5174 if (IsByRef.empty() || !IsByRef[En.index()]) {
5175 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5176 "red.value." + Twine(En.index()));
5177 }
5178 Value *PrivateRedValue = Builder.CreateLoad(
5179 ValueType, RHS, "red.private.value" + Twine(En.index()));
5180 Value *Reduced;
5181 InsertPointOrErrorTy AfterIP =
5182 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5183 if (!AfterIP)
5184 return AfterIP.takeError();
5185 Builder.restoreIP(*AfterIP);
5186
5187 if (!IsByRef.empty() && !IsByRef[En.index()])
5188 Builder.CreateStore(Reduced, RI.Variable);
5189 }
5190 }
5191 emitBlock(ExitBB, CurFunc);
5192 if (ContinuationBlock) {
5193 Builder.CreateBr(ContinuationBlock);
5194 Builder.SetInsertPoint(ContinuationBlock);
5195 }
5196 Config.setEmitLLVMUsed();
5197
5198 return Builder.saveIP();
5199}
5200
5202 Type *VoidTy = Type::getVoidTy(M.getContext());
5203 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5204 auto *FuncTy =
5205 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5207 ".omp.reduction.func", &M);
5208}
5209
5211 Function *ReductionFunc,
5213 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5214 IRBuilder<>::InsertPointGuard IPG(Builder);
5215 Module *Module = ReductionFunc->getParent();
5216 BasicBlock *ReductionFuncBlock =
5217 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5218 Builder.SetInsertPoint(ReductionFuncBlock);
5219 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5220 Value *LHSArrayPtr = nullptr;
5221 Value *RHSArrayPtr = nullptr;
5222 if (IsGPU) {
5223 // Need to alloca memory here and deal with the pointers before getting
5224 // LHS/RHS pointers out
5225 //
5226 Argument *Arg0 = ReductionFunc->getArg(0);
5227 Argument *Arg1 = ReductionFunc->getArg(1);
5228 Type *Arg0Type = Arg0->getType();
5229 Type *Arg1Type = Arg1->getType();
5230
5231 Value *LHSAlloca =
5232 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5233 Value *RHSAlloca =
5234 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5235 Value *LHSAddrCast =
5236 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5237 Value *RHSAddrCast =
5238 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5239 Builder.CreateStore(Arg0, LHSAddrCast);
5240 Builder.CreateStore(Arg1, RHSAddrCast);
5241 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5242 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5243 } else {
5244 LHSArrayPtr = ReductionFunc->getArg(0);
5245 RHSArrayPtr = ReductionFunc->getArg(1);
5246 }
5247
5248 unsigned NumReductions = ReductionInfos.size();
5249 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5250
5251 for (auto En : enumerate(ReductionInfos)) {
5252 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5253 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5254 RedArrayTy, LHSArrayPtr, 0, En.index());
5255 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5256 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5257 LHSI8Ptr, RI.Variable->getType());
5258 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5259 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5260 RedArrayTy, RHSArrayPtr, 0, En.index());
5261 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5262 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5263 RHSI8Ptr, RI.PrivateVariable->getType());
5264 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5265 Value *Reduced;
5267 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5268 if (!AfterIP)
5269 return AfterIP.takeError();
5270
5271 Builder.restoreIP(*AfterIP);
5272 // TODO: Consider flagging an error.
5273 if (!Builder.GetInsertBlock())
5274 return Error::success();
5275
5276 // store is inside of the reduction region when using by-ref
5277 if (!IsByRef[En.index()])
5278 Builder.CreateStore(Reduced, LHSPtr);
5279 }
5280 Builder.CreateRetVoid();
5281 return Error::success();
5282}
5283
5285 const LocationDescription &Loc, InsertPointTy AllocaIP,
5286 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5287 bool IsNoWait, bool IsTeamsReduction) {
5288 assert(ReductionInfos.size() == IsByRef.size());
5289 if (Config.isGPU())
5290 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5291 IsByRef, IsNoWait, IsTeamsReduction);
5292
5293 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5294
5295 if (!updateToLocation(Loc))
5296 return InsertPointTy();
5297
5298 if (ReductionInfos.size() == 0)
5299 return Builder.saveIP();
5300
5301 BasicBlock *InsertBlock = Loc.IP.getNodeParent();
5302 BasicBlock *ContinuationBlock =
5303 InsertBlock->splitBasicBlock(Loc.IP, "reduce.finalize");
5304 InsertBlock->getTerminator()->eraseFromParent();
5305
5306 // Create and populate array of type-erased pointers to private reduction
5307 // values.
5308 unsigned NumReductions = ReductionInfos.size();
5309 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5310 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getTerminator());
5311 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5312
5313 Builder.SetInsertPoint(InsertBlock->end());
5314 // Emitting the alloca moved the insertion point into the alloca block and
5315 // can clear the debug loc. Restore back to Loc.DL.
5316 Builder.SetCurrentDebugLocation(Loc.DL);
5317
5318 for (auto En : enumerate(ReductionInfos)) {
5319 unsigned Index = En.index();
5320 const ReductionInfo &RI = En.value();
5321 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5322 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5323 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5324 }
5325
5326 // Emit a call to the runtime function that orchestrates the reduction.
5327 // Declare the reduction function in the process.
5328 Type *IndexTy = Builder.getIndexTy(
5329 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5330 Function *Func = Builder.GetInsertBlock()->getParent();
5331 Module *Module = Func->getParent();
5332 uint32_t SrcLocStrSize;
5333 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5334 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5335 return RI.AtomicReductionGen;
5336 });
5337 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5338 CanGenerateAtomic
5339 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5340 : IdentFlag(0));
5341 Value *ThreadId = getOrCreateThreadID(Ident);
5342 Constant *NumVariables = Builder.getInt32(NumReductions);
5343 const DataLayout &DL = Module->getDataLayout();
5344 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5345 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5346 Function *ReductionFunc = getFreshReductionFunc(*Module);
5347 Value *Lock = getOMPCriticalRegionLock(".reduction");
5349 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5350 : RuntimeFunction::OMPRTL___kmpc_reduce);
5351 CallInst *ReduceCall =
5352 createRuntimeFunctionCall(ReduceFunc,
5353 {Ident, ThreadId, NumVariables, RedArraySize,
5354 RedArray, ReductionFunc, Lock},
5355 "reduce");
5356
5357 // Create final reduction entry blocks for the atomic and non-atomic case.
5358 // Emit IR that dispatches control flow to one of the blocks based on the
5359 // reduction supporting the atomic mode.
5360 BasicBlock *NonAtomicRedBlock =
5361 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5362 BasicBlock *AtomicRedBlock =
5363 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5364 SwitchInst *Switch =
5365 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5366 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5367 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5368
5369 // Populate the non-atomic reduction using the elementwise reduction function.
5370 // This loads the elements from the global and private variables and reduces
5371 // them before storing back the result to the global variable.
5372 Builder.SetInsertPoint(NonAtomicRedBlock);
5373 for (auto En : enumerate(ReductionInfos)) {
5374 const ReductionInfo &RI = En.value();
5376 // We have one less load for by-ref case because that load is now inside of
5377 // the reduction region
5378 Value *RedValue = RI.Variable;
5379 if (!IsByRef[En.index()]) {
5380 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5381 "red.value." + Twine(En.index()));
5382 }
5383 Value *PrivateRedValue =
5384 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5385 "red.private.value." + Twine(En.index()));
5386 Value *Reduced;
5387 InsertPointOrErrorTy AfterIP =
5388 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5389 if (!AfterIP)
5390 return AfterIP.takeError();
5391 Builder.restoreIP(*AfterIP);
5392
5393 if (!Builder.GetInsertBlock())
5394 return InsertPointTy();
5395 // for by-ref case, the load is inside of the reduction region
5396 if (!IsByRef[En.index()])
5397 Builder.CreateStore(Reduced, RI.Variable);
5398 }
5399 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5400 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5401 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5402 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5403 Builder.CreateBr(ContinuationBlock);
5404
5405 // Populate the atomic reduction using the atomic elementwise reduction
5406 // function. There are no loads/stores here because they will be happening
5407 // inside the atomic elementwise reduction.
5408 Builder.SetInsertPoint(AtomicRedBlock);
5409 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5410 for (const ReductionInfo &RI : ReductionInfos) {
5412 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5413 if (!AfterIP)
5414 return AfterIP.takeError();
5415 Builder.restoreIP(*AfterIP);
5416 if (!Builder.GetInsertBlock())
5417 return InsertPointTy();
5418 }
5419 Builder.CreateBr(ContinuationBlock);
5420 } else {
5421 Builder.CreateUnreachable();
5422 }
5423
5424 // Populate the outlined reduction function using the elementwise reduction
5425 // function. Partial values are extracted from the type-erased array of
5426 // pointers to private variables.
5427 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5428 IsByRef, /*isGPU=*/false);
5429 if (Err)
5430 return Err;
5431
5432 if (!Builder.GetInsertBlock())
5433 return InsertPointTy();
5434
5435 Builder.SetInsertPoint(ContinuationBlock);
5436 return Builder.saveIP();
5437}
5438
5441 BodyGenCallbackTy BodyGenCB,
5442 FinalizeCallbackTy FiniCB) {
5443 if (!updateToLocation(Loc))
5444 return Loc.IP;
5445
5446 Directive OMPD = Directive::OMPD_master;
5447 uint32_t SrcLocStrSize;
5448 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5449 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5450 Value *ThreadId = getOrCreateThreadID(Ident);
5451 Value *Args[] = {Ident, ThreadId};
5452
5453 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5454 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5455
5456 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5457 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5458
5459 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5460 /*Conditional*/ true, /*hasFinalize*/ true);
5461}
5462
5465 BodyGenCallbackTy BodyGenCB,
5466 FinalizeCallbackTy FiniCB, Value *Filter) {
5468 if (!updateToLocation(Loc))
5469 return Loc.IP;
5470
5471 Directive OMPD = Directive::OMPD_masked;
5472 uint32_t SrcLocStrSize;
5473 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5474 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5475 Value *ThreadId = getOrCreateThreadID(Ident);
5476 Value *Args[] = {Ident, ThreadId, Filter};
5477 Value *ArgsEnd[] = {Ident, ThreadId};
5478
5479 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5480 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5481
5482 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5483 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5484
5485 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5486 /*Conditional*/ true, /*hasFinalize*/ true);
5487}
5488
5490 llvm::FunctionCallee Callee,
5492 const llvm::Twine &Name) {
5493 llvm::CallInst *Call = Builder.CreateCall(
5494 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5495 Call->setDoesNotThrow();
5496 return Call;
5497}
5498
5499// Expects input basic block is dominated by BeforeScanBB.
5500// Once Scan directive is encountered, the code after scan directive should be
5501// dominated by AfterScanBB. Scan directive splits the code sequence to
5502// scan and input phase. Based on whether inclusive or exclusive
5503// clause is used in the scan directive and whether input loop or scan loop
5504// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5505// input loop and second is the scan loop. The code generated handles only
5506// inclusive scans now.
5508 const LocationDescription &Loc, InsertPointTy AllocaIP,
5509 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5510 bool IsInclusive, ScanInfo *ScanRedInfo) {
5511 if (ScanRedInfo->OMPFirstScanLoop) {
5512 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5513 ScanVarsType, ScanRedInfo);
5514 if (Err)
5515 return Err;
5516 }
5517 if (!updateToLocation(Loc))
5518 return Loc.IP;
5519
5520 llvm::Value *IV = ScanRedInfo->IV;
5521
5522 if (ScanRedInfo->OMPFirstScanLoop) {
5523 // Emit buffer[i] = red; at the end of the input phase.
5524 for (size_t i = 0; i < ScanVars.size(); i++) {
5525 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5526 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5527 Type *DestTy = ScanVarsType[i];
5528 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5529 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5530
5531 Builder.CreateStore(Src, Val);
5532 }
5533 }
5534 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5535 emitBlock(ScanRedInfo->OMPScanDispatch,
5536 Builder.GetInsertBlock()->getParent());
5537
5538 if (!ScanRedInfo->OMPFirstScanLoop) {
5539 IV = ScanRedInfo->IV;
5540 // Emit red = buffer[i]; at the entrance to the scan phase.
5541 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5542 for (size_t i = 0; i < ScanVars.size(); i++) {
5543 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5544 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5545 Type *DestTy = ScanVarsType[i];
5546 Value *SrcPtr =
5547 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5548 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5549 Builder.CreateStore(Src, ScanVars[i]);
5550 }
5551 }
5552
5553 // TODO: Update it to CreateBr and remove dead blocks
5554 llvm::Value *CmpI = Builder.getInt1(true);
5555 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5556 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5557 ScanRedInfo->OMPAfterScanBlock);
5558 } else {
5559 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5560 ScanRedInfo->OMPBeforeScanBlock);
5561 }
5562 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5563 Builder.GetInsertBlock()->getParent());
5564 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5565 return Builder.saveIP();
5566}
5567
5568Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5569 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5570 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5571
5572 Builder.restoreIP(AllocaIP);
5573 // Create the shared pointer at alloca IP.
5574 for (size_t i = 0; i < ScanVars.size(); i++) {
5575 llvm::Value *BuffPtr =
5576 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5577 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5578 }
5579
5580 // Allocate temporary buffer by master thread
5581 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5582 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5583 Builder.restoreIP(CodeGenIP);
5584 Value *AllocSpan =
5585 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5586 for (size_t i = 0; i < ScanVars.size(); i++) {
5587 Type *IntPtrTy = Builder.getInt32Ty();
5588 Value *Allocsize = Builder.CreateTypeSize(
5589 IntPtrTy, M.getDataLayout().getTypeAllocSize(ScanVarsType[i]));
5590 Value *Buff =
5591 Builder.CreateMalloc(IntPtrTy, Allocsize, AllocSpan, nullptr, "arr");
5592 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5593 }
5594 return Error::success();
5595 };
5596 // TODO: Perform finalization actions for variables. This has to be
5597 // called for variables which have destructors/finalizers.
5598 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5599
5600 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5601 llvm::Value *FilterVal = Builder.getInt32(0);
5603 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5604
5605 if (!AfterIP)
5606 return AfterIP.takeError();
5607 Builder.restoreIP(*AfterIP);
5608 BasicBlock *InputBB = Builder.GetInsertBlock();
5609 if (InputBB->hasTerminator())
5610 Builder.SetInsertPoint(InputBB->getTerminator());
5611 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5612 if (!AfterIP)
5613 return AfterIP.takeError();
5614 Builder.restoreIP(*AfterIP);
5615
5616 return Error::success();
5617}
5618
5619Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5620 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5621 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5622 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5623 Builder.restoreIP(CodeGenIP);
5624 for (ReductionInfo RedInfo : ReductionInfos) {
5625 Value *PrivateVar = RedInfo.PrivateVariable;
5626 Value *OrigVar = RedInfo.Variable;
5627 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5628 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5629
5630 Type *SrcTy = RedInfo.ElementType;
5631 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5632 "arrayOffset");
5633 Value *Src = Builder.CreateLoad(SrcTy, Val);
5634
5635 Builder.CreateStore(Src, OrigVar);
5636 Builder.CreateFree(Buff);
5637 }
5638 return Error::success();
5639 };
5640 // TODO: Perform finalization actions for variables. This has to be
5641 // called for variables which have destructors/finalizers.
5642 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5643
5644 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5645 Builder.SetInsertPoint(TI);
5646 else
5647 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5648
5649 llvm::Value *FilterVal = Builder.getInt32(0);
5651 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5652
5653 if (!AfterIP)
5654 return AfterIP.takeError();
5655 Builder.restoreIP(*AfterIP);
5656 BasicBlock *InputBB = Builder.GetInsertBlock();
5657 if (InputBB->hasTerminator())
5658 Builder.SetInsertPoint(InputBB->getTerminator());
5659 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5660 if (!AfterIP)
5661 return AfterIP.takeError();
5662 Builder.restoreIP(*AfterIP);
5663 return Error::success();
5664}
5665
5667 const LocationDescription &Loc,
5669 ScanInfo *ScanRedInfo) {
5670
5671 if (!updateToLocation(Loc))
5672 return Loc.IP;
5673 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5674 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5675 Builder.restoreIP(CodeGenIP);
5676 Function *CurFn = Builder.GetInsertBlock()->getParent();
5677 // for (int k = 0; k <= ceil(log2(n)); ++k)
5678 llvm::BasicBlock *LoopBB =
5679 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5680 llvm::BasicBlock *ExitBB =
5681 splitBB(Builder, false, "omp.outer.log.scan.exit");
5683 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::log2,
5684 Builder.getDoubleTy());
5685 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5686 llvm::Value *Arg =
5687 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5688 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5690 Builder.getModule(), (llvm::Intrinsic::ID)llvm::Intrinsic::ceil,
5691 Builder.getDoubleTy());
5692 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5693 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5694 llvm::Value *NMin1 = Builder.CreateNUWSub(
5695 ScanRedInfo->Span,
5696 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5697 Builder.SetInsertPoint(InputBB);
5698 Builder.CreateBr(LoopBB);
5699 emitBlock(LoopBB, CurFn);
5700 Builder.SetInsertPoint(LoopBB);
5701
5702 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5703 // size pow2k = 1;
5704 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5705 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5706 InputBB);
5707 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5708 InputBB);
5709 // for (size i = n - 1; i >= 2 ^ k; --i)
5710 // tmp[i] op= tmp[i-pow2k];
5711 llvm::BasicBlock *InnerLoopBB =
5712 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5713 llvm::BasicBlock *InnerExitBB =
5714 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5715 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5716 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5717 emitBlock(InnerLoopBB, CurFn);
5718 Builder.SetInsertPoint(InnerLoopBB);
5719 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5720 IVal->addIncoming(NMin1, LoopBB);
5721 for (ReductionInfo RedInfo : ReductionInfos) {
5722 Value *ReductionVal = RedInfo.PrivateVariable;
5723 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5724 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5725 Type *DestTy = RedInfo.ElementType;
5726 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5727 Value *LHSPtr =
5728 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5729 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5730 Value *RHSPtr =
5731 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5732 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5733 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5734 llvm::Value *Result;
5735 InsertPointOrErrorTy AfterIP =
5736 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5737 if (!AfterIP)
5738 return AfterIP.takeError();
5739 Builder.CreateStore(Result, LHSPtr);
5740 }
5741 llvm::Value *NextIVal = Builder.CreateNUWSub(
5742 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5743 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5744 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5745 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5746 emitBlock(InnerExitBB, CurFn);
5747 llvm::Value *Next = Builder.CreateNUWAdd(
5748 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5749 Counter->addIncoming(Next, Builder.GetInsertBlock());
5750 // pow2k <<= 1;
5751 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5752 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5753 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5754 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5755 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5756 return Error::success();
5757 };
5758
5759 // TODO: Perform finalization actions for variables. This has to be
5760 // called for variables which have destructors/finalizers.
5761 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5762
5763 llvm::Value *FilterVal = Builder.getInt32(0);
5765 createMasked(Builder, BodyGenCB, FiniCB, FilterVal);
5766
5767 if (!AfterIP)
5768 return AfterIP.takeError();
5769 Builder.restoreIP(*AfterIP);
5770 AfterIP = createBarrier(Builder, llvm::omp::OMPD_barrier);
5771
5772 if (!AfterIP)
5773 return AfterIP.takeError();
5774 Builder.restoreIP(*AfterIP);
5775 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5776 if (Err)
5777 return Err;
5778
5779 return AfterIP;
5780}
5781
5782Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5783 llvm::function_ref<Error()> InputLoopGen,
5784 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5785 ScanInfo *ScanRedInfo) {
5786
5787 {
5788 // Emit loop with input phase:
5789 // for (i: 0..<num_iters>) {
5790 // <input phase>;
5791 // buffer[i] = red;
5792 // }
5793 ScanRedInfo->OMPFirstScanLoop = true;
5794 Error Err = InputLoopGen();
5795 if (Err)
5796 return Err;
5797 }
5798 {
5799 // Emit loop with scan phase:
5800 // for (i: 0..<num_iters>) {
5801 // red = buffer[i];
5802 // <scan phase>;
5803 // }
5804 ScanRedInfo->OMPFirstScanLoop = false;
5805 Error Err = ScanLoopGen(Builder);
5806 if (Err)
5807 return Err;
5808 }
5809 return Error::success();
5810}
5811
5812void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5813 Function *Fun = Builder.GetInsertBlock()->getParent();
5814 ScanRedInfo->OMPScanDispatch =
5815 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5816 ScanRedInfo->OMPAfterScanBlock =
5817 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5818 ScanRedInfo->OMPBeforeScanBlock =
5819 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5820 ScanRedInfo->OMPScanLoopExit =
5821 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5822}
5824 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5825 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5826 Module *M = F->getParent();
5827 LLVMContext &Ctx = M->getContext();
5828 Type *IndVarTy = TripCount->getType();
5829
5830 // Create the basic block structure.
5831 BasicBlock *Preheader =
5832 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5833 BasicBlock *Header =
5834 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5835 BasicBlock *Cond =
5836 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5837 BasicBlock *Body =
5838 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5839 BasicBlock *Latch =
5840 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5841 BasicBlock *Exit =
5842 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5843 BasicBlock *After =
5844 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5845
5846 // Use specified DebugLoc for new instructions.
5847 Builder.SetCurrentDebugLocation(DL);
5848
5849 Builder.SetInsertPoint(Preheader);
5850 Builder.CreateBr(Header);
5851
5852 Builder.SetInsertPoint(Header);
5853 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5854 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5855 Builder.CreateBr(Cond);
5856
5857 Builder.SetInsertPoint(Cond);
5858 Value *Cmp =
5859 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5860 Builder.CreateCondBr(Cmp, Body, Exit);
5861
5862 Builder.SetInsertPoint(Body);
5863 Builder.CreateBr(Latch);
5864
5865 Builder.SetInsertPoint(Latch);
5866 // Decide whether the induction variable increment can carry nsw.
5867 //
5868 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5869 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5870 // for valid programs 0 <= count <= INT_MAX always holds.
5871 //
5872 // Collapsed loops: the trip count is a product that can overflow i32 even for
5873 // a conforming program, so nsw is kept only when the product is a constant
5874 // that provably fits, dropped otherwise.
5875 bool HasNSW = Config.hasNoSignedWrap();
5876 if (HasNSW) {
5877 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5878 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5880 if (CI->getValue().ugt(SignedMax))
5881 HasNSW = false;
5882 } else if (IsCollapsed) {
5883 HasNSW = false;
5884 }
5885 }
5886 Value *Next =
5887 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5888 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5889 Builder.CreateBr(Header);
5890 IndVarPHI->addIncoming(Next, Latch);
5891
5892 Builder.SetInsertPoint(Exit);
5893 Builder.CreateBr(After);
5894
5895 // Remember and return the canonical control flow.
5896 LoopInfos.emplace_front();
5897 CanonicalLoopInfo *CL = &LoopInfos.front();
5898
5899 CL->Header = Header;
5900 CL->Cond = Cond;
5901 CL->Latch = Latch;
5902 CL->Exit = Exit;
5903
5904#ifndef NDEBUG
5905 CL->assertOK();
5906#endif
5907 return CL;
5908}
5909
5912 LoopBodyGenCallbackTy BodyGenCB,
5913 Value *TripCount, const Twine &Name) {
5914 BasicBlock *BB = Loc.IP.getNodeParent();
5915 BasicBlock *NextBB = BB->getNextNode();
5916
5917 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5918 NextBB, NextBB, Name);
5919 BasicBlock *After = CL->getAfter();
5920
5921 // If location is not set, don't connect the loop.
5922 if (updateToLocation(Loc)) {
5923 // Split the loop at the insertion point: Branch to the preheader and move
5924 // every following instruction to after the loop (the After BB). Also, the
5925 // new successor is the loop's after block.
5926 spliceBB(Builder, After, /*CreateBranch=*/false);
5927 Builder.CreateBr(CL->getPreheader());
5928 }
5929
5930 // Emit the body content. We do it after connecting the loop to the CFG to
5931 // avoid that the callback encounters degenerate BBs.
5932 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5933 return Err;
5934
5935#ifndef NDEBUG
5936 CL->assertOK();
5937#endif
5938 return CL;
5939}
5940
5942 ScanInfos.emplace_front();
5943 ScanInfo *Result = &ScanInfos.front();
5944 return Result;
5945}
5946
5950 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5951 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5952 LocationDescription ComputeLoc =
5953 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5954 updateToLocation(ComputeLoc);
5955
5957
5959 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5960 ScanRedInfo->Span = TripCount;
5961 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5962 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5963
5964 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5965 Builder.restoreIP(CodeGenIP);
5966 ScanRedInfo->IV = IV;
5967 createScanBBs(ScanRedInfo);
5968 BasicBlock *InputBlock = Builder.GetInsertBlock();
5969 Instruction *Terminator = InputBlock->getTerminator();
5970 assert(Terminator->getNumSuccessors() == 1);
5971 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5972 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5973 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5974 Builder.GetInsertBlock()->getParent());
5975 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5976 emitBlock(ScanRedInfo->OMPScanLoopExit,
5977 Builder.GetInsertBlock()->getParent());
5978 Builder.CreateBr(ContinueBlock);
5979 Builder.SetInsertPoint(
5980 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5981 return BodyGenCB(Builder.saveIP(), IV);
5982 };
5983
5984 const auto &&InputLoopGen = [&]() -> Error {
5986 createCanonicalLoop(Builder, BodyGen, Start, Stop, Step, IsSigned,
5987 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5988 if (!LoopInfo)
5989 return LoopInfo.takeError();
5990 Result.push_back(*LoopInfo);
5991 Builder.restoreIP((*LoopInfo)->getAfterIP());
5992 return Error::success();
5993 };
5994 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5996 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5997 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5998 if (!LoopInfo)
5999 return LoopInfo.takeError();
6000 Result.push_back(*LoopInfo);
6001 Builder.restoreIP((*LoopInfo)->getAfterIP());
6002 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
6003 return Error::success();
6004 };
6005 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
6006 if (Err)
6007 return Err;
6008 return Result;
6009}
6010
6012 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
6013 bool IsSigned, bool InclusiveStop, const Twine &Name) {
6014
6015 // Consider the following difficulties (assuming 8-bit signed integers):
6016 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
6017 // DO I = 1, 100, 50
6018 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
6019 // DO I = 100, 0, -128
6020
6021 // Start, Stop and Step must be of the same integer type.
6022 auto *IndVarTy = cast<IntegerType>(Start->getType());
6023 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
6024 assert(IndVarTy == Step->getType() && "Step type mismatch");
6025
6027
6028 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
6029 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
6030
6031 // Like Step, but always positive.
6032 Value *Incr = Step;
6033
6034 // Distance between Start and Stop; always positive.
6035 Value *Span;
6036
6037 // Condition whether there are no iterations are executed at all, e.g. because
6038 // UB < LB.
6039 Value *ZeroCmp;
6040
6041 if (IsSigned) {
6042 // Ensure that increment is positive. If not, negate and invert LB and UB.
6043 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
6044 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
6045 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6046 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6047 Span = Builder.CreateSub(UB, LB, "", false, true);
6048 ZeroCmp = Builder.CreateICmp(
6049 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6050 } else {
6051 Span = Builder.CreateSub(Stop, Start, "", true);
6052 ZeroCmp = Builder.CreateICmp(
6053 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6054 }
6055
6056 Value *CountIfLooping;
6057 if (InclusiveStop) {
6058 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6059 } else {
6060 // Avoid incrementing past stop since it could overflow.
6061 Value *CountIfTwo = Builder.CreateAdd(
6062 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6063 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6064 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6065 }
6066
6067 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6068 "omp_" + Name + ".tripcount");
6069}
6070
6073 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6074 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6075 ScanInfo *ScanRedInfo) {
6076 LocationDescription ComputeLoc =
6077 ComputeIP.isValid() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6078
6080 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6081
6082 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6083 Builder.restoreIP(CodeGenIP);
6084 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6085 /*HasNSW=*/Config.hasNoSignedWrap());
6086 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6087 /*HasNSW=*/Config.hasNoSignedWrap());
6088 if (InScan)
6089 ScanRedInfo->IV = IndVar;
6090 return BodyGenCB(Builder.saveIP(), IndVar);
6091 };
6092 LocationDescription LoopLoc =
6093 ComputeIP.isValid()
6094 ? Loc
6095 : LocationDescription(Builder.saveIP(),
6096 Builder.getCurrentDebugLocation());
6097 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6098}
6099
6100// Returns an LLVM function to call for initializing loop bounds using OpenMP
6101// static scheduling for composite `distribute parallel for` depending on
6102// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6103// integers as unsigned similarly to CanonicalLoopInfo.
6104static FunctionCallee
6106 OpenMPIRBuilder &OMPBuilder) {
6107 unsigned Bitwidth = Ty->getIntegerBitWidth();
6108 if (Bitwidth == 32)
6109 return OMPBuilder.getOrCreateRuntimeFunction(
6110 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6111 if (Bitwidth == 64)
6112 return OMPBuilder.getOrCreateRuntimeFunction(
6113 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6114 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6115}
6116
6117// Returns an LLVM function to call for initializing loop bounds using OpenMP
6118// static scheduling depending on `type`. Only i32 and i64 are supported by the
6119// runtime. Always interpret integers as unsigned similarly to
6120// CanonicalLoopInfo.
6122 OpenMPIRBuilder &OMPBuilder) {
6123 unsigned Bitwidth = Ty->getIntegerBitWidth();
6124 if (Bitwidth == 32)
6125 return OMPBuilder.getOrCreateRuntimeFunction(
6126 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6127 if (Bitwidth == 64)
6128 return OMPBuilder.getOrCreateRuntimeFunction(
6129 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6130 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6131}
6132
6133OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6134 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6135 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6136 OMPScheduleType DistScheduleSchedType) {
6137 assert(CLI->isValid() && "Requires a valid canonical loop");
6138 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6139 "Require dedicated allocate IP");
6140
6141 // Set up the source location value for OpenMP runtime.
6142 Builder.restoreIP(CLI->getPreheaderIP());
6143 Builder.SetCurrentDebugLocation(DL);
6144
6145 uint32_t SrcLocStrSize;
6146 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6148 switch (LoopType) {
6149 case WorksharingLoopType::ForStaticLoop:
6150 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6151 break;
6152 case WorksharingLoopType::DistributeStaticLoop:
6153 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6154 break;
6155 case WorksharingLoopType::DistributeForStaticLoop:
6156 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6157 break;
6158 }
6159 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6160
6161 // Declare useful OpenMP runtime functions.
6162 Type *IVTy = CLI->getIndVarType();
6163 FunctionCallee StaticInit =
6164 LoopType == WorksharingLoopType::DistributeForStaticLoop
6165 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6166 : getKmpcForStaticInitForType(IVTy, M, *this);
6167 FunctionCallee StaticFini =
6168 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6169
6170 // Allocate space for computed loop bounds as expected by the "init" function.
6171 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getFirstNonPHIOrAlloca());
6172
6173 Type *I32Type = Type::getInt32Ty(M.getContext());
6174 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6175 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6176 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6177 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6178 CLI->setLastIter(PLastIter);
6179
6180 // At the end of the preheader, prepare for calling the "init" function by
6181 // storing the current loop bounds into the allocated space. A canonical loop
6182 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6183 // and produces an inclusive upper bound.
6184 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6185 Constant *Zero = ConstantInt::get(IVTy, 0);
6186 Constant *One = ConstantInt::get(IVTy, 1);
6187 Builder.CreateStore(Zero, PLowerBound);
6188 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6189 Builder.CreateStore(UpperBound, PUpperBound);
6190 Builder.CreateStore(One, PStride);
6191
6192 Value *ThreadNum =
6193 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6194
6195 OMPScheduleType SchedType =
6196 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6197 ? OMPScheduleType::OrderedDistribute
6199 Constant *SchedulingType =
6200 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6201
6202 // Call the "init" function and update the trip count of the loop with the
6203 // value it produced.
6204 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6205 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6206 this](Value *SchedulingType, auto &Builder) {
6207 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6208 PLowerBound, PUpperBound});
6209 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6210 Value *PDistUpperBound =
6211 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6212 Args.push_back(PDistUpperBound);
6213 }
6214 Args.append({PStride, One, Zero});
6215 createRuntimeFunctionCall(StaticInit, Args);
6216 };
6217 BuildInitCall(SchedulingType, Builder);
6218 if (HasDistSchedule &&
6219 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6220 Constant *DistScheduleSchedType = ConstantInt::get(
6221 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6222 // We want to emit a second init function call for the dist_schedule clause
6223 // to the Distribute construct. This should only be done however if a
6224 // Workshare Loop is nested within a Distribute Construct
6225 BuildInitCall(DistScheduleSchedType, Builder);
6226 }
6227 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6228 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6229 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6230 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6231 if (CLI->VirtualCollapse) {
6232 bindCollapsedRange(CLI, LowerBound, TripCount);
6233#ifndef NDEBUG
6234 CLI->assertOK();
6235#endif
6236 } else {
6237 CLI->setTripCount(TripCount);
6238 }
6239
6240 // Update all uses of the induction variable except the one in the condition
6241 // block that compares it with the actual upper bound, and the increment in
6242 // the latch block.
6243
6244 if (!CLI->VirtualCollapse)
6245 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6246 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6247 Builder.SetCurrentDebugLocation(DL);
6248 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6249 /*HasNSW=*/Config.hasNoSignedWrap());
6250 });
6251
6252 // In the "exit" block, call the "fini" function.
6253 Builder.SetInsertPoint(CLI->getExit()->getTerminator()->getIterator());
6254 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6255
6256 // Add the barrier if requested.
6257 if (NeedsBarrier) {
6258 InsertPointOrErrorTy BarrierIP =
6260 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6261 /* CheckCancelFlag */ false);
6262 if (!BarrierIP)
6263 return BarrierIP.takeError();
6264 }
6265
6266 InsertPointTy AfterIP = CLI->getAfterIP();
6267 // TODO: Removing Virtual's outer bound requires another way to reject idle
6268 // assignments whose raw count wraps. Keep it for correctness and trip-count
6269 // information for analyses. The expected saving is small; investigate the
6270 // effects on downstream optimizations before removing it.
6271 CLI->invalidate();
6272
6273 return AfterIP;
6274}
6275
6276static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6277 LoopInfo &LI);
6278static void addLoopMetadata(CanonicalLoopInfo *Loop,
6280
6282 LLVMContext &Ctx, Loop *Loop,
6284 SmallVector<Metadata *> &LoopMDList) {
6285 SmallSet<BasicBlock *, 8> Reachable;
6286
6287 // Get the basic blocks from the loop in which memref instructions
6288 // can be found.
6289 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6290 // preferably without running any passes.
6291 for (BasicBlock *Block : Loop->getBlocks()) {
6292 if (Block == CLI->getCond() || Block == CLI->getHeader())
6293 continue;
6294 Reachable.insert(Block);
6295 }
6296
6297 // Add access group metadata to memory-access instructions.
6299 for (BasicBlock *BB : Reachable)
6301 // TODO: If the loop has existing parallel access metadata, have
6302 // to combine two lists.
6303 LoopMDList.push_back(MDNode::get(
6304 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6305}
6306
6308OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6309 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6310 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6311 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6312 assert(CLI->isValid() && "Requires a valid canonical loop");
6313 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6314
6315 LLVMContext &Ctx = CLI->getFunction()->getContext();
6316 Value *IV = CLI->getIndVar();
6317 Value *OrigTripCount = CLI->getTripCount();
6318 Type *IVTy = IV->getType();
6319 assert(IVTy->getIntegerBitWidth() <= 64 &&
6320 "Max supported tripcount bitwidth is 64 bits");
6321 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6322 : Type::getInt64Ty(Ctx);
6323 Type *I32Type = Type::getInt32Ty(M.getContext());
6324 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6325 Constant *One = ConstantInt::get(InternalIVTy, 1);
6326
6327 Function *F = CLI->getFunction();
6328 // Blocks must have terminators.
6329 // FIXME: Don't run analyses on incomplete/invalid IR.
6330 SmallVector<Instruction *> UIs;
6331 for (BasicBlock &BB : *F)
6332 if (!BB.hasTerminator())
6333 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6335 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6336 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6337 LoopAnalysis LIA;
6338 LoopInfo &&LI = LIA.run(*F, FAM);
6339 for (Instruction *I : UIs)
6340 I->eraseFromParent();
6341 Loop *L = LI.getLoopFor(CLI->getHeader());
6342 SmallVector<Metadata *> LoopMDList;
6343 if (ChunkSize || DistScheduleChunkSize)
6344 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6345 addLoopMetadata(CLI, LoopMDList);
6346
6347 // Declare useful OpenMP runtime functions.
6348 FunctionCallee StaticInit =
6349 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6350 FunctionCallee StaticFini =
6351 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6352
6353 // Allocate space for computed loop bounds as expected by the "init" function.
6354 Builder.restoreIP(AllocaIP);
6355 Builder.SetCurrentDebugLocation(DL);
6356 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6357 Value *PLowerBound =
6358 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6359 Value *PUpperBound =
6360 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6361 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6362 CLI->setLastIter(PLastIter);
6363
6364 // Set up the source location value for the OpenMP runtime.
6365 Builder.restoreIP(CLI->getPreheaderIP());
6366 Builder.SetCurrentDebugLocation(DL);
6367
6368 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6369 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6370 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6371 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6372 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6373 "distschedulechunksize");
6374 Value *CastedTripCount =
6375 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6376
6377 Constant *SchedulingType =
6378 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6379 Constant *DistSchedulingType =
6380 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6381 Builder.CreateStore(Zero, PLowerBound);
6382 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6383 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6384 Value *UpperBound =
6385 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6386 Builder.CreateStore(UpperBound, PUpperBound);
6387 Builder.CreateStore(One, PStride);
6388
6389 // Call the "init" function and update the trip count of the loop with the
6390 // value it produced.
6391 uint32_t SrcLocStrSize;
6392 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6393 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6394 if (DistScheduleSchedType != OMPScheduleType::None) {
6395 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6396 }
6397 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6398 Value *ThreadNum =
6399 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6400 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6401 PUpperBound, PStride, One,
6402 this](Value *SchedulingType, Value *ChunkSize,
6403 auto &Builder) {
6405 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6406 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6407 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6408 /*pstride=*/PStride, /*incr=*/One,
6409 /*chunk=*/ChunkSize});
6410 };
6411 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6412 if (DistScheduleSchedType != OMPScheduleType::None &&
6413 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6414 SchedType != OMPScheduleType::OrderedDistribute) {
6415 // We want to emit a second init function call for the dist_schedule clause
6416 // to the Distribute construct. This should only be done however if a
6417 // Workshare Loop is nested within a Distribute Construct
6418 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6419 }
6420
6421 // Load values written by the "init" function.
6422 Value *FirstChunkStart =
6423 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6424 Value *FirstChunkStop =
6425 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6426 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6427 Value *ChunkRange =
6428 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6429 Value *NextChunkStride =
6430 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6431
6432 // Create outer "dispatch" loop for enumerating the chunks.
6433 BasicBlock *DispatchEnter = splitBB(Builder, true);
6434 Value *DispatchCounter;
6435
6436 // It is safe to assume this didn't return an error because the callback
6437 // passed into createCanonicalLoop is the only possible error source, and it
6438 // always returns success.
6439 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6440 {Builder.saveIP(), DL},
6441 [&](InsertPointTy BodyIP, Value *Counter) {
6442 DispatchCounter = Counter;
6443 return Error::success();
6444 },
6445 FirstChunkStart, CastedTripCount, NextChunkStride,
6446 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6447 "dispatch"));
6448
6449 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6450 // not have to preserve the canonical invariant.
6451 BasicBlock *DispatchBody = DispatchCLI->getBody();
6452 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6453 BasicBlock *DispatchExit = DispatchCLI->getExit();
6454 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6455 DispatchCLI->invalidate();
6456
6457 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6458 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6459 redirectTo(CLI->getExit(), DispatchLatch, DL);
6460 redirectTo(DispatchBody, DispatchEnter, DL);
6461
6462 // Prepare the prolog of the chunk loop.
6463 Builder.restoreIP(CLI->getPreheaderIP());
6464 Builder.SetCurrentDebugLocation(DL);
6465
6466 // Compute the number of iterations of the chunk loop.
6467 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6468 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6469 Value *IsLastChunk =
6470 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6471 Value *CountUntilOrigTripCount =
6472 Builder.CreateSub(CastedTripCount, DispatchCounter);
6473 Value *ChunkTripCount = Builder.CreateSelect(
6474 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6475 Value *BackcastedChunkTC =
6476 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6477 CLI->setTripCount(BackcastedChunkTC);
6478
6479 // Update all uses of the induction variable except the one in the condition
6480 // block that compares it with the actual upper bound, and the increment in
6481 // the latch block.
6482 Value *BackcastedDispatchCounter =
6483 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6484 CLI->mapIndVar([&](Instruction *) -> Value * {
6485 Builder.restoreIP(CLI->getBodyIP());
6486 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6487 });
6488
6489 // In the "exit" block, call the "fini" function.
6490 Builder.SetInsertPoint(DispatchExit->getFirstInsertionPt());
6491 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6492
6493 // Add the barrier if requested.
6494 if (NeedsBarrier) {
6495 InsertPointOrErrorTy AfterIP =
6496 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6497 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6498 if (!AfterIP)
6499 return AfterIP.takeError();
6500 }
6501
6502#ifndef NDEBUG
6503 // Even though we currently do not support applying additional methods to it,
6504 // the chunk loop should remain a canonical loop.
6505 CLI->assertOK();
6506#endif
6507
6508 return DispatchAfter->getFirstInsertionPt();
6509}
6510
6511// Returns an LLVM function to call for executing an OpenMP static worksharing
6512// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6513// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6514static FunctionCallee
6516 WorksharingLoopType LoopType) {
6517 unsigned Bitwidth = Ty->getIntegerBitWidth();
6518 Module &M = OMPBuilder->M;
6519 switch (LoopType) {
6520 case WorksharingLoopType::ForStaticLoop:
6521 if (Bitwidth == 32)
6522 return OMPBuilder->getOrCreateRuntimeFunction(
6523 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6524 if (Bitwidth == 64)
6525 return OMPBuilder->getOrCreateRuntimeFunction(
6526 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6527 break;
6528 case WorksharingLoopType::DistributeStaticLoop:
6529 if (Bitwidth == 32)
6530 return OMPBuilder->getOrCreateRuntimeFunction(
6531 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6532 if (Bitwidth == 64)
6533 return OMPBuilder->getOrCreateRuntimeFunction(
6534 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6535 break;
6536 case WorksharingLoopType::DistributeForStaticLoop:
6537 if (Bitwidth == 32)
6538 return OMPBuilder->getOrCreateRuntimeFunction(
6539 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6540 if (Bitwidth == 64)
6541 return OMPBuilder->getOrCreateRuntimeFunction(
6542 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6543 break;
6544 }
6545 if (Bitwidth != 32 && Bitwidth != 64) {
6546 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6547 }
6548 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6549}
6550
6551// Inserts a call to proper OpenMP Device RTL function which handles
6552// loop worksharing.
6554 WorksharingLoopType LoopType,
6555 BasicBlock *InsertBlock, Value *Ident,
6556 Value *LoopBodyArg, Value *TripCount,
6557 Function &LoopBodyFn, bool NoLoop) {
6558 Type *TripCountTy = TripCount->getType();
6559 Module &M = OMPBuilder->M;
6560 IRBuilder<> &Builder = OMPBuilder->Builder;
6561 FunctionCallee RTLFn =
6562 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6563 SmallVector<Value *, 8> RealArgs;
6564 RealArgs.push_back(Ident);
6565 RealArgs.push_back(&LoopBodyFn);
6566 RealArgs.push_back(LoopBodyArg);
6567 RealArgs.push_back(TripCount);
6568 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6569 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6570 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6571 Builder.restoreIP(std::prev(InsertBlock->end()));
6572 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6573 return;
6574 }
6575 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6576 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6577 Builder.restoreIP(std::prev(InsertBlock->end()));
6578 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6579
6580 RealArgs.push_back(
6581 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6582 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6583 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6584 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6585 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6586 } else {
6587 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6588 }
6589
6590 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6591}
6592
6594 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6595 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6596 WorksharingLoopType LoopType, bool NoLoop) {
6597 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6598 BasicBlock *Preheader = CLI->getPreheader();
6599 Value *TripCount = CLI->getTripCount();
6600
6601 // After loop body outling, the loop body contains only set up
6602 // of loop body argument structure and the call to the outlined
6603 // loop body function. Firstly, we need to move setup of loop body args
6604 // into loop preheader.
6605 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6606 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6607
6608 // The next step is to remove the whole loop. We do not it need anymore.
6609 // That's why make an unconditional branch from loop preheader to loop
6610 // exit block
6611 Builder.restoreIP(Preheader->end());
6612 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6613 Preheader->getTerminator()->eraseFromParent();
6614 Builder.CreateBr(CLI->getExit());
6615
6616 // Delete dead loop blocks
6617 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6618 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6619 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6620 CleanUpInfo.EntryBB = CLI->getHeader();
6621 CleanUpInfo.ExitBB = CLI->getExit();
6622 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6623 DeleteDeadBlocks(BlocksToBeRemoved);
6624
6625 // Find the instruction which corresponds to loop body argument structure
6626 // and remove the call to loop body function instruction.
6627 Value *LoopBodyArg;
6628 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6629 assert(OutlinedFnUser &&
6630 "Expected unique undroppable user of outlined function");
6631 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6632 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6633 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6634 "Expected outlined function call to be located in loop preheader");
6635 // Check in case no argument structure has been passed.
6636 if (OutlinedFnCallInstruction->arg_size() > 1)
6637 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6638 else
6639 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6640 OutlinedFnCallInstruction->eraseFromParent();
6641
6642 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6643 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6644
6645 for (auto &ToBeDeletedItem : ToBeDeleted)
6646 ToBeDeletedItem->eraseFromParent();
6647 CLI->invalidate();
6648}
6649
6650OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6651 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6652 WorksharingLoopType LoopType, bool NeedsBarrier, bool NoLoop,
6653 bool NeedsLastIter) {
6654 uint32_t SrcLocStrSize;
6655 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6656
6657 // Mirrors host runtime reporting of last iteration by in-body computation.
6658 if (NeedsLastIter) {
6659 Type *I32Type = Type::getInt32Ty(M.getContext());
6660 Builder.restoreIP(AllocaIP);
6661 AllocaInst *PLastIter =
6662 Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6663 CLI->setLastIter(PLastIter);
6664
6665 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6666 Builder.CreateStore(ConstantInt::get(I32Type, 0), PLastIter);
6667
6668 Builder.SetInsertPoint(CLI->getBody()->getFirstInsertionPt());
6669 Value *TripCount = CLI->getTripCount();
6670 Value *LastIter =
6671 Builder.CreateSub(TripCount, ConstantInt::get(TripCount->getType(), 1));
6672 Value *IsLast =
6673 Builder.CreateICmpEQ(CLI->getIndVar(), LastIter, "omp.is_last_iter");
6674 Builder.CreateStore(Builder.CreateZExt(IsLast, I32Type), PLastIter);
6675 }
6676
6678 switch (LoopType) {
6679 case WorksharingLoopType::ForStaticLoop:
6680 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6681 break;
6682 case WorksharingLoopType::DistributeStaticLoop:
6683 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6684 break;
6685 case WorksharingLoopType::DistributeForStaticLoop:
6686 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6687 break;
6688 }
6689 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6690
6691 auto OI = std::make_unique<OutlineInfo>();
6692 OI->OuterAllocBB = CLI->getPreheader();
6693 Function *OuterFn = CLI->getPreheader()->getParent();
6694
6695 // Instructions which need to be deleted at the end of code generation
6696 SmallVector<Instruction *, 4> ToBeDeleted;
6697
6698 OI->OuterAllocBB = AllocaIP.getNodeParent();
6699
6700 // Mark the body loop as region which needs to be extracted
6701 OI->EntryBB = CLI->getBody();
6702 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6703 "omp.prelatch");
6704
6705 // Prepare loop body for extraction
6706 Builder.restoreIP(CLI->getPreheader()->begin());
6707
6708 // Insert new loop counter variable which will be used only in loop
6709 // body.
6710 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6711 Instruction *NewLoopCntLoad =
6712 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6713 // New loop counter instructions are redundant in the loop preheader when
6714 // code generation for workshare loop is finshed. That's why mark them as
6715 // ready for deletion.
6716 ToBeDeleted.push_back(NewLoopCntLoad);
6717 ToBeDeleted.push_back(NewLoopCnt);
6718
6719 // Analyse loop body region. Find all input variables which are used inside
6720 // loop body region.
6721 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6723 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6724
6725 CodeExtractorAnalysisCache CEAC(*OuterFn);
6726 CodeExtractor Extractor(Blocks,
6727 /* DominatorTree */ nullptr,
6728 /* AggregateArgs */ true,
6729 /* BlockFrequencyInfo */ nullptr,
6730 /* BranchProbabilityInfo */ nullptr,
6731 /* AssumptionCache */ nullptr,
6732 /* AllowVarArgs */ true,
6733 /* AllowAlloca */ true,
6734 /* AllocationBlock */ CLI->getPreheader(),
6735 /* DeallocationBlocks */ {},
6736 /* Suffix */ ".omp_wsloop",
6737 /* AggrArgsIn0AddrSpace */ true);
6738
6739 BasicBlock *CommonExit = nullptr;
6740 SetVector<Value *> SinkingCands, HoistingCands;
6741
6742 // Find allocas outside the loop body region which are used inside loop
6743 // body
6744 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6745
6746 // We need to model loop body region as the function f(cnt, loop_arg).
6747 // That's why we replace loop induction variable by the new counter
6748 // which will be one of loop body function argument
6750 CLI->getIndVar()->user_end());
6751 for (auto Use : Users) {
6752 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6753 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6754 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6755 }
6756 }
6757 }
6758 // Make sure that loop counter variable is not merged into loop body
6759 // function argument structure and it is passed as separate variable
6760 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6761
6762 // PostOutline CB is invoked when loop body function is outlined and
6763 // loop body is replaced by call to outlined function. We need to add
6764 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6765 // function will handle loop control logic.
6766 //
6767 OI->PostOutlineCB = [=, ToBeDeletedVec =
6768 std::move(ToBeDeleted)](Function &OutlinedFn) {
6769 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6770 LoopType, NoLoop);
6771 };
6772 addOutlineInfo(std::move(OI));
6773
6774 // Keep the barrier outside the outlined loop body so that every thread
6775 // encounters it, including threads that execute no iterations.
6776 if (NeedsBarrier) {
6777 Builder.SetInsertPoint(CLI->getExit()->getTerminator());
6778 // Standalone distribute loops never request a barrier. For both regular
6779 // worksharing loops and combined distribute/for loops, the barrier is
6780 // associated with the worksharing loop, hence OMPD_for.
6781 InsertPointOrErrorTy BarrierIP =
6782 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6783 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6784 if (!BarrierIP)
6785 return BarrierIP.takeError();
6786 }
6787 return CLI->getAfterIP();
6788}
6789
6792 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6793 bool HasSimdModifier, bool HasMonotonicModifier,
6794 bool HasNonmonotonicModifier, bool HasOrderedClause,
6795 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6796 Value *DistScheduleChunkSize, bool NeedsLastIter) {
6797 if (CLI->VirtualCollapse &&
6798 (Config.isTargetDevice() ||
6799 (SchedKind != omp::OMP_SCHEDULE_Static &&
6800 SchedKind != omp::OMP_SCHEDULE_Default) ||
6801 ChunkSize || HasOrderedClause || HasDistSchedule ||
6802 DistScheduleChunkSize || LoopType != WorksharingLoopType::ForStaticLoop))
6804 "Virtual collapse requires unchunked static worksharing");
6805 if (Config.isTargetDevice())
6806 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6807 NoLoop, NeedsLastIter);
6808 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6809 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6810 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6811
6812 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6813 OMPScheduleType::ModifierOrdered;
6814 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6815 if (HasDistSchedule) {
6816 DistScheduleSchedType = DistScheduleChunkSize
6817 ? OMPScheduleType::OrderedDistributeChunked
6818 : OMPScheduleType::OrderedDistribute;
6819 }
6820 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6821 case OMPScheduleType::BaseStatic:
6822 case OMPScheduleType::BaseDistribute:
6823 assert((!ChunkSize || !DistScheduleChunkSize) &&
6824 "No chunk size with static-chunked schedule");
6825 if (IsOrdered && !HasDistSchedule)
6826 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6827 NeedsBarrier, ChunkSize);
6828 // FIXME: Monotonicity ignored?
6829 if (DistScheduleChunkSize)
6830 return applyStaticChunkedWorkshareLoop(
6831 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6832 DistScheduleChunkSize, DistScheduleSchedType);
6833 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6834 HasDistSchedule);
6835
6836 case OMPScheduleType::BaseStaticChunked:
6837 case OMPScheduleType::BaseDistributeChunked:
6838 if (IsOrdered && !HasDistSchedule)
6839 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6840 NeedsBarrier, ChunkSize);
6841 // FIXME: Monotonicity ignored?
6842 return applyStaticChunkedWorkshareLoop(
6843 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6844 DistScheduleChunkSize, DistScheduleSchedType);
6845
6846 case OMPScheduleType::BaseRuntime:
6847 case OMPScheduleType::BaseAuto:
6848 case OMPScheduleType::BaseGreedy:
6849 case OMPScheduleType::BaseBalanced:
6850 case OMPScheduleType::BaseSteal:
6851 case OMPScheduleType::BaseRuntimeSimd:
6852 assert(!ChunkSize &&
6853 "schedule type does not support user-defined chunk sizes");
6854 [[fallthrough]];
6855 case OMPScheduleType::BaseGuidedSimd:
6856 case OMPScheduleType::BaseDynamicChunked:
6857 case OMPScheduleType::BaseGuidedChunked:
6858 case OMPScheduleType::BaseGuidedIterativeChunked:
6859 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6860 case OMPScheduleType::BaseStaticBalancedChunked:
6861 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6862 NeedsBarrier, ChunkSize);
6863
6864 default:
6865 llvm_unreachable("Unknown/unimplemented schedule kind");
6866 }
6867}
6868
6869/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6870/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6871/// the runtime. Always interpret integers as unsigned similarly to
6872/// CanonicalLoopInfo.
6873static FunctionCallee
6875 unsigned Bitwidth = Ty->getIntegerBitWidth();
6876 if (Bitwidth == 32)
6877 return OMPBuilder.getOrCreateRuntimeFunction(
6878 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6879 if (Bitwidth == 64)
6880 return OMPBuilder.getOrCreateRuntimeFunction(
6881 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6882 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6883}
6884
6885/// Returns an LLVM function to call for updating the next loop using OpenMP
6886/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6887/// the runtime. Always interpret integers as unsigned similarly to
6888/// CanonicalLoopInfo.
6889static FunctionCallee
6891 unsigned Bitwidth = Ty->getIntegerBitWidth();
6892 if (Bitwidth == 32)
6893 return OMPBuilder.getOrCreateRuntimeFunction(
6894 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6895 if (Bitwidth == 64)
6896 return OMPBuilder.getOrCreateRuntimeFunction(
6897 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6898 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6899}
6900
6901/// Returns an LLVM function to call for finalizing the dynamic loop using
6902/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6903/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6904static FunctionCallee
6906 unsigned Bitwidth = Ty->getIntegerBitWidth();
6907 if (Bitwidth == 32)
6908 return OMPBuilder.getOrCreateRuntimeFunction(
6909 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6910 if (Bitwidth == 64)
6911 return OMPBuilder.getOrCreateRuntimeFunction(
6912 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6913 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6914}
6915
6917OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6918 InsertPointTy AllocaIP,
6919 OMPScheduleType SchedType,
6920 bool NeedsBarrier, Value *Chunk) {
6921 assert(CLI->isValid() && "Requires a valid canonical loop");
6922 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6923 "Require dedicated allocate IP");
6925 "Require valid schedule type");
6926
6927 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6928 OMPScheduleType::ModifierOrdered;
6929
6930 // Set up the source location value for OpenMP runtime.
6931 Builder.SetCurrentDebugLocation(DL);
6932
6933 uint32_t SrcLocStrSize;
6934 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6935 Value *SrcLoc =
6936 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6937
6938 // Declare useful OpenMP runtime functions.
6939 Value *IV = CLI->getIndVar();
6940 Type *IVTy = IV->getType();
6941 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6942 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6943
6944 // Allocate space for computed loop bounds as expected by the "init" function.
6945 Builder.SetInsertPoint(AllocaIP.getNodeParent()->getFirstNonPHIOrAlloca());
6946 Type *I32Type = Type::getInt32Ty(M.getContext());
6947 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6948 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6949 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6950 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6951 CLI->setLastIter(PLastIter);
6952
6953 // At the end of the preheader, prepare for calling the "init" function by
6954 // storing the current loop bounds into the allocated space. A canonical loop
6955 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6956 // and produces an inclusive upper bound.
6957 BasicBlock *PreHeader = CLI->getPreheader();
6958 Builder.SetInsertPoint(PreHeader->getTerminator());
6959 Constant *One = ConstantInt::get(IVTy, 1);
6960 Builder.CreateStore(One, PLowerBound);
6961 Value *UpperBound = CLI->getTripCount();
6962 Builder.CreateStore(UpperBound, PUpperBound);
6963 Builder.CreateStore(One, PStride);
6964
6965 BasicBlock *Header = CLI->getHeader();
6966 BasicBlock *Exit = CLI->getExit();
6967 BasicBlock *Cond = CLI->getCond();
6968 BasicBlock *Latch = CLI->getLatch();
6969 InsertPointTy AfterIP = CLI->getAfterIP();
6970
6971 // The CLI will be "broken" in the code below, as the loop is no longer
6972 // a valid canonical loop.
6973
6974 if (!Chunk)
6975 Chunk = One;
6976
6977 Value *ThreadNum =
6978 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6979
6980 Constant *SchedulingType =
6981 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6982
6983 // Call the "init" function.
6984 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6985 /* LowerBound */ One, UpperBound,
6986 /* step */ One, Chunk});
6987
6988 // An outer loop around the existing one.
6989 BasicBlock *OuterCond = BasicBlock::Create(
6990 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6991 PreHeader->getParent());
6992 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6993 Builder.SetInsertPoint(OuterCond->getFirstInsertionPt());
6995 DynamicNext,
6996 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6997 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6998 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6999 Value *LowerBound =
7000 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
7001 Builder.CreateCondBr(MoreWork, Header, Exit);
7002
7003 // Change PHI-node in loop header to use outer cond rather than preheader,
7004 // and set IV to the LowerBound.
7005 Instruction *Phi = &Header->front();
7006 auto *PI = cast<PHINode>(Phi);
7007 PI->setIncomingBlock(0, OuterCond);
7008 PI->setIncomingValue(0, LowerBound);
7009
7010 // Then set the pre-header to jump to the OuterCond
7011 Instruction *Term = PreHeader->getTerminator();
7012 auto *Br = cast<UncondBrInst>(Term);
7013 Br->setSuccessor(OuterCond);
7014
7015 // Modify the inner condition:
7016 // * Use the UpperBound returned from the DynamicNext call.
7017 // * jump to the loop outer loop when done with one of the inner loops.
7018 Builder.SetInsertPoint(Cond->getFirstInsertionPt());
7019 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
7020 Instruction *Comp = &*Builder.GetInsertPoint();
7021 auto *CI = cast<CmpInst>(Comp);
7022 CI->setOperand(1, UpperBound);
7023 // Redirect the inner exit to branch to outer condition.
7024 Instruction *Branch = &Cond->back();
7025 auto *BI = cast<CondBrInst>(Branch);
7026 assert(BI->getSuccessor(1) == Exit);
7027 BI->setSuccessor(1, OuterCond);
7028
7029 // Call the "fini" function if "ordered" is present in wsloop directive.
7030 if (Ordered) {
7031 Builder.SetInsertPoint(&Latch->back());
7032 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
7033 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
7034 }
7035
7036 // Add the barrier if requested.
7037 if (NeedsBarrier) {
7038 Builder.SetInsertPoint(&Exit->back());
7039 InsertPointOrErrorTy BarrierIP =
7041 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
7042 /* CheckCancelFlag */ false);
7043 if (!BarrierIP)
7044 return BarrierIP.takeError();
7045 }
7046
7047 CLI->invalidate();
7048 return AfterIP;
7049}
7050
7051/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
7052/// after this \p OldTarget will be orphaned.
7054 BasicBlock *NewTarget, DebugLoc DL) {
7055 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
7056 redirectTo(Pred, NewTarget, DL);
7057}
7058
7060 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
7061 // We add a block to BBsToKeep iff we have proven it has an external use.
7063
7064 while (true) {
7065 bool Changed = false;
7066
7067 for (BasicBlock *BB : BBs) {
7068 if (BBsToKeep.contains(BB))
7069 continue;
7070
7071 for (Use &U : BB->uses()) {
7072 auto *UseInst = dyn_cast<Instruction>(U.getUser());
7073 if (!UseInst)
7074 continue;
7075 BasicBlock *UseBB = UseInst->getParent();
7076 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
7077 BBsToKeep.insert(BB);
7078 Changed = true;
7079 break;
7080 }
7081 }
7082 }
7083
7084 if (!Changed)
7085 break;
7086 }
7087
7089 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
7090 DeleteDeadBlocks(BBsToDelete);
7091}
7092
7093/// Physical handles captured before the source descriptors are invalidated.
7100
7102OpenMPIRBuilder::planVirtualRowLoop(ArrayRef<CanonicalLoopInfo *> Loops) {
7103 // The executor inserts a guard before the outer latch. An indirect jump
7104 // by block address could bypass that guard.
7105 if (isa<IndirectBrInst>(Loops.back()->getAfter()->getTerminator()))
7107 "Virtual collapse requires unobserved canonical loop controls");
7108 // Save the original blocks and IVs before the executor changes the CFG.
7109 VirtualRowPlan Plan;
7110 Plan.Latches.push_back(Loops.front()->getLatch());
7111 for (CanonicalLoopInfo *Loop : Loops) {
7112 Plan.IndVars.push_back(cast<PHINode>(Loop->getIndVar()));
7113 Plan.Preheaders.push_back(Loop->getPreheader());
7114 }
7115 // The inner loop must return directly to the latch where the guard is added.
7116 if (Loops.front()->getBody()->getUniqueSuccessor() !=
7117 Plan.Preheaders.back() ||
7118 Loops.back()->getAfter()->getSingleSuccessor() != Plan.Latches.front())
7119 reportFatalInternalError("Virtual collapse requires direct nesting");
7120 Plan.InnerCmp = cast<CmpInst>(&Loops.back()->getCond()->front());
7121 return Plan;
7122}
7123
7124void OpenMPIRBuilder::createVirtualRowExecutor(CanonicalLoopInfo *Result,
7125 const VirtualRowPlan &Plan,
7126 ArrayRef<Value *> TripCounts,
7127 Value *Begin, Value *Count,
7128 BasicBlock *Entry) {
7129 DebugLoc DL = Builder.getCurrentDebugLocation();
7130 BasicBlock *OuterHeader = Plan.IndVars.front()->getParent();
7131 Type *IVType = Begin->getType();
7132 Constant *Zero = ConstantInt::get(IVType, 0);
7133 // A row is one invocation of the inner loop. Decode the first logical
7134 // iteration once, before entering the original loop nest.
7135 Builder.SetInsertPoint(Entry);
7136 Value *InnerBegin =
7137 Builder.CreateURem(Begin, TripCounts.back(), "omp_collapsed.begin.1");
7138 Value *OuterBegin =
7139 Builder.CreateUDiv(Begin, TripCounts.back(), "omp_collapsed.begin.0");
7140 // Include the skipped prefix of the first row so each completed row
7141 // removes one full inner trip count from Remaining.
7142 Value *InitialRemaining =
7143 Builder.CreateAdd(Count, InnerBegin, "omp_collapsed.row.remaining");
7144 Builder.CreateBr(OuterHeader);
7145
7146 Builder.SetInsertPoint(OuterHeader->getFirstNonPHIIt());
7147 Builder.SetCurrentDebugLocation(DL);
7148 PHINode *Remaining =
7149 Builder.CreatePHI(IVType, 2, "omp_collapsed.row.remaining.0");
7150 Remaining->addIncoming(InitialRemaining, Entry);
7151 // Only the first row starts at InnerBegin; subsequent rows start at zero.
7152 // For an inner trip count of one, the existing zero start is sufficient.
7153 if (!cast<ConstantInt>(TripCounts.back())->isOne()) {
7154 PHINode *Start =
7155 Builder.CreatePHI(IVType, 2, "omp_collapsed.row.start.1.0");
7156 Start->addIncoming(InnerBegin, Entry);
7157 Start->addIncoming(Zero, Plan.Latches.front());
7158 Plan.IndVars.back()->setIncomingValue(0, Start);
7159 }
7160 // Saved inner-preheader insertions must not change the IV PHI's entry edge.
7161 // Keep the original branch as their anchor and give it a PHI-free successor.
7162 auto *InnerBr = cast<UncondBrInst>(Plan.Preheaders.back()->getTerminator());
7163 BasicBlock *InnerHeader = Plan.IndVars.back()->getParent();
7164 BasicBlock *InnerEntry =
7165 BasicBlock::Create(M.getContext(), "omp_collapsed.row.preheader",
7166 InnerHeader->getParent(), InnerHeader);
7167 Builder.SetInsertPoint(InnerEntry);
7168 Builder.SetCurrentDebugLocation(DL);
7169 Builder.CreateBr(InnerHeader);
7170 InnerBr->setSuccessor(InnerEntry);
7171 Plan.IndVars.back()->setIncomingBlock(0, InnerEntry);
7172 // The final row ends at Remaining; earlier rows keep their original bound.
7173 Builder.SetInsertPoint(Plan.Preheaders.back()->getTerminator());
7174 Builder.SetCurrentDebugLocation(DL);
7175 Value *IsFinalRow = Builder.CreateICmpULE(Remaining, TripCounts.back(),
7176 "omp_collapsed.row.is_final");
7177 Value *InnerStop = Builder.CreateSelect(
7178 IsFinalRow, Remaining, TripCounts.back(), "omp_collapsed.row.stop");
7179 Plan.InnerCmp->setOperand(1, InnerStop);
7180
7181 // Only non-final rows reach the outer latch, so Remaining is greater than
7182 // the inner trip count and this subtraction cannot underflow.
7183 BasicBlock *Latch = Plan.Latches.front();
7184 Builder.SetInsertPoint(Latch->getTerminator());
7185 Builder.SetCurrentDebugLocation(DL);
7186 Value *RemainingNext = Builder.CreateSub(Remaining, TripCounts.back(),
7187 "omp_collapsed.row.remaining.next",
7188 /*HasNUW=*/true);
7189 Remaining->addIncoming(RemainingNext, Latch);
7190 // Check completion after the inner loop and its continuation, before
7191 // incrementing the outer IV. The final row exits the whole nest.
7192 BasicBlock *Guard =
7193 Latch->splitBasicBlockBefore(Latch->begin(), "omp_collapsed.row.done");
7194 Guard->getTerminator()->eraseFromParent();
7195 Builder.SetInsertPoint(Guard);
7196 Builder.CreateCondBr(IsFinalRow, Result->getExit(), Latch);
7197 Plan.IndVars.front()->setIncomingValue(0, OuterBegin);
7198 Plan.IndVars.front()->setIncomingBlock(0, Entry);
7199}
7200
7201CanonicalLoopInfo *OpenMPIRBuilder::createVirtualCollapsedLoop(
7203 const VirtualRowPlan &Plan, ArrayRef<Value *> TripCounts,
7204 Value *CollapsedTripCount) {
7205 // Reuse the outer loop's control blocks; the physical nest stays in place.
7206 LoopInfos.emplace_front();
7207 CanonicalLoopInfo *Result = &LoopInfos.front();
7208 Result->Header = Loops.front()->getHeader();
7209 Result->Cond = Loops.front()->getCond();
7210 Result->Latch = Loops.front()->getLatch();
7211 Result->Exit = Loops.front()->getExit();
7212 BasicBlock *Preheader = Plan.Preheaders.front();
7213 Function *F = Result->getFunction();
7214 Type *IVType = CollapsedTripCount->getType();
7215 Constant *Zero = ConstantInt::get(IVType, 0);
7216 // Keep initialization separate from decoding so an empty range skips both
7217 // division and the loop nest.
7218 BasicBlock *Init = BasicBlock::Create(M.getContext(), "omp_collapsed.init", F,
7219 Result->getHeader());
7220 BasicBlock *Entry = BasicBlock::Create(M.getContext(), "omp_collapsed.decode",
7221 F, Result->getHeader());
7222 Builder.SetCurrentDebugLocation(DL);
7223 Builder.SetInsertPoint(Init);
7224 // Keep explicit range copies until scheduling replaces operand zero. Create
7225 // the instructions directly so the initial constants are not folded away.
7226 auto *Begin = Builder.Insert(BinaryOperator::CreateAdd(Zero, Zero),
7227 "omp_collapsed.range.begin");
7228 auto *Count =
7229 Builder.Insert(BinaryOperator::CreateAdd(CollapsedTripCount, Zero),
7230 "omp_collapsed.range.count");
7231 Builder.CreateCondBr(Builder.CreateICmpNE(Count, Zero), Entry,
7232 Result->getExit());
7233 Result->VirtualCollapse =
7234 CanonicalLoopInfo::VirtualCollapseInfo{Begin, Count};
7235 // Route the preheader through Init. The executor reconnects the outer IV
7236 // PHI to the decode block.
7237 cast<UncondBrInst>(Preheader->getTerminator())->setSuccessor(Init);
7238 createVirtualRowExecutor(Result, Plan, TripCounts, Begin, Count, Entry);
7239 // The transformed nest is now represented by Result, not the input loops.
7240 for (CanonicalLoopInfo *Loop : Loops)
7241 Loop->invalidate();
7242#ifndef NDEBUG
7243 Result->assertOK();
7244#endif
7245 return Result;
7246}
7247
7248void OpenMPIRBuilder::bindCollapsedRange(CanonicalLoopInfo *Loop, Value *Begin,
7249 Value *Count) {
7250 // Update only the copied values; the range has no PHI predecessor to rebind.
7251 Loop->VirtualCollapse->Begin->setOperand(0, Begin);
7252 Loop->VirtualCollapse->Count->setOperand(0, Count);
7253}
7254
7255CanonicalLoopInfo *
7257 InsertPointTy ComputeIP) {
7258 assert(Loops.size() >= 1 && "At least one loop required");
7259 size_t NumLoops = Loops.size();
7260
7261 // Nothing to do if there is already just one loop.
7262 if (NumLoops == 1)
7263 return Loops.front();
7264
7265 if (Config.CollapseMapping == CollapseIVMapping::Virtual) {
7266 if (Loops.size() != 2)
7268 "Virtual collapse requires two constant-count loops");
7269 SmallVector<Value *, 4> TripCounts;
7270 for (CanonicalLoopInfo *Loop : Loops) {
7271 if (Loop->VirtualCollapse)
7272 Loop->reportUnsupportedVirtualCollapse();
7273 Value *TC = Loop->getTripCount();
7274 if (!isa<ConstantInt>(TC))
7276 "Virtual collapse requires two constant-count loops");
7277 TripCounts.push_back(TC);
7278 }
7279 VirtualRowPlan Plan = planVirtualRowLoop(Loops);
7280 // Constant counts fold the product without emitting instructions.
7281 Value *CollapsedTripCount =
7282 Builder.CreateNUWMul(TripCounts.front(), TripCounts.back());
7283 return createVirtualCollapsedLoop(DL, Loops, Plan, TripCounts,
7284 CollapsedTripCount);
7285 }
7286
7287 CanonicalLoopInfo *Outermost = Loops.front();
7288 CanonicalLoopInfo *Innermost = Loops.back();
7289 BasicBlock *OrigPreheader = Outermost->getPreheader();
7290 BasicBlock *OrigAfter = Outermost->getAfter();
7291 Function *F = OrigPreheader->getParent();
7292
7293 // Loop control blocks that may become orphaned later.
7294 SmallVector<BasicBlock *, 12> OldControlBBs;
7295 OldControlBBs.reserve(6 * Loops.size());
7297 Loop->collectControlBlocks(OldControlBBs);
7298
7299 // Setup the IRBuilder for inserting the trip count computation.
7300 Builder.SetCurrentDebugLocation(DL);
7301 if (ComputeIP.isValid())
7302 Builder.restoreIP(ComputeIP);
7303 else
7304 Builder.restoreIP(Outermost->getPreheaderIP());
7305
7306 // Derive the collapsed' loop trip count.
7307 // TODO: Find common/largest indvar type.
7308 Value *CollapsedTripCount = nullptr;
7309 for (CanonicalLoopInfo *L : Loops) {
7310 assert(L->isValid() &&
7311 "All loops to collapse must be valid canonical loops");
7312 Value *OrigTripCount = L->getTripCount();
7313 if (!CollapsedTripCount) {
7314 CollapsedTripCount = OrigTripCount;
7315 continue;
7316 }
7317
7318 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7319 CollapsedTripCount =
7320 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7321 }
7322
7323 // Create the collapsed loop control flow.
7324 CanonicalLoopInfo *Result =
7325 createLoopSkeleton(DL, CollapsedTripCount, F,
7326 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7327 /*IsCollapsed=*/true);
7328
7329 // Build the collapsed loop body code.
7330 // Start with deriving the input loop induction variables from the collapsed
7331 // one, using a divmod scheme. To preserve the original loops' order, the
7332 // innermost loop use the least significant bits.
7333 Builder.restoreIP(Result->getBodyIP());
7334
7335 Value *Leftover = Result->getIndVar();
7336 SmallVector<Value *> NewIndVars;
7337 NewIndVars.resize(NumLoops);
7338 for (int i = NumLoops - 1; i >= 1; --i) {
7339 Value *OrigTripCount = Loops[i]->getTripCount();
7340
7341 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7342 NewIndVars[i] = NewIndVar;
7343
7344 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7345 }
7346 // Outermost loop gets all the remaining bits.
7347 NewIndVars[0] = Leftover;
7348
7349 // Construct the loop body control flow.
7350 // We progressively construct the branch structure following in direction of
7351 // the control flow, from the leading in-between code, the loop nest body, the
7352 // trailing in-between code, and rejoining the collapsed loop's latch.
7353 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7354 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7355 // its predecessors as sources.
7356 BasicBlock *ContinueBlock = Result->getBody();
7357 BasicBlock *ContinuePred = nullptr;
7358 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7359 BasicBlock *NextSrc) {
7360 if (ContinueBlock)
7361 redirectTo(ContinueBlock, Dest, DL);
7362 else
7363 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7364
7365 ContinueBlock = nullptr;
7366 ContinuePred = NextSrc;
7367 };
7368
7369 // The code before the nested loop of each level.
7370 // Because we are sinking it into the nest, it will be executed more often
7371 // that the original loop. More sophisticated schemes could keep track of what
7372 // the in-between code is and instantiate it only once per thread.
7373 for (size_t i = 0; i < NumLoops - 1; ++i)
7374 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7375
7376 // Connect the loop nest body.
7377 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7378
7379 // The code after the nested loop at each level.
7380 for (size_t i = NumLoops - 1; i > 0; --i)
7381 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7382
7383 // Connect the finished loop to the collapsed loop latch.
7384 ContinueWith(Result->getLatch(), nullptr);
7385
7386 // Replace the input loops with the new collapsed loop.
7387 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7388 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7389
7390 // Replace the input loop indvars with the derived ones.
7391 for (size_t i = 0; i < NumLoops; ++i)
7392 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7393
7394 // Remove unused parts of the input loops.
7395 removeUnusedBlocksFromParent(OldControlBBs);
7396
7397 for (CanonicalLoopInfo *L : Loops)
7398 L->invalidate();
7399
7400#ifndef NDEBUG
7401 Result->assertOK();
7402#endif
7403 return Result;
7404}
7405
7406std::vector<CanonicalLoopInfo *>
7408 ArrayRef<Value *> TileSizes) {
7409 assert(TileSizes.size() == Loops.size() &&
7410 "Must pass as many tile sizes as there are loops");
7411 int NumLoops = Loops.size();
7412 assert(NumLoops >= 1 && "At least one loop to tile required");
7413
7414 CanonicalLoopInfo *OutermostLoop = Loops.front();
7415 CanonicalLoopInfo *InnermostLoop = Loops.back();
7416 Function *F = OutermostLoop->getBody()->getParent();
7417 BasicBlock *InnerEnter = InnermostLoop->getBody();
7418 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7419
7420 // Loop control blocks that may become orphaned later.
7421 SmallVector<BasicBlock *, 12> OldControlBBs;
7422 OldControlBBs.reserve(6 * Loops.size());
7424 Loop->collectControlBlocks(OldControlBBs);
7425
7426 // Collect original trip counts and induction variable to be accessible by
7427 // index. Also, the structure of the original loops is not preserved during
7428 // the construction of the tiled loops, so do it before we scavenge the BBs of
7429 // any original CanonicalLoopInfo.
7430 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7431 for (CanonicalLoopInfo *L : Loops) {
7432 assert(L->isValid() && "All input loops must be valid canonical loops");
7433 OrigTripCounts.push_back(L->getTripCount());
7434 OrigIndVars.push_back(L->getIndVar());
7435 }
7436
7437 // Collect the code between loop headers. These may contain SSA definitions
7438 // that are used in the loop nest body. To be usable with in the innermost
7439 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7440 // these instructions may be executed more often than before the tiling.
7441 // TODO: It would be sufficient to only sink them into body of the
7442 // corresponding tile loop.
7444 for (int i = 0; i < NumLoops - 1; ++i) {
7445 CanonicalLoopInfo *Surrounding = Loops[i];
7446 CanonicalLoopInfo *Nested = Loops[i + 1];
7447
7448 BasicBlock *EnterBB = Surrounding->getBody();
7449 BasicBlock *ExitBB = Nested->getHeader();
7450 InbetweenCode.emplace_back(EnterBB, ExitBB);
7451 }
7452
7453 // Compute the trip counts of the floor loops.
7454 Builder.SetCurrentDebugLocation(DL);
7455 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7456 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7457 for (int i = 0; i < NumLoops; ++i) {
7458 Value *TileSize = TileSizes[i];
7459 Value *OrigTripCount = OrigTripCounts[i];
7460 Type *IVType = OrigTripCount->getType();
7461
7462 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7463 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7464
7465 // 0 if tripcount divides the tilesize, 1 otherwise.
7466 // 1 means we need an additional iteration for a partial tile.
7467 //
7468 // Unfortunately we cannot just use the roundup-formula
7469 // (tripcount + tilesize - 1)/tilesize
7470 // because the summation might overflow. We do not want introduce undefined
7471 // behavior when the untiled loop nest did not.
7472 Value *FloorTripOverflow =
7473 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7474
7475 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7476 Value *FloorTripCount =
7477 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7478 "omp_floor" + Twine(i) + ".tripcount", true);
7479
7480 // Remember some values for later use.
7481 FloorCompleteCount.push_back(FloorCompleteTripCount);
7482 FloorCount.push_back(FloorTripCount);
7483 FloorRems.push_back(FloorTripRem);
7484 }
7485
7486 // Generate the new loop nest, from the outermost to the innermost.
7487 std::vector<CanonicalLoopInfo *> Result;
7488 Result.reserve(NumLoops * 2);
7489
7490 // The basic block of the surrounding loop that enters the nest generated
7491 // loop.
7492 BasicBlock *Enter = OutermostLoop->getPreheader();
7493
7494 // The basic block of the surrounding loop where the inner code should
7495 // continue.
7496 BasicBlock *Continue = OutermostLoop->getAfter();
7497
7498 // Where the next loop basic block should be inserted.
7499 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7500
7501 auto EmbeddNewLoop =
7502 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7503 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7504 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7505 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7506 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7507 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7508
7509 // Setup the position where the next embedded loop connects to this loop.
7510 Enter = EmbeddedLoop->getBody();
7511 Continue = EmbeddedLoop->getLatch();
7512 OutroInsertBefore = EmbeddedLoop->getLatch();
7513 return EmbeddedLoop;
7514 };
7515
7516 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7517 const Twine &NameBase) {
7518 for (auto P : enumerate(TripCounts)) {
7519 CanonicalLoopInfo *EmbeddedLoop =
7520 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7521 Result.push_back(EmbeddedLoop);
7522 }
7523 };
7524
7525 EmbeddNewLoops(FloorCount, "floor");
7526
7527 // Within the innermost floor loop, emit the code that computes the tile
7528 // sizes.
7529 Builder.SetInsertPoint(Enter->getTerminator());
7530 SmallVector<Value *, 4> TileCounts;
7531 for (int i = 0; i < NumLoops; ++i) {
7532 CanonicalLoopInfo *FloorLoop = Result[i];
7533 Value *TileSize = TileSizes[i];
7534
7535 Value *FloorIsEpilogue =
7536 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7537 Value *TileTripCount =
7538 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7539
7540 TileCounts.push_back(TileTripCount);
7541 }
7542
7543 // Create the tile loops.
7544 EmbeddNewLoops(TileCounts, "tile");
7545
7546 // Insert the inbetween code into the body.
7547 BasicBlock *BodyEnter = Enter;
7548 BasicBlock *BodyEntered = nullptr;
7549 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7550 BasicBlock *EnterBB = P.first;
7551 BasicBlock *ExitBB = P.second;
7552
7553 if (BodyEnter)
7554 redirectTo(BodyEnter, EnterBB, DL);
7555 else
7556 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7557
7558 BodyEnter = nullptr;
7559 BodyEntered = ExitBB;
7560 }
7561
7562 // Append the original loop nest body into the generated loop nest body.
7563 if (BodyEnter)
7564 redirectTo(BodyEnter, InnerEnter, DL);
7565 else
7566 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7568
7569 // Replace the original induction variable with an induction variable computed
7570 // from the tile and floor induction variables.
7571 Builder.restoreIP(Result.back()->getBodyIP());
7572 for (int i = 0; i < NumLoops; ++i) {
7573 CanonicalLoopInfo *FloorLoop = Result[i];
7574 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7575 Value *OrigIndVar = OrigIndVars[i];
7576 Value *Size = TileSizes[i];
7577
7578 Value *Scale =
7579 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7580 Value *Shift =
7581 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7582 OrigIndVar->replaceAllUsesWith(Shift);
7583 }
7584
7585 // Remove unused parts of the original loops.
7586 removeUnusedBlocksFromParent(OldControlBBs);
7587
7588 for (CanonicalLoopInfo *L : Loops)
7589 L->invalidate();
7590
7591#ifndef NDEBUG
7592 for (CanonicalLoopInfo *GenL : Result)
7593 GenL->assertOK();
7594#endif
7595 return Result;
7596}
7597
7598/// Attach metadata \p Properties to the basic block described by \p BB. If the
7599/// basic block already has metadata, the basic block properties are appended.
7602 // Nothing to do if no property to attach.
7603 if (Properties.empty())
7604 return;
7605
7606 LLVMContext &Ctx = BB->getContext();
7607 SmallVector<Metadata *> NewProperties;
7608 NewProperties.push_back(nullptr);
7609
7610 // If the basic block already has metadata, prepend it to the new metadata.
7611 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7612 if (Existing)
7613 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7614
7615 append_range(NewProperties, Properties);
7616 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7617 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7618
7619 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7620}
7621
7622/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7623/// loop already has metadata, the loop properties are appended.
7626 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7627
7628 // Attach metadata to the loop's latch
7629 BasicBlock *Latch = Loop->getLatch();
7630 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7632}
7633
7634/// Attach llvm.access.group metadata to the memref instructions of \p Block
7636 LoopInfo &LI) {
7637 for (Instruction &I : *Block) {
7638 if (I.mayReadOrWriteMemory()) {
7639 // TODO: This instruction may already have access group from
7640 // other pragmas e.g. #pragma clang loop vectorize. Append
7641 // so that the existing metadata is not overwritten.
7642 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7643 }
7644 }
7645}
7646
7647CanonicalLoopInfo *
7649 CanonicalLoopInfo *firstLoop = Loops.front();
7650 CanonicalLoopInfo *lastLoop = Loops.back();
7651 Function *F = firstLoop->getPreheader()->getParent();
7652
7653 // Loop control blocks that will become orphaned later
7654 SmallVector<BasicBlock *> oldControlBBs;
7656 Loop->collectControlBlocks(oldControlBBs);
7657
7658 // Collect original trip counts
7659 SmallVector<Value *> origTripCounts;
7660 for (CanonicalLoopInfo *L : Loops) {
7661 assert(L->isValid() && "All input loops must be valid canonical loops");
7662 origTripCounts.push_back(L->getTripCount());
7663 }
7664
7665 Builder.SetCurrentDebugLocation(DL);
7666
7667 // Compute max trip count.
7668 // The fused loop will be from 0 to max(origTripCounts)
7669 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7670 F, firstLoop->getHeader());
7671 Builder.SetInsertPoint(TCBlock);
7672 Value *fusedTripCount = nullptr;
7673 for (CanonicalLoopInfo *L : Loops) {
7674 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7675 Value *origTripCount = L->getTripCount();
7676 if (!fusedTripCount) {
7677 fusedTripCount = origTripCount;
7678 continue;
7679 }
7680 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7681 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7682 ".omp.fuse.tc");
7683 }
7684
7685 // Generate new loop
7686 CanonicalLoopInfo *fused =
7687 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7688 lastLoop->getLatch(), "fused");
7689
7690 // Replace original loops with the fused loop
7691 // Preheader and After are not considered inside the CLI.
7692 // These are used to compute the individual TCs of the loops
7693 // so they have to be put before the resulting fused loop.
7694 // Moving them up for readability.
7695 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7696 Loops[i]->getPreheader()->moveBefore(TCBlock);
7697 Loops[i]->getAfter()->moveBefore(TCBlock);
7698 }
7699 lastLoop->getPreheader()->moveBefore(TCBlock);
7700
7701 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7702 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7703 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7704 }
7705 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7706 redirectTo(TCBlock, fused->getPreheader(), DL);
7707 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7708
7709 // Build the fused body
7710 // Create new Blocks with conditions that jump to the original loop bodies
7712 SmallVector<Value *> condValues;
7713 for (size_t i = 0; i < Loops.size(); ++i) {
7714 BasicBlock *condBlock = BasicBlock::Create(
7715 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7716 Builder.SetInsertPoint(condBlock);
7717 Value *condValue =
7718 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7719 condBBs.push_back(condBlock);
7720 condValues.push_back(condValue);
7721 }
7722 // Join the condition blocks with the bodies of the original loops
7723 redirectTo(fused->getBody(), condBBs[0], DL);
7724 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7725 Builder.SetInsertPoint(condBBs[i]);
7726 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7727 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7728 // Replace the IV with the fused IV
7729 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7730 }
7731 // Last body jumps to the created end body block
7732 Builder.SetInsertPoint(condBBs.back());
7733 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7734 fused->getLatch());
7735 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7736 // Replace the IV with the fused IV
7737 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7738
7739 // The loop latch must have only one predecessor. Currently it is branched to
7740 // from both the last condition block and the last loop body
7741 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7742 "omp.fused.pre_latch");
7743
7744 // Remove unused parts
7745 removeUnusedBlocksFromParent(oldControlBBs);
7746
7747 // Invalidate old CLIs
7748 for (CanonicalLoopInfo *L : Loops)
7749 L->invalidate();
7750
7751#ifndef NDEBUG
7752 fused->assertOK();
7753#endif
7754 return fused;
7755}
7756
7757void OpenMPIRBuilder::addUnrollMetadata(CanonicalLoopInfo *Loop,
7759 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7760 if (Loop->VirtualCollapse)
7761 reportFatalInternalError("unrolling must precede Virtual collapse");
7763}
7764
7766 LLVMContext &Ctx = Builder.getContext();
7767 addUnrollMetadata(
7768 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7769 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7770}
7771
7773 LLVMContext &Ctx = Builder.getContext();
7774 addUnrollMetadata(
7775 Loop, {
7776 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7777 });
7778}
7779
7780void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7781 Value *IfCond, ValueToValueMapTy &VMap,
7782 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7783 const Twine &NamePrefix) {
7784 Function *F = CanonicalLoop->getFunction();
7785
7786 // We can't do
7787 // if (cond) {
7788 // simd_loop;
7789 // } else {
7790 // non_simd_loop;
7791 // }
7792 // because then the CanonicalLoopInfo would only point to one of the loops:
7793 // leading to other constructs operating on the same loop to malfunction.
7794 // Instead generate
7795 // while (...) {
7796 // if (cond) {
7797 // simd_body;
7798 // } else {
7799 // not_simd_body;
7800 // }
7801 // }
7802 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7803 // body at -O3
7804
7805 // Define where if branch should be inserted
7806 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7807
7808 // Create additional blocks for the if statement
7809 BasicBlock *Cond = SplitBeforeIt->getParent();
7810 llvm::LLVMContext &C = Cond->getContext();
7812 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7814 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7815
7816 // Create if condition branch.
7817 Builder.SetInsertPoint(SplitBeforeIt);
7818 Instruction *BrInstr =
7819 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7820 InsertPointTy IP(++BrInstr->getIterator());
7821 // Then block contains branch to omp loop body which needs to be vectorized
7822 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7823 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7824
7825 Builder.SetInsertPoint(ElseBlock);
7826
7827 // Clone loop for the else branch
7829
7830 SmallVector<BasicBlock *, 8> ExistingBlocks;
7831 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7832 ExistingBlocks.push_back(ThenBlock);
7833 ExistingBlocks.append(L->block_begin(), L->block_end());
7834 // Cond is the block that has the if clause condition
7835 // LoopCond is omp_loop.cond
7836 // LoopHeader is omp_loop.header
7837 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7838 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7839 assert(LoopCond && LoopHeader && "Invalid loop structure");
7840 for (BasicBlock *Block : ExistingBlocks) {
7841 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7842 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7843 continue;
7844 }
7845 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7846
7847 // fix name not to be omp.if.then
7848 if (Block == ThenBlock)
7849 NewBB->setName(NamePrefix + ".if.else");
7850
7851 NewBB->moveBefore(CanonicalLoop->getExit());
7852 VMap[Block] = NewBB;
7853 NewBlocks.push_back(NewBB);
7854 }
7855 remapInstructionsInBlocks(NewBlocks, VMap);
7856 Builder.CreateBr(NewBlocks.front());
7857
7858 // The loop latch must have only one predecessor. Currently it is branched to
7859 // from both the 'then' and 'else' branches.
7860 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7861 NamePrefix + ".pre_latch");
7862
7863 // Ensure that the then block is added to the loop so we add the attributes in
7864 // the next step
7865 L->addBasicBlockToLoop(ThenBlock, LI);
7866}
7867
7868unsigned
7870 const StringMap<bool> &Features) {
7871 if (TargetTriple.isX86()) {
7872 if (Features.lookup("avx512f"))
7873 return 512;
7874 else if (Features.lookup("avx"))
7875 return 256;
7876 return 128;
7877 }
7878 if (TargetTriple.isPPC())
7879 return 128;
7880 if (TargetTriple.isWasm())
7881 return 128;
7882 if (TargetTriple.isSystemZ())
7883 return 64;
7884 return 0;
7885}
7886
7888 MapVector<Value *, Value *> AlignedVars,
7889 Value *IfCond, OrderKind Order,
7890 ConstantInt *Simdlen, ConstantInt *Safelen) {
7891 if (CanonicalLoop->VirtualCollapse)
7892 CanonicalLoop->reportUnsupportedVirtualCollapse();
7893 LLVMContext &Ctx = Builder.getContext();
7894
7895 Function *F = CanonicalLoop->getFunction();
7896
7897 // Blocks must have terminators.
7898 // FIXME: Don't run analyses on incomplete/invalid IR.
7900 for (BasicBlock &BB : *F)
7901 if (!BB.hasTerminator())
7902 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7903
7904 // TODO: We should not rely on pass manager. Currently we use pass manager
7905 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7906 // object. We should have a method which returns all blocks between
7907 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7909 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7910 FAM.registerPass([]() { return LoopAnalysis(); });
7911 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7912
7913 LoopAnalysis LIA;
7914 LoopInfo &&LI = LIA.run(*F, FAM);
7915
7916 for (Instruction *I : UIs)
7917 I->eraseFromParent();
7918
7919 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7920 if (AlignedVars.size()) {
7921 InsertPointTy IP = Builder.saveIP();
7922 for (auto &AlignedItem : AlignedVars) {
7923 Value *AlignedPtr = AlignedItem.first;
7924 Value *Alignment = AlignedItem.second;
7925 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7926 Builder.SetInsertPoint(loadInst->getNextNode());
7927 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7928 Alignment);
7929 }
7930 Builder.restoreIP(IP);
7931 }
7932
7933 if (IfCond) {
7934 ValueToValueMapTy VMap;
7935 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7936 }
7937
7939
7940 // Get the basic blocks from the loop in which memref instructions
7941 // can be found.
7942 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7943 // preferably without running any passes.
7944 for (BasicBlock *Block : L->getBlocks()) {
7945 if (Block == CanonicalLoop->getCond() ||
7946 Block == CanonicalLoop->getHeader())
7947 continue;
7948 Reachable.insert(Block);
7949 }
7950
7951 SmallVector<Metadata *> LoopMDList;
7952
7953 // In presence of finite 'safelen', it may be unsafe to mark all
7954 // the memory instructions parallel, because loop-carried
7955 // dependences of 'safelen' iterations are possible.
7956 // If clause order(concurrent) is specified then the memory instructions
7957 // are marked parallel even if 'safelen' is finite.
7958 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7959 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7960
7961 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7962 // versions so we can't add the loop attributes in that case.
7963 if (IfCond) {
7964 // we can still add llvm.loop.parallel_access
7965 addLoopMetadata(CanonicalLoop, LoopMDList);
7966 return;
7967 }
7968
7969 // Use the above access group metadata to create loop level
7970 // metadata, which should be distinct for each loop.
7971 LoopMDList.push_back(
7972 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7973
7974 if (Simdlen || Safelen) {
7975 // If both simdlen and safelen clauses are specified, the value of the
7976 // simdlen parameter must be less than or equal to the value of the safelen
7977 // parameter. Therefore, use safelen only in the absence of simdlen.
7978 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7979 LoopMDList.push_back(
7980 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7981 ConstantAsMetadata::get(VectorizeWidth)}));
7982 }
7983
7984 addLoopMetadata(CanonicalLoop, LoopMDList);
7985}
7986
7987/// Create the TargetMachine object to query the backend for optimization
7988/// preferences.
7989///
7990/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7991/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7992/// needed for the LLVM pass pipline. We use some default options to avoid
7993/// having to pass too many settings from the frontend that probably do not
7994/// matter.
7995///
7996/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7997/// method. If we are going to use TargetMachine for more purposes, especially
7998/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7999/// might become be worth requiring front-ends to pass on their TargetMachine,
8000/// or at least cache it between methods. Note that while fontends such as Clang
8001/// have just a single main TargetMachine per translation unit, "target-cpu" and
8002/// "target-features" that determine the TargetMachine are per-function and can
8003/// be overrided using __attribute__((target("OPTIONS"))).
8004static std::unique_ptr<TargetMachine>
8006 Module *M = F->getParent();
8007
8008 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
8009 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
8010 const llvm::Triple &Triple = M->getTargetTriple();
8011
8012 std::string Error;
8014 if (!TheTarget)
8015 return {};
8016
8018 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
8019 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
8020 /*CodeModel=*/std::nullopt, OptLevel));
8021}
8022
8023/// Heuristically determine the best-performant unroll factor for \p CLI. This
8024/// depends on the target processor. We are re-using the same heuristics as the
8025/// LoopUnrollPass.
8027 Function *F = CLI->getFunction();
8028
8029 // Assume the user requests the most aggressive unrolling, even if the rest of
8030 // the code is optimized using a lower setting.
8032 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
8033
8034 // Blocks must have terminators.
8035 // FIXME: Don't run analyses on incomplete/invalid IR.
8037 for (BasicBlock &BB : *F)
8038 if (!BB.hasTerminator())
8039 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
8040
8042 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
8043 FAM.registerPass([]() { return AssumptionAnalysis(); });
8044 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
8045 FAM.registerPass([]() { return LoopAnalysis(); });
8046 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
8047 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
8048 TargetIRAnalysis TIRA;
8049 if (TM)
8050 TIRA = TargetIRAnalysis(
8051 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
8052 FAM.registerPass([&]() { return TIRA; });
8053
8054 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
8056 ScalarEvolution &&SE = SEA.run(*F, FAM);
8058 DominatorTree &&DT = DTA.run(*F, FAM);
8059 LoopAnalysis LIA;
8060 LoopInfo &&LI = LIA.run(*F, FAM);
8062 AssumptionCache &&AC = ACT.run(*F, FAM);
8064
8065 for (Instruction *I : UIs)
8066 I->eraseFromParent();
8067
8068 Loop *L = LI.getLoopFor(CLI->getHeader());
8069 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
8070
8072 L, SE, TTI,
8073 /*BlockFrequencyInfo=*/nullptr,
8074 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
8075 /*UserThreshold=*/std::nullopt,
8076 /*UserAllowPartial=*/true,
8077 /*UserAllowRuntime=*/true,
8078 /*UserUpperBound=*/std::nullopt,
8079 /*UserFullUnrollMaxCount=*/std::nullopt);
8080
8081 UP.Force = true;
8082
8083 // Account for additional optimizations taking place before the LoopUnrollPass
8084 // would unroll the loop.
8087
8088 // Use normal unroll factors even if the rest of the code is optimized for
8089 // size.
8092
8093 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
8094 << " Threshold=" << UP.Threshold << "\n"
8095 << " PartialThreshold=" << UP.PartialThreshold << "\n"
8096 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
8097 << " PartialOptSizeThreshold="
8098 << UP.PartialOptSizeThreshold << "\n");
8099
8100 // Disable peeling.
8103 /*UserAllowPeeling=*/false,
8104 /*UserAllowProfileBasedPeeling=*/false,
8105 /*UnrollingSpecficValues=*/false);
8106
8108 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
8109
8110 // Assume that reads and writes to stack variables can be eliminated by
8111 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
8112 // size.
8113 for (BasicBlock *BB : L->blocks()) {
8114 for (Instruction &I : *BB) {
8115 Value *Ptr;
8116 if (auto *Load = dyn_cast<LoadInst>(&I)) {
8117 Ptr = Load->getPointerOperand();
8118 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
8119 Ptr = Store->getPointerOperand();
8120 } else
8121 continue;
8122
8123 Ptr = Ptr->stripPointerCasts();
8124
8125 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
8126 if (Alloca->getParent() == &F->getEntryBlock())
8127 EphValues.insert(&I);
8128 }
8129 }
8130 }
8131
8132 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
8133
8134 // Loop is not unrollable if the loop contains certain instructions.
8135 if (!UCE.canUnroll()) {
8136 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
8137 return 1;
8138 }
8139
8140 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
8141 << "\n");
8142
8143 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
8144 // be able to use it.
8145 int TripCount = 0;
8146 int MaxTripCount = 0;
8147 bool MaxOrZero = false;
8148 unsigned TripMultiple = 0;
8149
8150 unsigned Factor =
8151 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
8152 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
8153 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
8154
8155 // This function returns 1 to signal to not unroll a loop.
8156 if (Factor == 0)
8157 return 1;
8158 return Factor;
8159}
8160
8162 int32_t Factor,
8163 CanonicalLoopInfo **UnrolledCLI) {
8164 assert(Factor >= 0 && "Unroll factor must not be negative");
8165
8166 Function *F = Loop->getFunction();
8167 LLVMContext &Ctx = F->getContext();
8168
8169 // If the unrolled loop is not used for another loop-associated directive, it
8170 // is sufficient to add metadata for the LoopUnrollPass.
8171 if (!UnrolledCLI) {
8172 SmallVector<Metadata *, 2> LoopMetadata;
8173 LoopMetadata.push_back(
8174 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
8175
8176 if (Factor >= 1) {
8178 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
8179 LoopMetadata.push_back(MDNode::get(
8180 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
8181 }
8182
8183 addUnrollMetadata(Loop, LoopMetadata);
8184 return;
8185 }
8186
8187 // Heuristically determine the unroll factor.
8188 if (Factor == 0) {
8189 if (Loop->VirtualCollapse)
8190 Loop->reportUnsupportedVirtualCollapse();
8192 }
8193
8194 // No change required with unroll factor 1.
8195 if (Factor == 1) {
8196 *UnrolledCLI = Loop;
8197 return;
8198 }
8199
8200 assert(Factor >= 2 &&
8201 "unrolling only makes sense with a factor of 2 or larger");
8202
8203 Type *IndVarTy = Loop->getIndVarType();
8204
8205 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
8206 // unroll the inner loop.
8207 Value *FactorVal =
8208 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
8209 /*isSigned=*/false));
8210 std::vector<CanonicalLoopInfo *> LoopNest =
8211 tileLoops(DL, {Loop}, {FactorVal});
8212 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
8213 *UnrolledCLI = LoopNest[0];
8214 CanonicalLoopInfo *InnerLoop = LoopNest[1];
8215
8216 // LoopUnrollPass can only fully unroll loops with constant trip count.
8217 // Unroll by the unroll factor with a fallback epilog for the remainder
8218 // iterations if necessary.
8220 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
8222 InnerLoop,
8223 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
8225 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
8226
8227#ifndef NDEBUG
8228 (*UnrolledCLI)->assertOK();
8229#endif
8230}
8231
8234 llvm::Value *BufSize, llvm::Value *CpyBuf,
8235 llvm::Value *CpyFn, llvm::Value *DidIt) {
8236 if (!updateToLocation(Loc))
8237 return Loc.IP;
8238
8239 uint32_t SrcLocStrSize;
8240 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8241 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8242 Value *ThreadId = getOrCreateThreadID(Ident);
8243
8244 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
8245
8246 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
8247
8248 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
8249 createRuntimeFunctionCall(Fn, Args);
8250
8251 return Builder.saveIP();
8252}
8253
8255 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8256 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
8258
8259 if (!updateToLocation(Loc))
8260 return Loc.IP;
8261
8262 // If needed allocate and initialize `DidIt` with 0.
8263 // DidIt: flag variable: 1=single thread; 0=not single thread.
8264 llvm::Value *DidIt = nullptr;
8265 if (!CPVars.empty()) {
8266 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
8267 Builder.CreateStore(Builder.getInt32(0), DidIt);
8268 }
8269
8270 Directive OMPD = Directive::OMPD_single;
8271 uint32_t SrcLocStrSize;
8272 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8273 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8274 Value *ThreadId = getOrCreateThreadID(Ident);
8275 Value *Args[] = {Ident, ThreadId};
8276
8277 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
8278 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8279
8280 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
8281 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8282
8283 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
8284 if (Error Err = FiniCB(IP))
8285 return Err;
8286
8287 // The thread that executes the single region must set `DidIt` to 1.
8288 // This is used by __kmpc_copyprivate, to know if the caller is the
8289 // single thread or not.
8290 if (DidIt)
8291 Builder.CreateStore(Builder.getInt32(1), DidIt);
8292
8293 return Error::success();
8294 };
8295
8296 // generates the following:
8297 // if (__kmpc_single()) {
8298 // .... single region ...
8299 // __kmpc_end_single
8300 // }
8301 // __kmpc_copyprivate
8302 // __kmpc_barrier
8303
8304 InsertPointOrErrorTy AfterIP =
8305 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8306 /*Conditional*/ true,
8307 /*hasFinalize*/ true);
8308 if (!AfterIP)
8309 return AfterIP.takeError();
8310
8311 if (DidIt) {
8312 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8313 // NOTE BufSize is currently unused, so just pass 0.
8315 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8316 CPFuncs[I], DidIt);
8317 // NOTE __kmpc_copyprivate already inserts a barrier
8318 } else if (!IsNowait) {
8319 InsertPointOrErrorTy AfterIP =
8321 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8322 /* CheckCancelFlag */ false);
8323 if (!AfterIP)
8324 return AfterIP.takeError();
8325 }
8326 return Builder.saveIP();
8327}
8328
8331 BodyGenCallbackTy BodyGenCB,
8332 FinalizeCallbackTy FiniCB, bool IsNowait) {
8333
8334 if (!updateToLocation(Loc))
8335 return Loc.IP;
8336
8337 // All threads execute the scope body — no conditional entry.
8338 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8339 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8340 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8341 /*IsCancellable=*/false);
8342 if (!AfterIP)
8343 return AfterIP.takeError();
8344
8345 Builder.restoreIP(*AfterIP);
8346 if (!IsNowait) {
8347 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8348 omp::Directive::OMPD_unknown,
8349 /*ForceSimpleCall=*/false,
8350 /*CheckCancelFlag=*/false);
8351 if (!AfterIP)
8352 return AfterIP.takeError();
8353 }
8354 return Builder.saveIP();
8355}
8356
8358 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8359 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8360
8361 if (!updateToLocation(Loc))
8362 return Loc.IP;
8363
8364 Directive OMPD = Directive::OMPD_critical;
8365 uint32_t SrcLocStrSize;
8366 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8367 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8368 Value *ThreadId = getOrCreateThreadID(Ident);
8369 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8370 Value *Args[] = {Ident, ThreadId, LockVar};
8371
8372 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8373 Function *RTFn = nullptr;
8374 if (HintInst) {
8375 // Add Hint to entry Args and create call
8376 EnterArgs.push_back(HintInst);
8377 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8378 } else {
8379 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8380 }
8381 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8382
8383 Function *ExitRTLFn =
8384 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8385 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8386
8387 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8388 /*Conditional*/ false, /*hasFinalize*/ true);
8389}
8390
8393 InsertPointTy AllocaIP, unsigned NumLoops,
8394 ArrayRef<llvm::Value *> StoreValues,
8395 const Twine &Name, bool IsDependSource) {
8396 assert(
8397 llvm::all_of(StoreValues,
8398 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8399 "OpenMP runtime requires depend vec with i64 type");
8400
8401 if (!updateToLocation(Loc))
8402 return Loc.IP;
8403
8404 // Allocate space for vector and generate alloc instruction.
8405 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8406 Builder.restoreIP(AllocaIP);
8407 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8408 ArgsBase->setAlignment(Align(8));
8410
8411 // Store the index value with offset in depend vector.
8412 for (unsigned I = 0; I < NumLoops; ++I) {
8413 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8414 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8415 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8416 STInst->setAlignment(Align(8));
8417 }
8418
8419 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8420 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8421
8422 uint32_t SrcLocStrSize;
8423 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8424 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8425 Value *ThreadId = getOrCreateThreadID(Ident);
8426 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8427
8428 Function *RTLFn = nullptr;
8429 if (IsDependSource)
8430 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8431 else
8432 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8433 createRuntimeFunctionCall(RTLFn, Args);
8434
8435 return Builder.saveIP();
8436}
8437
8439 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8440 FinalizeCallbackTy FiniCB, bool IsThreads) {
8441 if (!updateToLocation(Loc))
8442 return Loc.IP;
8443
8444 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8445 Instruction *EntryCall = nullptr;
8446 Instruction *ExitCall = nullptr;
8447
8448 if (IsThreads) {
8449 uint32_t SrcLocStrSize;
8450 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8451 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8452 Value *ThreadId = getOrCreateThreadID(Ident);
8453 Value *Args[] = {Ident, ThreadId};
8454
8455 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8456 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8457
8458 Function *ExitRTLFn =
8459 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8460 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8461 }
8462
8463 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8464 /*Conditional*/ false, /*hasFinalize*/ true);
8465}
8466
8467OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8468 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8469 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8470 bool HasFinalize, bool IsCancellable) {
8471
8472 if (HasFinalize)
8473 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8474
8475 // Create inlined region's entry and body blocks, in preparation
8476 // for conditional creation
8477 BasicBlock *EntryBB = Builder.GetInsertBlock();
8478 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8480 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8481 SplitPos->setDebugLoc(Builder.getCurrentDebugLocation());
8482 }
8483 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8484 BasicBlock *FiniBB =
8485 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8486
8487 Builder.SetInsertPoint(EntryBB->getTerminator());
8488 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8489
8490 // generate body
8491 if (Error Err =
8492 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8493 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8494 return Err;
8495
8496 // emit exit call and do any needed finalization.
8497 auto FinIP = FiniBB->getFirstInsertionPt();
8498 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8499 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8500 "Unexpected control flow graph state!!");
8501 InsertPointOrErrorTy AfterIP =
8502 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8503 if (!AfterIP)
8504 return AfterIP.takeError();
8505
8506 // If we are skipping the region of a non conditional, remove the exit
8507 // block, and clear the builder's insertion point.
8508 assert(SplitPos->getParent() == ExitBB &&
8509 "Unexpected Insertion point location!");
8510 auto merged = MergeBlockIntoPredecessor(ExitBB);
8511 BasicBlock *ExitPredBB = SplitPos->getParent();
8512 auto InsertBB = merged ? ExitPredBB : ExitBB;
8514 SplitPos->eraseFromParent();
8515 Builder.SetInsertPoint(InsertBB);
8516
8517 return Builder.saveIP();
8518}
8519
8520OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8521 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8522 // if nothing to do, Return current insertion point.
8523 if (!Conditional || !EntryCall)
8524 return Builder.saveIP();
8525
8526 BasicBlock *EntryBB = Builder.GetInsertBlock();
8527 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8528 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8529 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8530
8531 // Emit thenBB and set the Builder's insertion point there for
8532 // body generation next. Place the block after the current block.
8533 Function *CurFn = EntryBB->getParent();
8534 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8535
8536 // Move Entry branch to end of ThenBB, and replace with conditional
8537 // branch (If-stmt)
8538 Instruction *EntryBBTI = EntryBB->getTerminator();
8539 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8540 EntryBBTI->removeFromParent();
8541 Builder.SetInsertPoint(UI);
8542 Builder.Insert(EntryBBTI);
8543 UI->eraseFromParent();
8544 Builder.SetInsertPoint(ThenBB->getTerminator());
8545
8546 // return an insertion point to ExitBB.
8547 return ExitBB->getFirstInsertionPt();
8548}
8549
8550OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8551 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8552 bool HasFinalize) {
8553
8554 Builder.restoreIP(FinIP);
8555
8556 // If there is finalization to do, emit it before the exit call
8557 if (HasFinalize) {
8558 assert(!FinalizationStack.empty() &&
8559 "Unexpected finalization stack state!");
8560
8561 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8562 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8563
8564 BasicBlock *FinBB = FinIP.getNodeParent();
8565 if (Error Err = Fi.mergeFiniBB(Builder, FinBB))
8566 return std::move(Err);
8567
8568 // Exit condition: insertion point is before the terminator of the new Fini
8569 // block
8570 Builder.SetInsertPoint(FinBB->getTerminator());
8571 }
8572
8573 if (!ExitCall)
8574 return Builder.saveIP();
8575
8576 // place the Exitcall as last instruction before Finalization block terminator
8577 ExitCall->removeFromParent();
8578 Builder.Insert(ExitCall);
8579
8580 return ExitCall->getIterator();
8581}
8582
8584 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8585 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8586 if (!IP.isValid())
8587 return IP;
8588
8590
8591 // creates the following CFG structure
8592 // OMP_Entry : (MasterAddr != PrivateAddr)?
8593 // F T
8594 // | \
8595 // | copin.not.master
8596 // | /
8597 // v /
8598 // copyin.not.master.end
8599 // |
8600 // v
8601 // OMP.Entry.Next
8602
8603 BasicBlock *OMP_Entry = IP.getNodeParent();
8604 Function *CurFn = OMP_Entry->getParent();
8605 BasicBlock *CopyBegin =
8606 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8607 BasicBlock *CopyEnd = nullptr;
8608
8609 // If entry block is terminated, split to preserve the branch to following
8610 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8612 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8613 "copyin.not.master.end");
8614 OMP_Entry->getTerminator()->eraseFromParent();
8615 } else {
8616 CopyEnd =
8617 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8618 }
8619
8620 Builder.SetInsertPoint(OMP_Entry);
8621 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8622 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8623 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8624 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8625
8626 Builder.SetInsertPoint(CopyBegin);
8627 if (BranchtoEnd)
8628 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8629
8630 return Builder.saveIP();
8631}
8632
8634 Value *Size, Value *Allocator,
8635 std::string Name) {
8637 if (!updateToLocation(Loc))
8638 return nullptr;
8639
8640 uint32_t SrcLocStrSize;
8641 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8642 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8643 Value *ThreadId = getOrCreateThreadID(Ident);
8644 Value *Args[] = {ThreadId, Size, Allocator};
8645
8646 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8647
8648 return createRuntimeFunctionCall(Fn, Args, Name);
8649}
8650
8652 Value *Align, Value *Size,
8653 Value *Allocator,
8654 std::string Name) {
8656 if (!updateToLocation(Loc))
8657 return nullptr;
8658
8659 uint32_t SrcLocStrSize;
8660 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8661 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8662 Value *ThreadId = getOrCreateThreadID(Ident);
8663 Value *Args[] = {ThreadId, Align, Size, Allocator};
8664
8665 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8666
8667 return Builder.CreateCall(Fn, Args, Name);
8668}
8669
8671 Value *Addr, Value *Allocator,
8672 std::string Name) {
8674 if (!updateToLocation(Loc))
8675 return nullptr;
8676
8677 uint32_t SrcLocStrSize;
8678 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8679 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8680 Value *ThreadId = getOrCreateThreadID(Ident);
8681 Value *Args[] = {ThreadId, Addr, Allocator};
8682 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8683 return createRuntimeFunctionCall(Fn, Args, Name);
8684}
8685
8687 Value *Size,
8688 const Twine &Name) {
8691
8692 Value *Args[] = {Size};
8693 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8694 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8696 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8697 return Call;
8698}
8699
8701 Type *VarType,
8702 const Twine &Name) {
8703 return createOMPAllocShared(
8704 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8705}
8706
8708 Value *Addr, Value *Size,
8709 const Twine &Name) {
8712
8713 Value *Args[] = {Addr, Size};
8714 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8715 return Builder.CreateCall(Fn, Args, Name);
8716}
8717
8719 Value *Addr, Type *VarType,
8720 const Twine &Name) {
8721 return createOMPFreeShared(
8722 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8723 Name);
8724}
8725
8727 const LocationDescription &Loc, Value *InteropVar,
8729 Value *DependenceAddress, bool HaveNowaitClause) {
8732
8733 uint32_t SrcLocStrSize;
8734 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8735 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8736 Value *ThreadId = getOrCreateThreadID(Ident);
8737 if (Device == nullptr)
8739 else if (Device->getType() != Int32)
8740 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8741 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8742 if (NumDependences == nullptr) {
8743 NumDependences = ConstantInt::get(Int32, 0);
8744 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8745 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8746 }
8747 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8748 Value *Args[] = {
8749 Ident, ThreadId, InteropVar, InteropTypeVal,
8750 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8751
8752 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8753
8754 return createRuntimeFunctionCall(Fn, Args);
8755}
8756
8758 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8759 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8762
8763 uint32_t SrcLocStrSize;
8764 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8765 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8766 Value *ThreadId = getOrCreateThreadID(Ident);
8767 if (Device == nullptr)
8769 else if (Device->getType() != Int32)
8770 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8771 if (NumDependences == nullptr) {
8772 NumDependences = ConstantInt::get(Int32, 0);
8773 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8774 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8775 }
8776 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8777 Value *Args[] = {
8778 Ident, ThreadId, InteropVar, Device,
8779 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8780
8781 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8782
8783 return createRuntimeFunctionCall(Fn, Args);
8784}
8785
8787 Value *InteropVar, Value *Device,
8788 Value *NumDependences,
8789 Value *DependenceAddress,
8790 bool HaveNowaitClause) {
8793 uint32_t SrcLocStrSize;
8794 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8795 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8796 Value *ThreadId = getOrCreateThreadID(Ident);
8797 if (Device == nullptr)
8799 else if (Device->getType() != Int32)
8800 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8801 if (NumDependences == nullptr) {
8802 NumDependences = ConstantInt::get(Int32, 0);
8803 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8804 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8805 }
8806 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8807 Value *Args[] = {
8808 Ident, ThreadId, InteropVar, Device,
8809 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8810
8811 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8812
8813 return createRuntimeFunctionCall(Fn, Args);
8814}
8815
8818 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8821
8822 uint32_t SrcLocStrSize;
8823 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8824 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8825 Value *ThreadId = getOrCreateThreadID(Ident);
8826 Constant *ThreadPrivateCache =
8827 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8828 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8829
8830 Function *Fn =
8831 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8832
8833 return createRuntimeFunctionCall(Fn, Args);
8834}
8835
8837 const LocationDescription &Loc,
8839 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8840 "expected num_threads and num_teams to be specified");
8841
8842 if (!updateToLocation(Loc))
8843 return nullptr;
8844
8845 uint32_t SrcLocStrSize;
8846 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8847 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8848 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8849 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8850 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8851 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8852 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8853 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8854
8855 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8856 Function *Kernel = DebugKernelWrapper;
8857
8858 // We need to strip the debug prefix to get the correct kernel name.
8859 StringRef KernelName = Kernel->getName();
8860 const std::string DebugPrefix = "_debug__";
8861 if (KernelName.ends_with(DebugPrefix)) {
8862 KernelName = KernelName.drop_back(DebugPrefix.length());
8863 Kernel = M.getFunction(KernelName);
8864 assert(Kernel && "Expected the real kernel to exist");
8865 }
8866
8867 // Manifest the launch configuration in the metadata matching the kernel
8868 // environment.
8869 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8870 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8871 Attrs.MaxTeams.front());
8872
8873 // Don't derive or write thread bounds for Bare kernels.
8874 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8875 if (Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_BARE) {
8876 // If MaxThreads is not set and needs adjustment, select the maximum
8877 // between the default workgroup size and the MinThreads value. This is
8878 // only meaningful for targets with a known grid value (i.e. GPUs); for
8879 // other targets (e.g. host kernels) leave it unset so the runtime falls
8880 // back to its own device-specific default.
8881 if (MaxThreadsVal < 0 && UseDefaultMaxThreads && hasGridValue(T))
8882 MaxThreadsVal =
8883 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8884 Attrs.MinThreads.front());
8885
8886 // Generic mode runs the main thread on a warp of its own, past
8887 // thread_limit. Reserve the widest warp any target has. Not on SPIR-V,
8888 // causes problems with Level Zero.
8889 if (MaxThreadsVal > 0 &&
8890 Attrs.ExecFlags == omp::OMP_TGT_EXEC_MODE_GENERIC && hasGridValue(T) &&
8891 !T.isSPIRV())
8892 MaxThreadsVal = int32_t(
8893 std::min<int64_t>(int64_t(MaxThreadsVal) + 64,
8894 int64_t(getGridValue(T, Kernel).GV_Max_WG_Size)));
8895
8896 if (MaxThreadsVal > 0)
8897 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8898 MaxThreadsVal);
8899 }
8900
8901 Constant *MinThreads =
8902 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8903 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8904 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8905 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8906 Constant *ReductionDataSize =
8907 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8908
8909 const DataLayout &DL = M.getDataLayout();
8910
8911 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8912 Constant *DynamicEnvironmentInitializer =
8913 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8914 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8915 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8916 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8917 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8918 DL.getDefaultGlobalsAddressSpace());
8919 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8920
8921 Constant *DynamicEnvironment =
8922 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8923 ? DynamicEnvironmentGV
8924 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8925 DynamicEnvironmentPtr);
8926
8927 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8928 ConfigurationEnvironment, {
8929 UseGenericStateMachineVal,
8930 MayUseNestedParallelismVal,
8931 IsSPMDVal,
8932 MinThreads,
8933 MaxThreads,
8934 MinTeams,
8935 MaxTeams,
8936 ReductionDataSize,
8937 });
8938 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8939 KernelEnvironment, {
8940 ConfigurationEnvironmentInitializer,
8941 Ident,
8942 DynamicEnvironment,
8943 });
8944 std::string KernelEnvironmentName =
8945 (KernelName + "_kernel_environment").str();
8946 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8947 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8948 KernelEnvironmentInitializer, KernelEnvironmentName,
8949 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8950 DL.getDefaultGlobalsAddressSpace());
8951 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8952
8953 return KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8954 ? KernelEnvironmentGV
8955 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8956 KernelEnvironmentPtr);
8957}
8958
8960 const LocationDescription &Loc,
8962 Constant *KernelEnvironment = emitKernelEnvironment(Loc, Attrs);
8963 if (!KernelEnvironment)
8964 return Loc.IP;
8965
8966 if (!updateToLocation(Loc))
8967 return Loc.IP;
8968
8969 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8971 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8972
8973 Value *KernelLaunchEnvironment =
8974 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8975 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8976 KernelLaunchEnvironment =
8977 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8978 ? KernelLaunchEnvironment
8979 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8980 KernelLaunchEnvParamTy);
8981 CallInst *ThreadKind = createRuntimeFunctionCall(
8982 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8983
8984 Value *ExecUserCode = Builder.CreateICmpEQ(
8985 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8986 "exec_user_code");
8987
8988 // ThreadKind = __kmpc_target_init(...)
8989 // if (ThreadKind == -1)
8990 // user_code
8991 // else
8992 // return;
8993
8994 auto *UI = Builder.CreateUnreachable();
8995 BasicBlock *CheckBB = UI->getParent();
8996 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8997
8998 BasicBlock *WorkerExitBB = BasicBlock::Create(
8999 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
9000 Builder.SetInsertPoint(WorkerExitBB);
9001 Builder.CreateRetVoid();
9002
9003 auto *CheckBBTI = CheckBB->getTerminator();
9004 Builder.SetInsertPoint(CheckBBTI);
9005 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
9006
9007 CheckBBTI->eraseFromParent();
9008 UI->eraseFromParent();
9009
9010 // Continue in the "user_code" block, see diagram above and in
9011 // openmp/libomptarget/deviceRTLs/common/include/target.h .
9012 return UserCodeEntryBB->getFirstInsertionPt();
9013}
9014
9016 int32_t TeamsReductionDataSize) {
9017 if (!updateToLocation(Loc))
9018 return;
9019
9021 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
9022
9024
9025 if (!TeamsReductionDataSize)
9026 return;
9027
9028 Function *Kernel = Builder.GetInsertBlock()->getParent();
9029 // We need to strip the debug prefix to get the correct kernel name.
9030 StringRef KernelName = Kernel->getName();
9031 const std::string DebugPrefix = "_debug__";
9032 if (KernelName.ends_with(DebugPrefix))
9033 KernelName = KernelName.drop_back(DebugPrefix.length());
9034 auto *KernelEnvironmentGV =
9035 M.getNamedGlobal((KernelName + "_kernel_environment").str());
9036 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
9037 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
9038 auto *NewInitializer = ConstantFoldInsertValueInstruction(
9039 KernelEnvironmentInitializer,
9040 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
9041 KernelEnvironmentGV->setInitializer(NewInitializer);
9042}
9043
9044static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
9045 bool Min) {
9046 if (Kernel.hasFnAttribute(Name)) {
9047 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
9048 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
9049 }
9050 Kernel.addFnAttr(Name, llvm::utostr(Value));
9051}
9052
9053std::pair<int32_t, int32_t>
9055 int32_t ThreadLimit =
9056 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
9057
9058 if (T.isAMDGPU()) {
9059 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
9060 if (!Attr.isValid() || !Attr.isStringAttribute())
9061 return {0, ThreadLimit};
9062 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
9063 int32_t LB, UB;
9064 if (!llvm::to_integer(UBStr, UB, 10))
9065 return {0, ThreadLimit};
9066 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
9067 if (!llvm::to_integer(LBStr, LB, 10))
9068 return {0, UB};
9069 return {LB, UB};
9070 }
9071
9072 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
9073 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
9074 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
9075 }
9076 return {0, ThreadLimit};
9077}
9078
9080 Function &Kernel, int32_t LB,
9081 int32_t UB) {
9082 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
9083
9084 if (T.isAMDGPU()) {
9085 Kernel.addFnAttr("amdgpu-flat-work-group-size",
9086 llvm::utostr(LB) + "," + llvm::utostr(UB));
9087 return;
9088 }
9089
9091}
9092
9093std::pair<int32_t, int32_t>
9095 // TODO: Read from backend annotations if available.
9096 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
9097}
9098
9100 int32_t LB, int32_t UB) {
9101 if (UB > 0) {
9102 if (T.isNVPTX())
9104 if (T.isAMDGPU())
9105 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
9106 }
9107
9108 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
9109}
9110
9111void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
9112 Function *OutlinedFn) {
9113 if (Config.isTargetDevice()) {
9115 // TODO: Determine if DSO local can be set to true.
9116 OutlinedFn->setDSOLocal(false);
9118 if (T.isAMDGCN())
9120 else if (T.isNVPTX())
9122 else if (T.isSPIRV())
9124 }
9125}
9126
9127Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
9128 StringRef EntryFnIDName) {
9129 if (Config.isTargetDevice()) {
9130 assert(OutlinedFn && "The outlined function must exist if embedded");
9131 return OutlinedFn;
9132 }
9133
9134 return new GlobalVariable(
9135 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
9136 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
9137}
9138
9139Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
9140 StringRef EntryFnName) {
9141 if (OutlinedFn)
9142 return OutlinedFn;
9143
9144 assert(!M.getGlobalVariable(EntryFnName, true) &&
9145 "Named kernel already exists?");
9146 return new GlobalVariable(
9147 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
9148 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
9149}
9150
9152 TargetRegionEntryInfo &EntryInfo,
9153 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
9154 Function *&OutlinedFn, Constant *&OutlinedFnID) {
9155
9156 SmallString<64> EntryFnName;
9157 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
9158
9159 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
9160 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
9161 if (!CBResult)
9162 return CBResult.takeError();
9163 OutlinedFn = *CBResult;
9164 } else {
9165 OutlinedFn = nullptr;
9166 }
9167
9168 // If this target outline function is not an offload entry, we don't need to
9169 // register it. This may be in the case of a false if clause, or if there are
9170 // no OpenMP targets.
9171 if (!IsOffloadEntry)
9172 return Error::success();
9173
9174 std::string EntryFnIDName =
9175 Config.isTargetDevice()
9176 ? std::string(EntryFnName)
9177 : createPlatformSpecificName({EntryFnName, "region_id"});
9178
9179 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
9180 EntryFnName, EntryFnIDName);
9181 return Error::success();
9182}
9183
9185 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
9186 StringRef EntryFnName, StringRef EntryFnIDName) {
9187 if (OutlinedFn)
9188 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
9189 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
9190 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
9191 OffloadInfoManager.registerTargetRegionEntryInfo(
9192 EntryInfo, EntryAddr, OutlinedFnID,
9194 return OutlinedFnID;
9195}
9196
9198 const LocationDescription &Loc, InsertPointTy AllocaIP,
9199 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
9200 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
9201 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
9202 omp::RuntimeFunction *MapperFunc,
9204 BodyGenTy BodyGenType)>
9205 BodyGenCB,
9206 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
9207 if (!updateToLocation(Loc))
9208 return InsertPointTy();
9209
9210 Builder.restoreIP(CodeGenIP);
9211
9212 bool IsStandAlone = !BodyGenCB;
9213 MapInfosTy *MapInfo;
9214 // Generate the code for the opening of the data environment. Capture all the
9215 // arguments of the runtime call by reference because they are used in the
9216 // closing of the region.
9217 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9218 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9219 MapInfo = &GenMapInfoCB(Builder.saveIP());
9220 if (Error Err = emitOffloadingArrays(
9221 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
9222 /*IsNonContiguous=*/true, DeviceAddrCB))
9223 return Err;
9224
9225 TargetDataRTArgs RTArgs;
9227
9228 // Emit the number of elements in the offloading arrays.
9229 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9230
9231 // Source location for the ident struct
9232 if (!SrcLocInfo) {
9233 uint32_t SrcLocStrSize;
9234 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9235 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9236 }
9237
9238 SmallVector<llvm::Value *, 13> OffloadingArgs = {
9239 SrcLocInfo, DeviceID,
9240 PointerNum, RTArgs.BasePointersArray,
9241 RTArgs.PointersArray, RTArgs.SizesArray,
9242 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9243 RTArgs.MappersArray};
9244
9245 if (IsStandAlone) {
9246 assert(MapperFunc && "MapperFunc missing for standalone target data");
9247
9248 auto TaskBodyCB = [&](Value *, Value *,
9250 if (Info.HasNoWait) {
9251 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
9255 }
9256
9258 OffloadingArgs);
9259
9260 if (Info.HasNoWait) {
9261 BasicBlock *OffloadContBlock =
9262 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
9263 Function *CurFn = Builder.GetInsertBlock()->getParent();
9264 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
9265 Builder.restoreIP(Builder.saveIP());
9266 }
9267 return Error::success();
9268 };
9269
9270 bool RequiresOuterTargetTask = Info.HasNoWait;
9271 if (!RequiresOuterTargetTask)
9272 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
9273 /*TargetTaskAllocaIP=*/{}));
9274 else
9275 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
9276 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
9277 } else {
9278 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
9279 omp::OMPRTL___tgt_target_data_begin_mapper);
9280
9281 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
9282
9283 for (auto DeviceMap : Info.DevicePtrInfoMap) {
9284 if (isa<AllocaInst>(DeviceMap.second.second)) {
9285 auto *LI =
9286 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
9287 Builder.CreateStore(LI, DeviceMap.second.second);
9288 }
9289 }
9290
9291 // If device pointer privatization is required, emit the body of the
9292 // region here. It will have to be duplicated: with and without
9293 // privatization.
9294 InsertPointOrErrorTy AfterIP =
9295 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
9296 if (!AfterIP)
9297 return AfterIP.takeError();
9298 Builder.restoreIP(*AfterIP);
9299 }
9300 return Error::success();
9301 };
9302
9303 // If we need device pointer privatization, we need to emit the body of the
9304 // region with no privatization in the 'else' branch of the conditional.
9305 // Otherwise, we don't have to do anything.
9306 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9307 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
9308 InsertPointOrErrorTy AfterIP =
9309 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
9310 if (!AfterIP)
9311 return AfterIP.takeError();
9312 Builder.restoreIP(*AfterIP);
9313 return Error::success();
9314 };
9315
9316 // Generate code for the closing of the data region.
9317 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9318 ArrayRef<BasicBlock *> DeallocBlocks) {
9319 TargetDataRTArgs RTArgs;
9320 Info.EmitDebug = !MapInfo->Names.empty();
9321 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
9322
9323 // Emit the number of elements in the offloading arrays.
9324 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9325
9326 // Source location for the ident struct
9327 if (!SrcLocInfo) {
9328 uint32_t SrcLocStrSize;
9329 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9330 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9331 }
9332
9333 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9334 PointerNum, RTArgs.BasePointersArray,
9335 RTArgs.PointersArray, RTArgs.SizesArray,
9336 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9337 RTArgs.MappersArray};
9338 Function *EndMapperFunc =
9339 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9340
9341 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9342 return Error::success();
9343 };
9344
9345 // We don't have to do anything to close the region if the if clause evaluates
9346 // to false.
9347 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9348 ArrayRef<BasicBlock *> DeallocBlocks) {
9349 return Error::success();
9350 };
9351
9352 Error Err = [&]() -> Error {
9353 if (BodyGenCB) {
9354 Error Err = [&]() {
9355 if (IfCond)
9356 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9357 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9358 }();
9359
9360 if (Err)
9361 return Err;
9362
9363 // If we don't require privatization of device pointers, we emit the body
9364 // in between the runtime calls. This avoids duplicating the body code.
9365 InsertPointOrErrorTy AfterIP =
9366 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9367 if (!AfterIP)
9368 return AfterIP.takeError();
9369 restoreIPandDebugLoc(Builder, *AfterIP);
9370
9371 if (IfCond)
9372 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9373 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9374 }
9375 if (IfCond)
9376 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9377 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9378 }();
9379
9380 if (Err)
9381 return Err;
9382
9383 return Builder.saveIP();
9384}
9385
9388 bool IsGPUDistribute) {
9389 assert((IVSize == 32 || IVSize == 64) &&
9390 "IV size is not compatible with the omp runtime");
9391 RuntimeFunction Name;
9392 if (IsGPUDistribute)
9393 Name = IVSize == 32
9394 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9395 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9396 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9397 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9398 else
9399 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9400 : omp::OMPRTL___kmpc_for_static_init_4u)
9401 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9402 : omp::OMPRTL___kmpc_for_static_init_8u);
9403
9404 return getOrCreateRuntimeFunction(M, Name);
9405}
9406
9408 bool IVSigned) {
9409 assert((IVSize == 32 || IVSize == 64) &&
9410 "IV size is not compatible with the omp runtime");
9411 RuntimeFunction Name = IVSize == 32
9412 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9413 : omp::OMPRTL___kmpc_dispatch_init_4u)
9414 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9415 : omp::OMPRTL___kmpc_dispatch_init_8u);
9416
9417 return getOrCreateRuntimeFunction(M, Name);
9418}
9419
9421 bool IVSigned) {
9422 assert((IVSize == 32 || IVSize == 64) &&
9423 "IV size is not compatible with the omp runtime");
9424 RuntimeFunction Name = IVSize == 32
9425 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9426 : omp::OMPRTL___kmpc_dispatch_next_4u)
9427 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9428 : omp::OMPRTL___kmpc_dispatch_next_8u);
9429
9430 return getOrCreateRuntimeFunction(M, Name);
9431}
9432
9434 bool IVSigned) {
9435 assert((IVSize == 32 || IVSize == 64) &&
9436 "IV size is not compatible with the omp runtime");
9437 RuntimeFunction Name = IVSize == 32
9438 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9439 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9440 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9441 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9442
9443 return getOrCreateRuntimeFunction(M, Name);
9444}
9445
9447 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9448}
9449
9451 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9452 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9453
9454 DISubprogram *NewSP = Func->getSubprogram();
9455 if (!NewSP)
9456 return;
9457
9459
9460 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9461 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9462 // Only use cached variable if the arg number matches. This is important
9463 // so that DIVariable created for privatized variables are not discarded.
9464 if (NewVar && (arg == NewVar->getArg()))
9465 return NewVar;
9466
9468 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9469 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9470 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9471 return NewVar;
9472 };
9473
9474 auto UpdateDebugRecord = [&](auto *DR) {
9475 DILocalVariable *OldVar = DR->getVariable();
9476 unsigned ArgNo = 0;
9477 for (auto Loc : DR->location_ops()) {
9478 auto Iter = ValueReplacementMap.find(Loc);
9479 if (Iter != ValueReplacementMap.end()) {
9480 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9481 ArgNo = std::get<1>(Iter->second) + 1;
9482 }
9483 }
9484 if (ArgNo != 0)
9485 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9486 };
9487
9489 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9490 if (DVR->getNumVariableLocationOps() != 1u) {
9491 DVR->setKillLocation();
9492 return;
9493 }
9494 Value *Loc = DVR->getVariableLocationOp(0u);
9495 BasicBlock *CurBB = DVR->getParent();
9496 BasicBlock *RequiredBB = nullptr;
9497
9498 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9499 RequiredBB = LocInst->getParent();
9500 else if (isa<llvm::Argument>(Loc))
9501 RequiredBB = &DVR->getFunction()->getEntryBlock();
9502
9503 if (RequiredBB && RequiredBB != CurBB) {
9504 assert(!RequiredBB->empty());
9505 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9506 RequiredBB->back().getIterator());
9507 DVRsToDelete.push_back(DVR);
9508 }
9509 };
9510
9511 // The location and scope of variable records still point to
9512 // the parent function of the target region. Update them.
9513 for (Instruction &I : instructions(Func)) {
9514 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9515 UpdateDebugRecord(&DVR);
9516 MoveDebugRecordToCorrectBlock(&DVR);
9517 }
9518 }
9519 for (auto *DVR : DVRsToDelete)
9520 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9521 // An extra argument is passed to the device. Create the debug data for it.
9522 if (OMPBuilder.Config.isTargetDevice()) {
9523 DICompileUnit *CU = NewSP->getUnit();
9524 Module *M = Func->getParent();
9525 DIBuilder DB(*M, true, CU);
9526 DIType *VoidPtrTy =
9527 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9528 unsigned ArgNo = Func->arg_size();
9529 DILocalVariable *Var = DB.createParameterVariable(
9530 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9531 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9532 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9533 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9534 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9535 &(*Func->begin()));
9536 }
9537}
9538
9540 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9541 return cast<Operator>(V)->getOperand(0);
9542 return V;
9543}
9544
9546 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9548 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9551 DebugLoc OutlinedFnLoc) {
9552 SmallVector<Type *> ParameterTypes;
9553 if (OMPBuilder.Config.isTargetDevice()) {
9554 // All parameters to target devices are passed as pointers
9555 // or i64. This assumes 64-bit address spaces/pointers.
9556 for (auto &Arg : Inputs)
9557 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9558 ? Arg->getType()
9559 : Type::getInt64Ty(Builder.getContext()));
9560 } else {
9561 for (auto &Arg : Inputs)
9562 ParameterTypes.push_back(Arg->getType());
9563 }
9564
9565 // The implicit dyn_ptr argument is always the last parameter on both host
9566 // and device so the argument counts match without runtime manipulation.
9567 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9568 ParameterTypes.push_back(PtrTy);
9569
9570 auto BB = Builder.GetInsertBlock();
9571 auto M = BB->getModule();
9572 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9573 /*isVarArg*/ false);
9574 auto Func =
9575 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9576
9577 // Forward target-cpu and target-features function attributes from the
9578 // original function to the new outlined function.
9579 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9580
9581 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9582 if (TargetCpuAttr.isStringAttribute())
9583 Func->addFnAttr(TargetCpuAttr);
9584
9585 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9586 if (TargetFeaturesAttr.isStringAttribute())
9587 Func->addFnAttr(TargetFeaturesAttr);
9588
9589 if (OMPBuilder.Config.isTargetDevice()) {
9590 Value *ExecMode =
9591 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9592 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9593 }
9594
9595 // Save insert point.
9596 IRBuilder<>::InsertPointGuard IPG(Builder);
9597 // We will generate the entries in the outlined function but the debug
9598 // location is still pointing to the parent function, which is the wrong
9599 // scope. OutlinedFnLoc, when the caller provides one, is the same source
9600 // position scoped to the subprogram that will be attached to the outlined
9601 // function, so it is what everything emitted below needs.
9602 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9603
9604 // Generate the region into the function.
9605 BasicBlock *EntryBB = BasicBlock::Create(Builder.getContext(), "entry", Func);
9606 Builder.SetInsertPoint(EntryBB);
9607
9608 // Insert target init call in the device compilation pass. On the host
9609 // (e.g. a non-GPU offload target), there is no runtime init/deinit
9610 // sequence, but the runtime still needs a '<kernel>_kernel_environment'
9611 // global to know how the kernel was configured, so emit it directly.
9612 if (OMPBuilder.Config.isTargetDevice())
9613 Builder.restoreIP(OMPBuilder.createTargetInit(Builder, DefaultAttrs));
9614 else
9615 OMPBuilder.emitKernelEnvironment(Builder, DefaultAttrs);
9616
9617 BasicBlock *UserCodeEntryBB = Builder.GetInsertBlock();
9618
9619 // As we embed the user code in the middle of our target region after we
9620 // generate entry code, we must move what allocas we can into the entry
9621 // block to avoid possible breaking optimisations
9623
9624 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "target.exit");
9625 BasicBlock *OutlinedBodyBB =
9626 splitBB(Builder, /*CreateBranch=*/true, "outlined.body");
9628 CBFunc(Builder.saveIP(), OutlinedBodyBB->begin(), ExitBB);
9629 if (!AfterIP)
9630 return AfterIP.takeError();
9631 Builder.SetInsertPoint(ExitBB);
9632 // The body callback builds the body with its own IRBuilder and cannot reach
9633 // this one directly. But a body holding another OpenMP construct, a nested
9634 // parallel say, calls OpenMPIRBuilder::createParallel, and that can leave
9635 // this Builder pointing at the wrong debug location, or at none at all. The
9636 // epilogue below belongs to the target construct rather than to whatever the
9637 // body emitted last, so re-establish the location the prologue was emitted
9638 // with.
9639 Builder.SetCurrentDebugLocation(OutlinedFnLoc);
9640
9641 // Insert target deinit call in the device compilation pass.
9642 if (OMPBuilder.Config.isTargetDevice())
9643 OMPBuilder.createTargetDeinit(Builder);
9644
9645 // Insert return instruction.
9646 Builder.CreateRetVoid();
9647
9648 // New Alloca IP at entry point of created device function.
9649 Builder.SetInsertPoint(EntryBB->getFirstNonPHIIt());
9650 auto AllocaIP = Builder.saveIP();
9651
9652 Builder.SetInsertPoint(UserCodeEntryBB->getFirstNonPHIOrPseudoProbe());
9653
9654 // Do not include the artificial dyn_ptr argument.
9655 const auto &ArgRange = make_range(Func->arg_begin(), Func->arg_end() - 1);
9656
9658
9659 auto ReplaceValue = [](Value *Input, Value *InputCopy, Function *Func) {
9660 // Things like GEP's can come in the form of Constants. Constants and
9661 // ConstantExpr's do not have access to the knowledge of what they're
9662 // contained in, so we must dig a little to find an instruction so we
9663 // can tell if they're used inside of the function we're outlining. We
9664 // also replace the original constant expression with a new instruction
9665 // equivalent; an instruction as it allows easy modification in the
9666 // following loop, as we can now know the constant (instruction) is
9667 // owned by our target function and replaceUsesOfWith can now be invoked
9668 // on it (cannot do this with constants it seems). A brand new one also
9669 // allows us to be cautious as it is perhaps possible the old expression
9670 // was used inside of the function but exists and is used externally
9671 // (unlikely by the nature of a Constant, but still).
9672 // NOTE: We cannot remove dead constants that have been rewritten to
9673 // instructions at this stage, we run the risk of breaking later lowering
9674 // by doing so as we could still be in the process of lowering the module
9675 // from MLIR to LLVM-IR and the MLIR lowering may still require the original
9676 // constants we have created rewritten versions of.
9677 if (auto *Const = dyn_cast<Constant>(Input))
9678 convertUsersOfConstantsToInstructions(Const, Func, false);
9679
9680 // Collect users before iterating over them to avoid invalidating the
9681 // iteration in case a user uses Input more than once (e.g. a call
9682 // instruction).
9683 SetVector<User *> Users(Input->users().begin(), Input->users().end());
9684 // Collect all the instructions
9686 if (auto *Instr = dyn_cast<Instruction>(User))
9687 if (Instr->getFunction() == Func)
9688 Instr->replaceUsesOfWith(Input, InputCopy);
9689 };
9690
9691 SmallVector<std::pair<Value *, Value *>> DeferredReplacement;
9692
9693 // Rewrite uses of input valus to parameters.
9694 for (auto InArg : zip(Inputs, ArgRange)) {
9695 Value *Input = std::get<0>(InArg);
9696 Argument &Arg = std::get<1>(InArg);
9697 Value *InputCopy = nullptr;
9698
9699 llvm::OpenMPIRBuilder::InsertPointOrErrorTy AfterIP = ArgAccessorFuncCB(
9700 Arg, Input, InputCopy, AllocaIP, Builder.saveIP(), ExitBB->begin());
9701 if (!AfterIP)
9702 return AfterIP.takeError();
9703 Builder.restoreIP(*AfterIP);
9704 ValueReplacementMap[Input] = std::make_tuple(InputCopy, Arg.getArgNo());
9705
9706 // In certain cases a Global may be set up for replacement, however, this
9707 // Global may be used in multiple arguments to the kernel, just segmented
9708 // apart, for example, if we have a global array, that is sectioned into
9709 // multiple mappings (technically not legal in OpenMP, but there is a case
9710 // in Fortran for Common Blocks where this is neccesary), we will end up
9711 // with GEP's into this array inside the kernel, that refer to the Global
9712 // but are technically separate arguments to the kernel for all intents and
9713 // purposes. If we have mapped a segment that requires a GEP into the 0-th
9714 // index, it will fold into an referal to the Global, if we then encounter
9715 // this folded GEP during replacement all of the references to the
9716 // Global in the kernel will be replaced with the argument we have generated
9717 // that corresponds to it, including any other GEP's that refer to the
9718 // Global that may be other arguments. This will invalidate all of the other
9719 // preceding mapped arguments that refer to the same global that may be
9720 // separate segments. To prevent this, we defer global processing until all
9721 // other processing has been performed.
9724 DeferredReplacement.push_back(std::make_pair(Input, InputCopy));
9725 continue;
9726 }
9727
9729 continue;
9730
9731 ReplaceValue(Input, InputCopy, Func);
9732 }
9733
9734 // Replace all of our deferred Input values, currently just Globals.
9735 for (auto Deferred : DeferredReplacement)
9736 ReplaceValue(std::get<0>(Deferred), std::get<1>(Deferred), Func);
9737
9738 FixupDebugInfoForOutlinedFunction(OMPBuilder, Builder, Func,
9739 ValueReplacementMap);
9740 return Func;
9741}
9742/// Given a task descriptor, TaskWithPrivates, return the pointer to the block
9743/// of pointers containing shared data between the parent task and the created
9744/// task.
9746 IRBuilderBase &Builder,
9747 Value *TaskWithPrivates,
9748 Type *TaskWithPrivatesTy) {
9749
9750 Type *TaskTy = OMPIRBuilder.Task;
9751 LLVMContext &Ctx = Builder.getContext();
9752 Value *TaskT =
9753 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 0);
9754 Value *Shareds = TaskT;
9755 // TaskWithPrivatesTy can be one of the following
9756 // 1. %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9757 // %struct.privates }
9758 // 2. %struct.kmp_task_ompbuilder_t ;; This is simply TaskTy
9759 //
9760 // In the former case, that is when TaskWithPrivatesTy != TaskTy,
9761 // its first member has to be the task descriptor. TaskTy is the type of the
9762 // task descriptor. TaskT is the pointer to the task descriptor. Loading the
9763 // first member of TaskT, gives us the pointer to shared data.
9764 if (TaskWithPrivatesTy != TaskTy)
9765 Shareds = Builder.CreateStructGEP(TaskTy, TaskT, 0);
9766 return Builder.CreateLoad(PointerType::getUnqual(Ctx), Shareds);
9767}
9768/// Create an entry point for a target task with the following.
9769/// It'll have the following signature
9770/// void @.omp_target_task_proxy_func(i32 %thread.id, ptr %task)
9771/// This function is called from emitTargetTask once the
9772/// code to launch the target kernel has been outlined already.
9773/// NumOffloadingArrays is the number of offloading arrays that we need to copy
9774/// into the task structure so that the deferred target task can access this
9775/// data even after the stack frame of the generating task has been rolled
9776/// back. Offloading arrays contain base pointers, pointers, sizes etc
9777/// of the data that the target kernel will access. These in effect are the
9778/// non-empty arrays of pointers held by OpenMPIRBuilder::TargetDataRTArgs.
9780 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI,
9781 StructType *PrivatesTy, StructType *TaskWithPrivatesTy,
9782 const size_t NumOffloadingArrays, const int SharedArgsOperandNo) {
9783
9784 // If NumOffloadingArrays is non-zero, PrivatesTy better not be nullptr.
9785 // This is because PrivatesTy is the type of the structure in which
9786 // we pass the offloading arrays to the deferred target task.
9787 assert((!NumOffloadingArrays || PrivatesTy) &&
9788 "PrivatesTy cannot be nullptr when there are offloadingArrays"
9789 "to privatize");
9790
9791 Module &M = OMPBuilder.M;
9792 // KernelLaunchFunction is the target launch function, i.e.
9793 // the function that sets up kernel arguments and calls
9794 // __tgt_target_kernel to launch the kernel on the device.
9795 //
9796 Function *KernelLaunchFunction = StaleCI->getCalledFunction();
9797
9798 // StaleCI is the CallInst which is the call to the outlined
9799 // target kernel launch function. If there are local live-in values
9800 // that the outlined function uses then these are aggregated into a structure
9801 // which is passed as the second argument. If there are no local live-in
9802 // values or if all values used by the outlined kernel are global variables,
9803 // then there's only one argument, the threadID. So, StaleCI can be
9804 //
9805 // %structArg = alloca { ptr, ptr }, align 8
9806 // %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
9807 // store ptr %20, ptr %gep_, align 8
9808 // %gep_8 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
9809 // store ptr %21, ptr %gep_8, align 8
9810 // call void @_QQmain..omp_par.1(i32 %global.tid.val6, ptr %structArg)
9811 //
9812 // OR
9813 //
9814 // call void @_QQmain..omp_par.1(i32 %global.tid.val6)
9815 LLVMContext &Ctx = StaleCI->getParent()->getContext();
9816
9817 Type *ThreadIDTy = Type::getInt32Ty(Ctx);
9818 Type *TaskPtrTy = OMPBuilder.TaskPtr;
9819 [[maybe_unused]] Type *TaskTy = OMPBuilder.Task;
9820
9821 auto ProxyFnTy =
9822 FunctionType::get(Builder.getVoidTy(), {ThreadIDTy, TaskPtrTy},
9823 /* isVarArg */ false);
9824 auto ProxyFn = Function::Create(ProxyFnTy, GlobalValue::InternalLinkage,
9825 ".omp_target_task_proxy_func", M);
9826 Value *ThreadId = ProxyFn->getArg(0);
9827 Value *TaskWithPrivates = ProxyFn->getArg(1);
9828 ThreadId->setName("thread.id");
9829 TaskWithPrivates->setName("task");
9830
9831 bool HasShareds = SharedArgsOperandNo > 0;
9832 bool HasOffloadingArrays = NumOffloadingArrays > 0;
9833 IRBuilder<>::InsertPointGuard IPG(Builder);
9834 BasicBlock *EntryBB =
9835 BasicBlock::Create(Builder.getContext(), "entry", ProxyFn);
9836 Builder.SetInsertPoint(EntryBB);
9837 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9838
9839 SmallVector<Value *> KernelLaunchArgs;
9840 KernelLaunchArgs.reserve(StaleCI->arg_size());
9841 KernelLaunchArgs.push_back(ThreadId);
9842
9843 if (HasOffloadingArrays) {
9844 assert(TaskTy != TaskWithPrivatesTy &&
9845 "If there are offloading arrays to pass to the target"
9846 "TaskTy cannot be the same as TaskWithPrivatesTy");
9847 (void)TaskTy;
9848 Value *Privates =
9849 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskWithPrivates, 1);
9850 for (unsigned int i = 0; i < NumOffloadingArrays; ++i)
9851 KernelLaunchArgs.push_back(
9852 Builder.CreateStructGEP(PrivatesTy, Privates, i));
9853 }
9854
9855 if (HasShareds) {
9856 auto *ArgStructAlloca =
9857 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgsOperandNo));
9858 assert(ArgStructAlloca &&
9859 "Unable to find the alloca instruction corresponding to arguments "
9860 "for extracted function");
9861 auto *ArgStructType = cast<StructType>(ArgStructAlloca->getAllocatedType());
9862 std::optional<TypeSize> ArgAllocSize =
9863 ArgStructAlloca->getAllocationSize(M.getDataLayout());
9864 assert(ArgStructType && ArgAllocSize &&
9865 "Unable to determine size of arguments for extracted function");
9866 uint64_t StructSize = ArgAllocSize->getFixedValue();
9867
9868 AllocaInst *NewArgStructAlloca =
9869 Builder.CreateAlloca(ArgStructType, nullptr, "structArg");
9870
9871 Value *SharedsSize = Builder.getInt64(StructSize);
9872
9874 OMPBuilder, Builder, TaskWithPrivates, TaskWithPrivatesTy);
9875
9876 Builder.CreateMemCpy(
9877 NewArgStructAlloca, NewArgStructAlloca->getAlign(), LoadShared,
9878 LoadShared->getPointerAlignment(M.getDataLayout()), SharedsSize);
9879 KernelLaunchArgs.push_back(NewArgStructAlloca);
9880 }
9881 OMPBuilder.createRuntimeFunctionCall(KernelLaunchFunction, KernelLaunchArgs);
9882 Builder.CreateRetVoid();
9883 return ProxyFn;
9884}
9886
9887 if (auto *GEP = dyn_cast<GetElementPtrInst>(V))
9888 return GEP->getSourceElementType();
9889 if (auto *Alloca = dyn_cast<AllocaInst>(V))
9890 return Alloca->getAllocatedType();
9891
9892 llvm_unreachable("Unhandled Instruction type");
9893 return nullptr;
9894}
9895// This function returns a struct that has at most two members.
9896// The first member is always %struct.kmp_task_ompbuilder_t, that is the task
9897// descriptor. The second member, if needed, is a struct containing arrays
9898// that need to be passed to the offloaded target kernel. For example,
9899// if .offload_baseptrs, .offload_ptrs and .offload_sizes have to be passed to
9900// the target kernel and their types are [3 x ptr], [3 x ptr] and [3 x i64]
9901// respectively, then the types created by this function are
9902//
9903// %struct.privates = type { [3 x ptr], [3 x ptr], [3 x i64] }
9904// %struct.task_with_privates = type { %struct.kmp_task_ompbuilder_t,
9905// %struct.privates }
9906// %struct.task_with_privates is returned by this function.
9907// If there aren't any offloading arrays to pass to the target kernel,
9908// %struct.kmp_task_ompbuilder_t is returned.
9909static StructType *
9911 ArrayRef<Value *> OffloadingArraysToPrivatize) {
9912
9913 if (OffloadingArraysToPrivatize.empty())
9914 return OMPIRBuilder.Task;
9915
9916 SmallVector<Type *, 4> StructFieldTypes;
9917 for (Value *V : OffloadingArraysToPrivatize) {
9918 assert(V->getType()->isPointerTy() &&
9919 "Expected pointer to array to privatize. Got a non-pointer value "
9920 "instead");
9921 Type *ArrayTy = getOffloadingArrayType(V);
9922 assert(ArrayTy && "ArrayType cannot be nullptr");
9923 StructFieldTypes.push_back(ArrayTy);
9924 }
9925 StructType *PrivatesStructTy =
9926 StructType::create(StructFieldTypes, "struct.privates");
9927 return StructType::create({OMPIRBuilder.Task, PrivatesStructTy},
9928 "struct.task_with_privates");
9929}
9931 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry,
9932 TargetRegionEntryInfo &EntryInfo,
9934 Function *&OutlinedFn, Constant *&OutlinedFnID,
9938 DebugLoc OutlinedFnLoc) {
9939
9940 OpenMPIRBuilder::FunctionGenCallback &&GenerateOutlinedFunction =
9941 [&](StringRef EntryFnName) {
9942 return createOutlinedFunction(OMPBuilder, Builder, DefaultAttrs,
9943 EntryFnName, Inputs, CBFunc,
9944 ArgAccessorFuncCB, OutlinedFnLoc);
9945 };
9946
9947 return OMPBuilder.emitTargetRegionFunction(
9948 EntryInfo, GenerateOutlinedFunction, IsOffloadEntry, OutlinedFn,
9949 OutlinedFnID);
9950}
9951
9953 TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc,
9955 const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs,
9956 bool HasNoWait) {
9957
9958 // The following explains the code-gen scenario for the `target` directive. A
9959 // similar scneario is followed for other device-related directives (e.g.
9960 // `target enter data`) but in similar fashion since we only need to emit task
9961 // that encapsulates the proper runtime call.
9962 //
9963 // When we arrive at this function, the target region itself has been
9964 // outlined into the function OutlinedFn.
9965 // So at ths point, for
9966 // --------------------------------------------------------------
9967 // void user_code_that_offloads(...) {
9968 // omp target depend(..) map(from:a) map(to:b) private(i)
9969 // do i = 1, 10
9970 // a(i) = b(i) + n
9971 // }
9972 //
9973 // --------------------------------------------------------------
9974 //
9975 // we have
9976 //
9977 // --------------------------------------------------------------
9978 //
9979 // void user_code_that_offloads(...) {
9980 // %.offload_baseptrs = alloca [2 x ptr], align 8
9981 // %.offload_ptrs = alloca [2 x ptr], align 8
9982 // %.offload_mappers = alloca [2 x ptr], align 8
9983 // ;; target region has been outlined and now we need to
9984 // ;; offload to it via a target task.
9985 // }
9986 // void outlined_device_function(ptr a, ptr b, ptr n) {
9987 // n = *n_ptr;
9988 // do i = 1, 10
9989 // a(i) = b(i) + n
9990 // }
9991 //
9992 // We have to now do the following
9993 // (i) Make an offloading call to outlined_device_function using the OpenMP
9994 // RTL. See 'kernel_launch_function' in the pseudo code below. This is
9995 // emitted by emitKernelLaunch
9996 // (ii) Create a task entry point function that calls kernel_launch_function
9997 // and is the entry point for the target task. See
9998 // '@.omp_target_task_proxy_func in the pseudocode below.
9999 // (iii) Create a task with the task entry point created in (ii)
10000 //
10001 // That is we create the following
10002 // struct task_with_privates {
10003 // struct kmp_task_ompbuilder_t task_struct;
10004 // struct privates {
10005 // [2 x ptr] ; baseptrs
10006 // [2 x ptr] ; ptrs
10007 // [2 x i64] ; sizes
10008 // }
10009 // }
10010 // void user_code_that_offloads(...) {
10011 // %.offload_baseptrs = alloca [2 x ptr], align 8
10012 // %.offload_ptrs = alloca [2 x ptr], align 8
10013 // %.offload_sizes = alloca [2 x i64], align 8
10014 //
10015 // %structArg = alloca { ptr, ptr, ptr }, align 8
10016 // %strucArg[0] = a
10017 // %strucArg[1] = b
10018 // %strucArg[2] = &n
10019 //
10020 // target_task_with_privates = @__kmpc_omp_target_task_alloc(...,
10021 // sizeof(kmp_task_ompbuilder_t),
10022 // sizeof(structArg),
10023 // @.omp_target_task_proxy_func,
10024 // ...)
10025 // memcpy(target_task_with_privates->task_struct->shareds, %structArg,
10026 // sizeof(structArg))
10027 // memcpy(target_task_with_privates->privates->baseptrs,
10028 // offload_baseptrs, sizeof(offload_baseptrs)
10029 // memcpy(target_task_with_privates->privates->ptrs,
10030 // offload_ptrs, sizeof(offload_ptrs)
10031 // memcpy(target_task_with_privates->privates->sizes,
10032 // offload_sizes, sizeof(offload_sizes)
10033 // dependencies_array = ...
10034 // ;; if nowait not present
10035 // call @__kmpc_omp_wait_deps(..., dependencies_array)
10036 // call @__kmpc_omp_task_begin_if0(...)
10037 // call @ @.omp_target_task_proxy_func(i32 thread_id, ptr
10038 // %target_task_with_privates)
10039 // call @__kmpc_omp_task_complete_if0(...)
10040 // }
10041 //
10042 // define internal void @.omp_target_task_proxy_func(i32 %thread.id,
10043 // ptr %task) {
10044 // %structArg = alloca {ptr, ptr, ptr}
10045 // %task_ptr = getelementptr(%task, 0, 0)
10046 // %shared_data = load (getelementptr %task_ptr, 0, 0)
10047 // mempcy(%structArg, %shared_data, sizeof(%structArg))
10048 //
10049 // %offloading_arrays = getelementptr(%task, 0, 1)
10050 // %offload_baseptrs = getelementptr(%offloading_arrays, 0, 0)
10051 // %offload_ptrs = getelementptr(%offloading_arrays, 0, 1)
10052 // %offload_sizes = getelementptr(%offloading_arrays, 0, 2)
10053 // kernel_launch_function(%thread.id, %offload_baseptrs, %offload_ptrs,
10054 // %offload_sizes, %structArg)
10055 // }
10056 //
10057 // We need the proxy function because the signature of the task entry point
10058 // expected by kmpc_omp_task is always the same and will be different from
10059 // that of the kernel_launch function.
10060 //
10061 // kernel_launch_function is generated by emitKernelLaunch and has the
10062 // always_inline attribute. For this example, it'll look like so:
10063 // void kernel_launch_function(%thread_id, %offload_baseptrs, %offload_ptrs,
10064 // %offload_sizes, %structArg) alwaysinline {
10065 // %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8
10066 // ; load aggregated data from %structArg
10067 // ; setup kernel_args using offload_baseptrs, offload_ptrs and
10068 // ; offload_sizes
10069 // call i32 @__tgt_target_kernel(...,
10070 // outlined_device_function,
10071 // ptr %kernel_args)
10072 // }
10073 // void outlined_device_function(ptr a, ptr b, ptr n) {
10074 // n = *n_ptr;
10075 // do i = 1, 10
10076 // a(i) = b(i) + n
10077 // }
10078 //
10079 BasicBlock *TargetTaskBodyBB =
10080 splitBB(Builder, /*CreateBranch=*/true, "target.task.body");
10081 BasicBlock *TargetTaskAllocaBB =
10082 splitBB(Builder, /*CreateBranch=*/true, "target.task.alloca");
10083
10084 InsertPointTy TargetTaskAllocaIP(TargetTaskAllocaBB->begin());
10085 InsertPointTy TargetTaskBodyIP(TargetTaskBodyBB->begin());
10086
10087 auto OI = std::make_unique<OutlineInfo>();
10088 OI->EntryBB = TargetTaskAllocaBB;
10089 OI->OuterAllocBB = AllocaIP.getNodeParent();
10090
10091 // Add the thread ID argument.
10093 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
10094 Builder, AllocaIP, ToBeDeleted, TargetTaskAllocaIP, "global.tid", false));
10095
10096 // Generate the task body which will subsequently be outlined.
10097 Builder.restoreIP(TargetTaskBodyIP);
10098 if (Error Err = TaskBodyCB(DeviceID, RTLoc, TargetTaskAllocaIP))
10099 return Err;
10100
10101 // The outliner (CodeExtractor) extract a sequence or vector of blocks that
10102 // it is given. These blocks are enumerated by
10103 // OpenMPIRBuilder::OutlineInfo::collectBlocks which expects the OI.ExitBlock
10104 // to be outside the region. In other words, OI.ExitBlock is expected to be
10105 // the start of the region after the outlining. We used to set OI.ExitBlock
10106 // to the InsertBlock after TaskBodyCB is done. This is fine in most cases
10107 // except when the task body is a single basic block. In that case,
10108 // OI.ExitBlock is set to the single task body block and will get left out of
10109 // the outlining process. So, simply create a new empty block to which we
10110 // uncoditionally branch from where TaskBodyCB left off
10111 OI->ExitBB = BasicBlock::Create(Builder.getContext(), "target.task.cont");
10112 emitBlock(OI->ExitBB, Builder.GetInsertBlock()->getParent(),
10113 /*IsFinished=*/true);
10114
10115 SmallVector<Value *, 2> OffloadingArraysToPrivatize;
10116 bool NeedsTargetTask = HasNoWait && DeviceID;
10117 if (NeedsTargetTask) {
10118 for (auto *V :
10119 {RTArgs.BasePointersArray, RTArgs.PointersArray, RTArgs.MappersArray,
10120 RTArgs.MapNamesArray, RTArgs.MapTypesArray, RTArgs.MapTypesArrayEnd,
10121 RTArgs.SizesArray}) {
10123 OffloadingArraysToPrivatize.push_back(V);
10124 OI->ExcludeArgsFromAggregate.push_back(V);
10125 }
10126 }
10127 }
10128 OI->PostOutlineCB = [this, ToBeDeleted, Dependencies, NeedsTargetTask,
10129 DeviceID, OffloadingArraysToPrivatize](
10130 Function &OutlinedFn) mutable {
10131 assert(OutlinedFn.hasOneUse() &&
10132 "there must be a single user for the outlined function");
10133
10134 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
10135
10136 // The first argument of StaleCI is always the thread id.
10137 // The next few arguments are the pointers to offloading arrays
10138 // if any. (see OffloadingArraysToPrivatize)
10139 // Finally, all other local values that are live-in into the outlined region
10140 // end up in a structure whose pointer is passed as the last argument. This
10141 // piece of data is passed in the "shared" field of the task structure. So,
10142 // we know we have to pass shareds to the task if the number of arguments is
10143 // greater than OffloadingArraysToPrivatize.size() + 1 The 1 is for the
10144 // thread id. Further, for safety, we assert that the number of arguments of
10145 // StaleCI is exactly OffloadingArraysToPrivatize.size() + 2
10146 const unsigned int NumStaleCIArgs = StaleCI->arg_size();
10147 bool HasShareds = NumStaleCIArgs > OffloadingArraysToPrivatize.size() + 1;
10148 assert((!HasShareds ||
10149 NumStaleCIArgs == (OffloadingArraysToPrivatize.size() + 2)) &&
10150 "Wrong number of arguments for StaleCI when shareds are present");
10151 int SharedArgOperandNo =
10152 HasShareds ? OffloadingArraysToPrivatize.size() + 1 : 0;
10153
10154 StructType *TaskWithPrivatesTy =
10155 createTaskWithPrivatesTy(*this, OffloadingArraysToPrivatize);
10156 StructType *PrivatesTy = nullptr;
10157
10158 if (!OffloadingArraysToPrivatize.empty())
10159 PrivatesTy =
10160 static_cast<StructType *>(TaskWithPrivatesTy->getElementType(1));
10161
10163 *this, Builder, StaleCI, PrivatesTy, TaskWithPrivatesTy,
10164 OffloadingArraysToPrivatize.size(), SharedArgOperandNo);
10165
10166 LLVM_DEBUG(dbgs() << "Proxy task entry function created: " << *ProxyFn
10167 << "\n");
10168
10169 Builder.SetInsertPoint(StaleCI);
10170
10171 // Gather the arguments for emitting the runtime call.
10172 uint32_t SrcLocStrSize;
10173 Constant *SrcLocStr =
10175 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
10176
10177 // @__kmpc_omp_task_alloc or @__kmpc_omp_target_task_alloc
10178 //
10179 // If `HasNoWait == true`, we call @__kmpc_omp_target_task_alloc to provide
10180 // the DeviceID to the deferred task and also since
10181 // @__kmpc_omp_target_task_alloc creates an untied/async task.
10182 Function *TaskAllocFn =
10183 !NeedsTargetTask
10184 ? getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc)
10186 OMPRTL___kmpc_omp_target_task_alloc);
10187
10188 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
10189 // call.
10190 Value *ThreadID = getOrCreateThreadID(Ident);
10191
10192 // Argument - `sizeof_kmp_task_t` (TaskSize)
10193 // Tasksize refers to the size in bytes of kmp_task_t data structure
10194 // plus any other data to be passed to the target task, if any, which
10195 // is packed into a struct. kmp_task_t and the struct so created are
10196 // packed into a wrapper struct whose type is TaskWithPrivatesTy.
10197 Value *TaskSize = Builder.getInt64(
10198 M.getDataLayout().getTypeStoreSize(TaskWithPrivatesTy));
10199
10200 // Argument - `sizeof_shareds` (SharedsSize)
10201 // SharedsSize refers to the shareds array size in the kmp_task_t data
10202 // structure.
10203 Value *SharedsSize = Builder.getInt64(0);
10204 if (HasShareds) {
10205 auto *ArgStructAlloca =
10206 dyn_cast<AllocaInst>(StaleCI->getArgOperand(SharedArgOperandNo));
10207 assert(ArgStructAlloca &&
10208 "Unable to find the alloca instruction corresponding to arguments "
10209 "for extracted function");
10210 std::optional<TypeSize> ArgAllocSize =
10211 ArgStructAlloca->getAllocationSize(M.getDataLayout());
10212 assert(ArgAllocSize &&
10213 "Unable to determine size of arguments for extracted function");
10214 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
10215 }
10216
10217 // Argument - `flags`
10218 // Task is tied iff (Flags & 1) == 1.
10219 // Task is untied iff (Flags & 1) == 0.
10220 // Task is final iff (Flags & 2) == 2.
10221 // Task is not final iff (Flags & 2) == 0.
10222 // A target task is not final and is untied.
10223 Value *Flags = Builder.getInt32(0);
10224
10225 // Emit the @__kmpc_omp_task_alloc runtime call
10226 // The runtime call returns a pointer to an area where the task captured
10227 // variables must be copied before the task is run (TaskData)
10228 CallInst *TaskData = nullptr;
10229
10230 SmallVector<llvm::Value *> TaskAllocArgs = {
10231 /*loc_ref=*/Ident, /*gtid=*/ThreadID,
10232 /*flags=*/Flags,
10233 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
10234 /*task_func=*/ProxyFn};
10235
10236 if (NeedsTargetTask) {
10237 assert(DeviceID && "Expected non-empty device ID.");
10238 TaskAllocArgs.push_back(DeviceID);
10239 }
10240
10241 TaskData = createRuntimeFunctionCall(TaskAllocFn, TaskAllocArgs);
10242
10243 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
10244 if (HasShareds) {
10245 Value *Shareds = StaleCI->getArgOperand(SharedArgOperandNo);
10247 *this, Builder, TaskData, TaskWithPrivatesTy);
10248 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
10249 SharedsSize);
10250 }
10251 if (!OffloadingArraysToPrivatize.empty()) {
10252 Value *Privates =
10253 Builder.CreateStructGEP(TaskWithPrivatesTy, TaskData, 1);
10254 for (unsigned int i = 0; i < OffloadingArraysToPrivatize.size(); ++i) {
10255 Value *PtrToPrivatize = OffloadingArraysToPrivatize[i];
10256 [[maybe_unused]] Type *ArrayType =
10257 getOffloadingArrayType(PtrToPrivatize);
10258 assert(ArrayType && "ArrayType cannot be nullptr");
10259
10260 Type *ElementType = PrivatesTy->getElementType(i);
10261 assert(ElementType == ArrayType &&
10262 "ElementType should match ArrayType");
10263 (void)ArrayType;
10264
10265 Value *Dst = Builder.CreateStructGEP(PrivatesTy, Privates, i);
10266 Builder.CreateMemCpy(
10267 Dst, Alignment, PtrToPrivatize, Alignment,
10268 Builder.getInt64(M.getDataLayout().getTypeStoreSize(ElementType)));
10269 }
10270 }
10271
10272 Value *DepArray = nullptr;
10273 Value *NumDeps = nullptr;
10274 if (Dependencies.DepArray) {
10275 DepArray = Dependencies.DepArray;
10276 NumDeps = Dependencies.NumDeps;
10277 } else if (!Dependencies.Deps.empty()) {
10278 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
10279 NumDeps = Builder.getInt32(Dependencies.Deps.size());
10280 }
10281
10282 // ---------------------------------------------------------------
10283 // V5.2 13.8 target construct
10284 // If the nowait clause is present, execution of the target task
10285 // may be deferred. If the nowait clause is not present, the target task is
10286 // an included task.
10287 // ---------------------------------------------------------------
10288 // The above means that the lack of a nowait on the target construct
10289 // translates to '#pragma omp task if(0)'
10290 if (!NeedsTargetTask) {
10291 if (DepArray) {
10292 Function *TaskWaitFn =
10293 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
10295 TaskWaitFn,
10296 {/*loc_ref=*/Ident, /*gtid=*/ThreadID,
10297 /*ndeps=*/NumDeps,
10298 /*dep_list=*/DepArray,
10299 /*ndeps_noalias=*/ConstantInt::get(Builder.getInt32Ty(), 0),
10300 /*noalias_dep_list=*/
10302 }
10303 // Included task.
10304 Function *TaskBeginFn =
10305 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
10306 Function *TaskCompleteFn =
10307 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
10308 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
10309 CallInst *CI = createRuntimeFunctionCall(ProxyFn, {ThreadID, TaskData});
10310 CI->setDebugLoc(StaleCI->getDebugLoc());
10311 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
10312 } else if (DepArray) {
10313 // HasNoWait - meaning the task may be deferred. Call
10314 // __kmpc_omp_task_with_deps if there are dependencies,
10315 // else call __kmpc_omp_task
10316 Function *TaskFn =
10317 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
10319 TaskFn,
10320 {Ident, ThreadID, TaskData, NumDeps, DepArray,
10321 ConstantInt::get(Builder.getInt32Ty(), 0),
10323 } else {
10324 // Emit the @__kmpc_omp_task runtime call to spawn the task
10325 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
10326 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
10327 }
10328
10329 Builder.ClearInsertionPoint();
10330 StaleCI->eraseFromParent();
10331 for (Instruction *I : llvm::reverse(ToBeDeleted))
10332 I->eraseFromParent();
10333 };
10334 addOutlineInfo(std::move(OI));
10335
10336 LLVM_DEBUG(dbgs() << "Insert block after emitKernelLaunch = \n"
10337 << *(Builder.GetInsertBlock()) << "\n");
10338 LLVM_DEBUG(dbgs() << "Module after emitKernelLaunch = \n"
10339 << *(Builder.getModule()) << "\n");
10340 return Builder.saveIP();
10341}
10342
10344 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info,
10345 TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo,
10346 CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous,
10347 bool ForEndCall, function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
10348 if (Error Err =
10349 emitOffloadingArrays(AllocaIP, CodeGenIP, CombinedInfo, Info,
10350 CustomMapperCB, IsNonContiguous, DeviceAddrCB))
10351 return Err;
10352 emitOffloadingArraysArgument(Builder, RTArgs, Info, ForEndCall);
10353 return Error::success();
10354}
10355
10356static void emitTargetCall(
10357 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride,
10362 Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID,
10366 const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait,
10367 Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback) {
10368 // Generate a function call to the host fallback implementation of the target
10369 // region. This is called by the host when no offload entry was generated for
10370 // the target region and when the offloading call fails at runtime.
10371 auto &&EmitTargetCallFallbackCB = [&](OpenMPIRBuilder::InsertPointTy IP)
10373 Builder.restoreIP(IP);
10374 // Ensure the host fallback has the same dyn_ptr ABI as the device.
10375 SmallVector<Value *> FallbackArgs(Args.begin(), Args.end());
10376 FallbackArgs.push_back(
10377 Constant::getNullValue(PointerType::getUnqual(Builder.getContext())));
10378 OMPBuilder.createRuntimeFunctionCall(OutlinedFn, FallbackArgs);
10379 return Builder.saveIP();
10380 };
10381
10382 bool HasDependencies = !Dependencies.empty();
10383 bool RequiresOuterTargetTask = HasNoWait || HasDependencies;
10384
10386
10387 auto TaskBodyCB =
10388 [&](Value *DeviceID, Value *RTLoc,
10389 IRBuilderBase::InsertPoint TargetTaskAllocaIP) -> Error {
10390 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10391 // produce any.
10393 // emitKernelLaunch makes the necessary runtime call to offload the
10394 // kernel. We then outline all that code into a separate function
10395 // ('kernel_launch_function' in the pseudo code above). This function is
10396 // then called by the target task proxy function (see
10397 // '@.omp_target_task_proxy_func' in the pseudo code above)
10398 // "@.omp_target_task_proxy_func' is generated by
10399 // emitTargetTaskProxyFunction.
10400 if (OutlinedFnID && DeviceID)
10401 return OMPBuilder.emitKernelLaunch(Builder, OutlinedFnID,
10402 EmitTargetCallFallbackCB, KArgs,
10403 DeviceID, RTLoc, TargetTaskAllocaIP);
10404
10405 // We only need to do the outlining if `DeviceID` is set to avoid calling
10406 // `emitKernelLaunch` if we want to code-gen for the host; e.g. if we are
10407 // generating the `else` branch of an `if` clause.
10408 //
10409 // When OutlinedFnID is set to nullptr, then it's not an offloading call.
10410 // In this case, we execute the host implementation directly.
10411 return EmitTargetCallFallbackCB(OMPBuilder.Builder.saveIP());
10412 }());
10413
10414 OMPBuilder.Builder.restoreIP(AfterIP);
10415 return Error::success();
10416 };
10417
10418 auto &&EmitTargetCallElse =
10419 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10421 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10422 // Assume no error was returned because EmitTargetCallFallbackCB doesn't
10423 // produce any.
10425 if (RequiresOuterTargetTask) {
10426 // Arguments that are intended to be directly forwarded to an
10427 // emitKernelLaunch call are pased as nullptr, since
10428 // OutlinedFnID=nullptr results in that call not being done.
10430 return OMPBuilder.emitTargetTask(TaskBodyCB, /*DeviceID=*/nullptr,
10431 /*RTLoc=*/nullptr, AllocaIP,
10432 Dependencies, EmptyRTArgs, HasNoWait);
10433 }
10434 return EmitTargetCallFallbackCB(Builder.saveIP());
10435 }());
10436
10437 Builder.restoreIP(AfterIP);
10438 return Error::success();
10439 };
10440
10441 auto &&EmitTargetCallThen =
10442 [&](OpenMPIRBuilder::InsertPointTy AllocaIP,
10444 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
10445 Info.HasNoWait = HasNoWait;
10446 OpenMPIRBuilder::MapInfosTy &MapInfo = GenMapInfoCB(Builder.saveIP());
10447
10449 if (Error Err = OMPBuilder.emitOffloadingArraysAndArgs(
10450 AllocaIP, Builder.saveIP(), Info, RTArgs, MapInfo, CustomMapperCB,
10451 /*IsNonContiguous=*/true,
10452 /*ForEndCall=*/false))
10453 return Err;
10454
10455 SmallVector<Value *, 3> NumTeamsC;
10456 for (auto [DefaultVal, RuntimeVal] :
10457 zip_equal(DefaultAttrs.MaxTeams, RuntimeAttrs.MaxTeams))
10458 NumTeamsC.push_back(RuntimeVal ? RuntimeVal
10459 : Builder.getInt32(DefaultVal));
10460
10461 // Calculate number of threads: 0 if no clauses specified, otherwise it is
10462 // the minimum between optional THREAD_LIMIT and NUM_THREADS clauses.
10463 auto InitMaxThreadsClause = [&Builder](Value *Clause) {
10464 if (Clause)
10465 Clause = Builder.CreateIntCast(Clause, Builder.getInt32Ty(),
10466 /*isSigned=*/false);
10467 return Clause;
10468 };
10469 auto CombineMaxThreadsClauses = [&Builder](Value *Clause, Value *&Result) {
10470 if (Clause)
10471 Result =
10472 Result ? Builder.CreateSelect(Builder.CreateICmpULT(Result, Clause),
10473 Result, Clause)
10474 : Clause;
10475 };
10476
10477 // If a multi-dimensional THREAD_LIMIT is set, it is the OMPX_BARE case, so
10478 // the NUM_THREADS clause is overriden by THREAD_LIMIT.
10479 SmallVector<Value *, 3> NumThreadsC;
10480 Value *MaxThreadsClause =
10481 RuntimeAttrs.TeamsThreadLimit.size() == 1
10482 ? InitMaxThreadsClause(RuntimeAttrs.MaxThreads.front())
10483 : nullptr;
10484
10485 for (auto [TeamsVal, TargetVal] : zip_equal(
10486 RuntimeAttrs.TeamsThreadLimit, RuntimeAttrs.TargetThreadLimit)) {
10487 Value *TeamsThreadLimitClause = InitMaxThreadsClause(TeamsVal);
10488 Value *NumThreads = InitMaxThreadsClause(TargetVal);
10489
10490 CombineMaxThreadsClauses(TeamsThreadLimitClause, NumThreads);
10491 CombineMaxThreadsClauses(MaxThreadsClause, NumThreads);
10492
10493 NumThreadsC.push_back(NumThreads ? NumThreads : Builder.getInt32(0));
10494 }
10495
10496 unsigned NumTargetItems = Info.NumberOfPtrs;
10497 Value *RTLoc = RTLocOverride;
10498 if (!RTLoc) {
10499 uint32_t SrcLocStrSize;
10500 Constant *SrcLocStr =
10501 OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
10502 RTLoc = OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize,
10503 llvm::omp::IdentFlag(0), 0);
10504 }
10505
10506 Value *TripCount = RuntimeAttrs.LoopTripCount
10507 ? Builder.CreateIntCast(RuntimeAttrs.LoopTripCount,
10508 Builder.getInt64Ty(),
10509 /*isSigned=*/false)
10510 : Builder.getInt64(0);
10511
10512 // Request zero groupprivate bytes by default.
10513 if (!DynCGroupMem)
10514 DynCGroupMem = Builder.getInt32(0);
10515
10517 NumTargetItems, RTArgs, TripCount, NumTeamsC, NumThreadsC, DynCGroupMem,
10518 HasNoWait, /*StrictBlocks=*/false, /*StrictThreads=*/false,
10519 DynCGroupMemFallback);
10520
10521 // Assume no error was returned because TaskBodyCB and
10522 // EmitTargetCallFallbackCB don't produce any.
10524 // The presence of certain clauses on the target directive require the
10525 // explicit generation of the target task.
10526 if (RequiresOuterTargetTask)
10527 return OMPBuilder.emitTargetTask(TaskBodyCB, RuntimeAttrs.DeviceID,
10528 RTLoc, AllocaIP, Dependencies,
10529 KArgs.RTArgs, Info.HasNoWait);
10530
10531 return OMPBuilder.emitKernelLaunch(
10532 Builder, OutlinedFnID, EmitTargetCallFallbackCB, KArgs,
10533 RuntimeAttrs.DeviceID, RTLoc, AllocaIP);
10534 }());
10535
10536 Builder.restoreIP(AfterIP);
10537 return Error::success();
10538 };
10539
10540 // If we don't have an ID for the target region, it means an offload entry
10541 // wasn't created. In this case we just run the host fallback directly and
10542 // ignore any potential 'if' clauses.
10543 if (!OutlinedFnID) {
10544 cantFail(EmitTargetCallElse(AllocaIP, Builder.saveIP(), DeallocBlocks));
10545 return;
10546 }
10547
10548 // If there's no 'if' clause, only generate the kernel launch code path.
10549 if (!IfCond) {
10550 cantFail(EmitTargetCallThen(AllocaIP, Builder.saveIP(), DeallocBlocks));
10551 return;
10552 }
10553
10554 cantFail(OMPBuilder.emitIfClause(IfCond, EmitTargetCallThen,
10555 EmitTargetCallElse, AllocaIP));
10556}
10557
10559 const LocationDescription &Loc, bool IsOffloadEntry, InsertPointTy AllocaIP,
10560 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
10561 TargetDataInfo &Info, TargetRegionEntryInfo &EntryInfo,
10562 const TargetKernelDefaultAttrs &DefaultAttrs,
10563 const TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond,
10564 SmallVectorImpl<Value *> &Inputs, GenMapInfoCallbackTy GenMapInfoCB,
10567 CustomMapperCallbackTy CustomMapperCB, const DependenciesInfo &Dependencies,
10568 bool HasNowait, Value *DynCGroupMem,
10569 OMPDynGroupprivateFallbackType DynCGroupMemFallback, DebugLoc OutlinedFnLoc,
10570 Value *RTLocOverride) {
10571
10572 if (!updateToLocation(Loc))
10573 return InsertPointTy();
10574
10575 Builder.restoreIP(CodeGenIP);
10576
10577 Function *OutlinedFn;
10578 Constant *OutlinedFnID = nullptr;
10579 // The target region is outlined into its own function. The LLVM IR for
10580 // the target region itself is generated using the callbacks CBFunc
10581 // and ArgAccessorFuncCB
10583 *this, Builder, IsOffloadEntry, EntryInfo, DefaultAttrs, OutlinedFn,
10584 OutlinedFnID, Inputs, CBFunc, ArgAccessorFuncCB, OutlinedFnLoc))
10585 return Err;
10586
10587 // If we are not on the target device, then we need to generate code
10588 // to make a remote call (offload) to the previously outlined function
10589 // that represents the target region. Do that now.
10590 if (!Config.isTargetDevice())
10591 emitTargetCall(*this, Builder, RTLocOverride, AllocaIP, DeallocBlocks, Info,
10592 DefaultAttrs, RuntimeAttrs, IfCond, OutlinedFn, OutlinedFnID,
10593 Inputs, GenMapInfoCB, CustomMapperCB, Dependencies,
10594 HasNowait, DynCGroupMem, DynCGroupMemFallback);
10595 return Builder.saveIP();
10596}
10597
10598std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
10599 StringRef FirstSeparator,
10600 StringRef Separator) {
10601 SmallString<128> Buffer;
10602 llvm::raw_svector_ostream OS(Buffer);
10603 StringRef Sep = FirstSeparator;
10604 for (StringRef Part : Parts) {
10605 OS << Sep << Part;
10606 Sep = Separator;
10607 }
10608 return OS.str().str();
10609}
10610
10611std::string
10613 return OpenMPIRBuilder::getNameWithSeparators(Parts, Config.firstSeparator(),
10614 Config.separator());
10615}
10616
10618 Type *Ty, const StringRef &Name, std::optional<unsigned> AddressSpace) {
10619 auto &Elem = *InternalVars.try_emplace(Name, nullptr).first;
10620 if (Elem.second) {
10621 assert(Elem.second->getValueType() == Ty &&
10622 "OMP internal variable has different type than requested");
10623 } else {
10624 // TODO: investigate the appropriate linkage type used for the global
10625 // variable for possibly changing that to internal or private, or maybe
10626 // create different versions of the function for different OMP internal
10627 // variables.
10628 const DataLayout &DL = M.getDataLayout();
10629 // TODO: Investigate why AMDGPU expects AS 0 for globals even though the
10630 // default global AS is 1.
10631 // See double-target-call-with-declare-target.f90 and
10632 // declare-target-vars-in-target-region.f90 libomptarget
10633 // tests.
10634 unsigned AddressSpaceVal = AddressSpace ? *AddressSpace
10635 : M.getTargetTriple().isAMDGPU()
10636 ? 0
10637 : DL.getDefaultGlobalsAddressSpace();
10638 auto Linkage = this->M.getTargetTriple().isWasm()
10641 auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, Linkage,
10642 Constant::getNullValue(Ty), Elem.first(),
10643 /*InsertBefore=*/nullptr,
10644 GlobalValue::NotThreadLocal, AddressSpaceVal);
10645 const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
10646 const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpaceVal);
10647 GV->setAlignment(std::max(TypeAlign, PtrAlign));
10648 Elem.second = GV;
10649 }
10650
10651 return Elem.second;
10652}
10653
10654Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
10655 std::string Prefix = Twine("gomp_critical_user_", CriticalName).str();
10656 std::string Name = getNameWithSeparators({Prefix, "var"}, ".", ".");
10657 return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
10658}
10659
10661 LLVMContext &Ctx = Builder.getContext();
10662 Value *Null =
10663 Constant::getNullValue(PointerType::getUnqual(BasePtr->getContext()));
10664 Value *SizeGep =
10665 Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
10666 Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
10667 return SizePtrToInt;
10668}
10669
10672 std::string VarName) {
10673 llvm::Constant *MaptypesArrayInit =
10674 llvm::ConstantDataArray::get(M.getContext(), Mappings);
10675 auto *MaptypesArrayGlobal = new llvm::GlobalVariable(
10676 M, MaptypesArrayInit->getType(),
10677 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MaptypesArrayInit,
10678 VarName);
10679 MaptypesArrayGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
10680 return MaptypesArrayGlobal;
10681}
10682
10684 InsertPointTy AllocaIP,
10685 unsigned NumOperands,
10686 struct MapperAllocas &MapperAllocas) {
10687 if (!updateToLocation(Loc))
10688 return;
10689
10690 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10691 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10692 Builder.restoreIP(AllocaIP);
10693 AllocaInst *ArgsBase = Builder.CreateAlloca(
10694 ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
10695 AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
10696 ".offload_ptrs");
10697 AllocaInst *ArgSizes = Builder.CreateAlloca(
10698 ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
10700 MapperAllocas.ArgsBase = ArgsBase;
10701 MapperAllocas.Args = Args;
10702 MapperAllocas.ArgSizes = ArgSizes;
10703}
10704
10706 Function *MapperFunc, Value *SrcLocInfo,
10707 Value *MaptypesArg, Value *MapnamesArg,
10709 int64_t DeviceID, unsigned NumOperands) {
10710 if (!updateToLocation(Loc))
10711 return;
10712
10713 auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
10714 auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
10715 Value *ArgsBaseGEP =
10716 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
10717 {Builder.getInt32(0), Builder.getInt32(0)});
10718 Value *ArgsGEP =
10719 Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
10720 {Builder.getInt32(0), Builder.getInt32(0)});
10721 Value *ArgSizesGEP =
10722 Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
10723 {Builder.getInt32(0), Builder.getInt32(0)});
10724 Value *NullPtr =
10725 Constant::getNullValue(PointerType::getUnqual(Int8Ptr->getContext()));
10726 createRuntimeFunctionCall(MapperFunc, {SrcLocInfo, Builder.getInt64(DeviceID),
10727 Builder.getInt32(NumOperands),
10728 ArgsBaseGEP, ArgsGEP, ArgSizesGEP,
10729 MaptypesArg, MapnamesArg, NullPtr});
10730}
10731
10733 TargetDataRTArgs &RTArgs,
10734 TargetDataInfo &Info,
10735 bool ForEndCall) {
10736 assert((!ForEndCall || Info.separateBeginEndCalls()) &&
10737 "expected region end call to runtime only when end call is separate");
10738 auto UnqualPtrTy = PointerType::getUnqual(M.getContext());
10739 auto VoidPtrTy = UnqualPtrTy;
10740 auto VoidPtrPtrTy = UnqualPtrTy;
10741 auto Int64Ty = Type::getInt64Ty(M.getContext());
10742 auto Int64PtrTy = UnqualPtrTy;
10743
10744 if (!Info.NumberOfPtrs) {
10745 RTArgs.BasePointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10746 RTArgs.PointersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10747 RTArgs.SizesArray = ConstantPointerNull::get(Int64PtrTy);
10748 RTArgs.MapTypesArray = ConstantPointerNull::get(Int64PtrTy);
10749 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10750 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10751 return;
10752 }
10753
10754 RTArgs.BasePointersArray = Builder.CreateConstInBoundsGEP2_32(
10755 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs),
10756 Info.RTArgs.BasePointersArray,
10757 /*Idx0=*/0, /*Idx1=*/0);
10758 RTArgs.PointersArray = Builder.CreateConstInBoundsGEP2_32(
10759 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray,
10760 /*Idx0=*/0,
10761 /*Idx1=*/0);
10762 RTArgs.SizesArray = Builder.CreateConstInBoundsGEP2_32(
10763 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
10764 /*Idx0=*/0, /*Idx1=*/0);
10765 RTArgs.MapTypesArray = Builder.CreateConstInBoundsGEP2_32(
10766 ArrayType::get(Int64Ty, Info.NumberOfPtrs),
10767 ForEndCall && Info.RTArgs.MapTypesArrayEnd ? Info.RTArgs.MapTypesArrayEnd
10768 : Info.RTArgs.MapTypesArray,
10769 /*Idx0=*/0,
10770 /*Idx1=*/0);
10771
10772 // Only emit the mapper information arrays if debug information is
10773 // requested.
10774 if (!Info.EmitDebug)
10775 RTArgs.MapNamesArray = ConstantPointerNull::get(VoidPtrPtrTy);
10776 else
10777 RTArgs.MapNamesArray = Builder.CreateConstInBoundsGEP2_32(
10778 ArrayType::get(VoidPtrTy, Info.NumberOfPtrs), Info.RTArgs.MapNamesArray,
10779 /*Idx0=*/0,
10780 /*Idx1=*/0);
10781 // If there is no user-defined mapper, set the mapper array to nullptr to
10782 // avoid an unnecessary data privatization
10783 if (!Info.HasMapper)
10784 RTArgs.MappersArray = ConstantPointerNull::get(VoidPtrPtrTy);
10785 else
10786 RTArgs.MappersArray =
10787 Builder.CreatePointerCast(Info.RTArgs.MappersArray, VoidPtrPtrTy);
10788}
10789
10791 InsertPointTy CodeGenIP,
10792 MapInfosTy &CombinedInfo,
10793 TargetDataInfo &Info) {
10795 CombinedInfo.NonContigInfo;
10796
10797 // Build an array of struct descriptor_dim and then assign it to
10798 // offload_args.
10799 //
10800 // struct descriptor_dim {
10801 // uint64_t offset;
10802 // uint64_t count;
10803 // uint64_t stride
10804 // };
10805 Type *Int64Ty = Builder.getInt64Ty();
10807 M.getContext(), ArrayRef<Type *>({Int64Ty, Int64Ty, Int64Ty}),
10808 "struct.descriptor_dim");
10809
10810 enum { OffsetFD = 0, CountFD, StrideFD };
10811 // We need two index variable here since the size of "Dims" is the same as
10812 // the size of Components, however, the size of offset, count, and stride is
10813 // equal to the size of base declaration that is non-contiguous.
10814 for (unsigned I = 0, L = 0, E = NonContigInfo.Dims.size(); I < E; ++I) {
10815 // Skip emitting ir if dimension size is 1 since it cannot be
10816 // non-contiguous.
10817 if (NonContigInfo.Dims[I] == 1)
10818 continue;
10819 Builder.restoreIP(AllocaIP);
10820 ArrayType *ArrayTy = ArrayType::get(DimTy, NonContigInfo.Dims[I]);
10821 AllocaInst *DimsAddr =
10822 Builder.CreateAlloca(ArrayTy, /* ArraySize = */ nullptr, "dims");
10823 Builder.restoreIP(CodeGenIP);
10824 for (unsigned II = 0, EE = NonContigInfo.Dims[I]; II < EE; ++II) {
10825 unsigned RevIdx = EE - II - 1;
10826 Value *DimsLVal = Builder.CreateInBoundsGEP(
10827 ArrayTy, DimsAddr, {Builder.getInt64(0), Builder.getInt64(II)});
10828 // Offset
10829 Value *OffsetLVal = Builder.CreateStructGEP(DimTy, DimsLVal, OffsetFD);
10830 Builder.CreateAlignedStore(
10831 NonContigInfo.Offsets[L][RevIdx], OffsetLVal,
10832 M.getDataLayout().getPrefTypeAlign(OffsetLVal->getType()));
10833 // Count
10834 Value *CountLVal = Builder.CreateStructGEP(DimTy, DimsLVal, CountFD);
10835 Builder.CreateAlignedStore(
10836 NonContigInfo.Counts[L][RevIdx], CountLVal,
10837 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10838 // Stride
10839 Value *StrideLVal = Builder.CreateStructGEP(DimTy, DimsLVal, StrideFD);
10840 Builder.CreateAlignedStore(
10841 NonContigInfo.Strides[L][RevIdx], StrideLVal,
10842 M.getDataLayout().getPrefTypeAlign(CountLVal->getType()));
10843 }
10844 // args[I] = &dims
10845 Builder.restoreIP(CodeGenIP);
10846 Value *DAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
10847 DimsAddr, Builder.getPtrTy());
10848 Value *P = Builder.CreateConstInBoundsGEP2_32(
10849 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs),
10850 Info.RTArgs.PointersArray, 0, I);
10851 Builder.CreateAlignedStore(
10852 DAddr, P, M.getDataLayout().getPrefTypeAlign(Builder.getPtrTy()));
10853 ++L;
10854 }
10855}
10856
10857void OpenMPIRBuilder::emitUDMapperArrayInitOrDel(
10858 Function *MapperFn, Value *MapperHandle, Value *Base, Value *Begin,
10859 Value *Size, Value *MapType, Value *MapName, TypeSize ElementSize,
10860 BasicBlock *ExitBB, bool IsInit) {
10861 StringRef Prefix = IsInit ? ".init" : ".del";
10862
10863 // Evaluate if this is an array section.
10865 M.getContext(), createPlatformSpecificName({"omp.array", Prefix}));
10866 Value *IsArray =
10867 Builder.CreateICmpSGT(Size, Builder.getInt64(1), "omp.arrayinit.isarray");
10868 Value *DeleteBit = Builder.CreateAnd(
10869 MapType,
10870 Builder.getInt64(
10871 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10872 OpenMPOffloadMappingFlags::OMP_MAP_DELETE)));
10873 Value *DeleteCond;
10874 Value *Cond;
10875 if (IsInit) {
10876 // base != begin?
10877 Value *BaseIsBegin = Builder.CreateICmpNE(Base, Begin);
10878 Cond = Builder.CreateOr(IsArray, BaseIsBegin);
10879 DeleteCond = Builder.CreateIsNull(
10880 DeleteBit,
10881 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10882 } else {
10883 Cond = IsArray;
10884 DeleteCond = Builder.CreateIsNotNull(
10885 DeleteBit,
10886 createPlatformSpecificName({"omp.array", Prefix, ".delete"}));
10887 }
10888 Cond = Builder.CreateAnd(Cond, DeleteCond);
10889 Builder.CreateCondBr(Cond, BodyBB, ExitBB);
10890
10891 emitBlock(BodyBB, MapperFn);
10892 // Get the array size by multiplying element size and element number (i.e., \p
10893 // Size).
10894 Value *ArraySize = Builder.CreateNUWMul(Size, Builder.getInt64(ElementSize));
10895 // Remove OMP_MAP_TO and OMP_MAP_FROM from the map type, so that it achieves
10896 // memory allocation/deletion purpose only.
10897 Value *MapTypeArg = Builder.CreateAnd(
10898 MapType,
10899 Builder.getInt64(
10900 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10901 OpenMPOffloadMappingFlags::OMP_MAP_TO |
10902 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
10903 MapTypeArg = Builder.CreateOr(
10904 MapTypeArg,
10905 Builder.getInt64(
10906 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
10907 OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT)));
10908
10909 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
10910 // data structure.
10911 Value *OffloadingArgs[] = {MapperHandle, Base, Begin,
10912 ArraySize, MapTypeArg, MapName};
10914 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
10915 OffloadingArgs);
10916}
10917
10920 llvm::Value *BeginArg)>
10921 GenMapInfoCB,
10922 Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB,
10923 bool PreserveMemberOfFlags, bool PropagatePresentToPointee) {
10924 SmallVector<Type *> Params;
10925 Params.emplace_back(Builder.getPtrTy());
10926 Params.emplace_back(Builder.getPtrTy());
10927 Params.emplace_back(Builder.getPtrTy());
10928 Params.emplace_back(Builder.getInt64Ty());
10929 Params.emplace_back(Builder.getInt64Ty());
10930 Params.emplace_back(Builder.getPtrTy());
10931
10932 auto *FnTy =
10933 FunctionType::get(Builder.getVoidTy(), Params, /* IsVarArg */ false);
10934
10935 Function *MapperFn =
10937 MapperFn->addFnAttr(Attribute::NoInline);
10938 MapperFn->addFnAttr(Attribute::NoUnwind);
10939 MapperFn->addParamAttr(0, Attribute::NoUndef);
10940 MapperFn->addParamAttr(1, Attribute::NoUndef);
10941 MapperFn->addParamAttr(2, Attribute::NoUndef);
10942 MapperFn->addParamAttr(3, Attribute::NoUndef);
10943 MapperFn->addParamAttr(4, Attribute::NoUndef);
10944 MapperFn->addParamAttr(5, Attribute::NoUndef);
10945
10946 // Start the mapper function code generation.
10947 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", MapperFn);
10949 Builder.SetInsertPoint(EntryBB);
10950 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
10951
10952 Value *MapperHandle = MapperFn->getArg(0);
10953 Value *BaseIn = MapperFn->getArg(1);
10954 Value *BeginIn = MapperFn->getArg(2);
10955 Value *Size = MapperFn->getArg(3);
10956 Value *MapType = MapperFn->getArg(4);
10957 Value *MapName = MapperFn->getArg(5);
10958
10959 // Compute the starting and end addresses of array elements.
10960 // Prepare common arguments for array initiation and deletion.
10961 // Convert the size in bytes into the number of array elements.
10962 TypeSize ElementSize = M.getDataLayout().getTypeStoreSize(ElemTy);
10963 Size = Builder.CreateExactUDiv(Size, Builder.getInt64(ElementSize));
10964 Value *PtrBegin = BeginIn;
10965 Value *PtrEnd = Builder.CreateGEP(ElemTy, PtrBegin, Size);
10966
10967 // Emit array initiation if this is an array section and \p MapType indicates
10968 // that memory allocation is required.
10969 BasicBlock *HeadBB = BasicBlock::Create(M.getContext(), "omp.arraymap.head");
10970 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
10971 MapType, MapName, ElementSize, HeadBB,
10972 /*IsInit=*/true);
10973
10974 // Emit a for loop to iterate through SizeArg of elements and map all of them.
10975
10976 // Emit the loop header block.
10977 emitBlock(HeadBB, MapperFn);
10978 BasicBlock *BodyBB = BasicBlock::Create(M.getContext(), "omp.arraymap.body");
10979 BasicBlock *DoneBB = BasicBlock::Create(M.getContext(), "omp.done");
10980 // Evaluate whether the initial condition is satisfied.
10981 Value *IsEmpty =
10982 Builder.CreateICmpEQ(PtrBegin, PtrEnd, "omp.arraymap.isempty");
10983 Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB);
10984
10985 // Emit the loop body block.
10986 emitBlock(BodyBB, MapperFn);
10987 BasicBlock *LastBB = BodyBB;
10988 PHINode *PtrPHI =
10989 Builder.CreatePHI(PtrBegin->getType(), 2, "omp.arraymap.ptrcurrent");
10990 PtrPHI->addIncoming(PtrBegin, HeadBB);
10991
10992 // Get map clause information. Fill up the arrays with all mapped variables.
10993 MapInfosOrErrorTy Info = GenMapInfoCB(Builder.saveIP(), PtrPHI, BeginIn);
10994 if (!Info)
10995 return Info.takeError();
10996
10997 // Call the runtime API __tgt_mapper_num_components to get the number of
10998 // pre-existing components.
10999 Value *OffloadingArgs[] = {MapperHandle};
11000 Value *PreviousSize = createRuntimeFunctionCall(
11001 getOrCreateRuntimeFunction(M, OMPRTL___tgt_mapper_num_components),
11002 OffloadingArgs);
11003 Value *ShiftedPreviousSize =
11004 Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
11005
11006 // Fill up the runtime mapper handle for all components.
11007 for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
11008 Value *CurBaseArg = Info->BasePointers[I];
11009 Value *CurBeginArg = Info->Pointers[I];
11010 Value *CurSizeArg = Info->Sizes[I];
11011 Value *CurNameArg = Info->Names.size()
11012 ? Info->Names[I]
11013 : Constant::getNullValue(Builder.getPtrTy());
11014
11015 Value *OriMapType = Builder.getInt64(
11016 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11017 Info->Types[I]));
11018 auto RawType =
11019 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11020 Info->Types[I]);
11021 constexpr uint64_t MemberOfMask =
11022 static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
11023 constexpr uint64_t AttachBit =
11024 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11025 OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
11026
11027 // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
11028 // current array element (N = __tgt_mapper_num_components() at loop body
11029 // start).
11030 //
11031 // Example 1:
11032 // struct S { int x; int *p; };
11033 //
11034 // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
11035 // use: S arr[2]; ... map(arr)
11036 // entries per element:
11037 //
11038 // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
11039 // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
11040 // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
11041 //
11042 // Example 2:
11043 // struct S1 { int x; int y; };
11044 // struct S2 { int z; S1 *s1p; };
11045 //
11046 // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
11047 // s2.s1p->y)
11048 // use: S2 arr[2]; ... map(arr)
11049 // entries per element:
11050 //
11051 // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
11052 // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
11053 // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
11054 // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
11055 // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
11056 //
11057 // x/y carry inner MEMBER_OF(2)
11058 // which is shifted by N to become MEMBER_OF(N+2).
11059 //
11060 // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
11061 // the combined ALLOC entry for the s1p->x..y block, and the individual
11062 // x/y entries that are MEMBER_OF that block, all describe storage
11063 // reached through the attach ptr arr[i].s1p.
11064 //
11065 // Entries of the following kinds do NOT receive a new outer MEMBER_OF
11066 // linking them to the parent struct:
11067 //
11068 // * (*) Entries with HasAttachPtr: they represent pointee data that
11069 // occupies a different storage block than the struct being mapped, so
11070 // they are not a member of it. They may still be MEMBER_OF an entry
11071 // within that pointee block, in which case those pre-existing bits are
11072 // shifted -- see (***).
11073 // * (**) ATTACH entries: they are not a member of anything — they just
11074 // link a ptr to its ptee.
11075 // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
11076 // its pre-shaped entries already carry their final MEMBER_OF bits.
11077 // TODO: set HasAttachPtr from Flang for entries whose storage is the
11078 // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
11079 // it.
11080 //
11081 // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
11082 // s1p->x/y entries above), those bits are still shifted by N.
11083 Value *MemberMapType;
11084 if (PreserveMemberOfFlags || (RawType & AttachBit) ||
11085 Info->HasAttachPtr[I]) {
11086 if (RawType & MemberOfMask)
11087 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
11088 else
11089 MemberMapType = OriMapType;
11090 } else {
11091 MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
11092 }
11093
11094 // Combine the map type inherited from user-defined mapper with that
11095 // specified in the program. According to the OMP_MAP_TO and OMP_MAP_FROM
11096 // bits of the \a MapType, which is the input argument of the mapper
11097 // function, the following code will set the OMP_MAP_TO and OMP_MAP_FROM
11098 // bits of MemberMapType.
11099 // [OpenMP 5.0], 1.2.6. map-type decay.
11100 // | alloc | to | from | tofrom | release | delete
11101 // ----------------------------------------------------------
11102 // alloc | alloc | alloc | alloc | alloc | release | delete
11103 // to | alloc | to | alloc | to | release | delete
11104 // from | alloc | alloc | from | from | release | delete
11105 // tofrom | alloc | to | from | tofrom | release | delete
11106 Value *LeftToFrom = Builder.CreateAnd(
11107 MapType,
11108 Builder.getInt64(
11109 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11110 OpenMPOffloadMappingFlags::OMP_MAP_TO |
11111 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
11112 BasicBlock *AllocBB = BasicBlock::Create(M.getContext(), "omp.type.alloc");
11113 BasicBlock *AllocElseBB =
11114 BasicBlock::Create(M.getContext(), "omp.type.alloc.else");
11115 BasicBlock *ToBB = BasicBlock::Create(M.getContext(), "omp.type.to");
11116 BasicBlock *ToElseBB =
11117 BasicBlock::Create(M.getContext(), "omp.type.to.else");
11118 BasicBlock *FromBB = BasicBlock::Create(M.getContext(), "omp.type.from");
11119 BasicBlock *EndBB = BasicBlock::Create(M.getContext(), "omp.type.end");
11120 Value *IsAlloc = Builder.CreateIsNull(LeftToFrom);
11121 Builder.CreateCondBr(IsAlloc, AllocBB, AllocElseBB);
11122 // In case of alloc, clear OMP_MAP_TO and OMP_MAP_FROM.
11123 emitBlock(AllocBB, MapperFn);
11124 Value *AllocMapType = Builder.CreateAnd(
11125 MemberMapType,
11126 Builder.getInt64(
11127 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11128 OpenMPOffloadMappingFlags::OMP_MAP_TO |
11129 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
11130 Builder.CreateBr(EndBB);
11131 emitBlock(AllocElseBB, MapperFn);
11132 Value *IsTo = Builder.CreateICmpEQ(
11133 LeftToFrom,
11134 Builder.getInt64(
11135 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11136 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
11137 Builder.CreateCondBr(IsTo, ToBB, ToElseBB);
11138 // In case of to, clear OMP_MAP_FROM.
11139 emitBlock(ToBB, MapperFn);
11140 Value *ToMapType = Builder.CreateAnd(
11141 MemberMapType,
11142 Builder.getInt64(
11143 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11144 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
11145 Builder.CreateBr(EndBB);
11146 emitBlock(ToElseBB, MapperFn);
11147 Value *IsFrom = Builder.CreateICmpEQ(
11148 LeftToFrom,
11149 Builder.getInt64(
11150 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11151 OpenMPOffloadMappingFlags::OMP_MAP_FROM)));
11152 Builder.CreateCondBr(IsFrom, FromBB, EndBB);
11153 // In case of from, clear OMP_MAP_TO.
11154 emitBlock(FromBB, MapperFn);
11155 Value *FromMapType = Builder.CreateAnd(
11156 MemberMapType,
11157 Builder.getInt64(
11158 ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11159 OpenMPOffloadMappingFlags::OMP_MAP_TO)));
11160 // In case of tofrom, do nothing.
11161 emitBlock(EndBB, MapperFn);
11162 LastBB = EndBB;
11163 PHINode *CurMapType =
11164 Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
11165 CurMapType->addIncoming(AllocMapType, AllocBB);
11166 CurMapType->addIncoming(ToMapType, ToBB);
11167 CurMapType->addIncoming(FromMapType, FromBB);
11168 CurMapType->addIncoming(MemberMapType, ToElseBB);
11169
11170 // Propagate map-type-modifying bits from the outer map clause to each map
11171 // inserted by the mapper.
11172 //
11173 // OpenMP 6.0:281:34: The effect of the mapper modifier is to remove the
11174 // list item from the map clause and to apply the clauses specified in the
11175 // declared mapper to the construct on which the map clause appears...
11176 // If any modifier with the map-type-modifying property appears in the map
11177 // clause then the effect is as if that modifier appears in each map clause
11178 // specified in the declared mapper.
11179 //
11180 // Map-type-modifying bits: ALWAYS, DELETE, CLOSE, PRESENT.
11181 //
11182 // ALWAYS/DELETE/CLOSE are propagated to every (non-ATTACH) entry.
11183 //
11184 // PRESENT is propagated only to entries that have an attach ptr
11185 // (HasAttachPtr): the pointee data, which occupies a different storage
11186 // block than the struct being mapped and so is not covered by the
11187 // present-check on the struct's own storage. A present modifier on the
11188 // outer clause must still require that pointee to be present on the device.
11189 //
11190 // This is gated on \p PropagatePresentToPointee (set by callers only for
11191 // OpenMP >= 6.0). Before 6.0 the present modifier is treated as not
11192 // applying to the pointee: the spec committee confirmed the divergence
11193 // between the present "motion" modifier (to/from) and the present map-type
11194 // modifier (map) was unintentional, to be fixed as an OpenMP 6.0 erratum,
11195 // so for 5.2 present is ignored for the pointee for both map and to/from.
11196 //
11197 // TODO: PRESENT should also be propagated to the struct's own members
11198 // (e.g. the s.x, s.y of map(present, mapper(id): s)) so that an absent
11199 // member triggers the present-check. We cannot do that yet: while pointer
11200 // members are mapped with PTR_AND_OBJ, a single combined entry allocates
11201 // the whole struct (including the pointer's storage), so propagating
11202 // PRESENT to it would wrongly require the pointer's pointee to be present.
11203 // Enable member propagation once Clang stops emitting PTR_AND_OBJ and uses
11204 // attach-style maps throughout.
11205 uint64_t ModifierBits =
11206 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11207 OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
11208 OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
11209 OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
11210 if (PropagatePresentToPointee && Info->HasAttachPtr[I])
11211 ModifierBits |=
11212 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11213 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11214 Value *ImportedModifierBits =
11215 Builder.CreateAnd(MapType, Builder.getInt64(ModifierBits));
11216 Value *CurMapTypeWithModifiers = Builder.CreateOr(
11217 CurMapType, ImportedModifierBits, "omp.maptype.with.modifiers");
11218
11219 // ATTACH entries must not receive map-type-modifying bits: ATTACH|ALWAYS is
11220 // reserved for the attach(always) map-type modifier, and other modifier
11221 // bits (DELETE, CLOSE) have no meaning for an ATTACH entry.
11222 Value *FinalMapType =
11223 (RawType & AttachBit) ? CurMapType : CurMapTypeWithModifiers;
11224
11225 Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
11226 CurSizeArg, FinalMapType, CurNameArg};
11227
11228 auto ChildMapperFn = CustomMapperCB(I);
11229 if (!ChildMapperFn)
11230 return ChildMapperFn.takeError();
11231 if (*ChildMapperFn) {
11232 // Call the corresponding mapper function.
11233 createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
11234 ->setDoesNotThrow();
11235 } else {
11236 // Call the runtime API __tgt_push_mapper_component to fill up the runtime
11237 // data structure.
11239 getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
11240 OffloadingArgs);
11241 }
11242 }
11243
11244 // Update the pointer to point to the next element that needs to be mapped,
11245 // and check whether we have mapped all elements.
11246 Value *PtrNext = Builder.CreateConstGEP1_32(ElemTy, PtrPHI, /*Idx0=*/1,
11247 "omp.arraymap.next");
11248 PtrPHI->addIncoming(PtrNext, LastBB);
11249 Value *IsDone = Builder.CreateICmpEQ(PtrNext, PtrEnd, "omp.arraymap.isdone");
11250 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), "omp.arraymap.exit");
11251 Builder.CreateCondBr(IsDone, ExitBB, BodyBB);
11252
11253 emitBlock(ExitBB, MapperFn);
11254 // Emit array deletion if this is an array section and \p MapType indicates
11255 // that deletion is required.
11256 emitUDMapperArrayInitOrDel(MapperFn, MapperHandle, BaseIn, BeginIn, Size,
11257 MapType, MapName, ElementSize, DoneBB,
11258 /*IsInit=*/false);
11259
11260 // Emit the function exit block.
11261 emitBlock(DoneBB, MapperFn, /*IsFinished=*/true);
11262
11263 Builder.CreateRetVoid();
11264 return MapperFn;
11265}
11266
11268 InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo,
11269 TargetDataInfo &Info, CustomMapperCallbackTy CustomMapperCB,
11270 bool IsNonContiguous,
11271 function_ref<void(unsigned int, Value *)> DeviceAddrCB) {
11272
11273 // Reset the array information.
11274 Info.clearArrayInfo();
11275 Info.NumberOfPtrs = CombinedInfo.BasePointers.size();
11276
11277 if (Info.NumberOfPtrs == 0)
11278 return Error::success();
11279
11280 Builder.restoreIP(AllocaIP);
11281 // Detect if we have any capture size requiring runtime evaluation of the
11282 // size so that a constant array could be eventually used.
11283 ArrayType *PointerArrayType =
11284 ArrayType::get(Builder.getPtrTy(), Info.NumberOfPtrs);
11285
11286 Info.RTArgs.BasePointersArray = Builder.CreateAlloca(
11287 PointerArrayType, /* ArraySize = */ nullptr, ".offload_baseptrs");
11288
11289 Info.RTArgs.PointersArray = Builder.CreateAlloca(
11290 PointerArrayType, /* ArraySize = */ nullptr, ".offload_ptrs");
11291 AllocaInst *MappersArray = Builder.CreateAlloca(
11292 PointerArrayType, /* ArraySize = */ nullptr, ".offload_mappers");
11293 Info.RTArgs.MappersArray = MappersArray;
11294
11295 // If we don't have any VLA types or other types that require runtime
11296 // evaluation, we can use a constant array for the map sizes, otherwise we
11297 // need to fill up the arrays as we do for the pointers.
11298 Type *Int64Ty = Builder.getInt64Ty();
11299 SmallVector<Constant *> ConstSizes(CombinedInfo.Sizes.size(),
11300 ConstantInt::get(Int64Ty, 0));
11301 SmallBitVector RuntimeSizes(CombinedInfo.Sizes.size());
11302 for (unsigned I = 0, E = CombinedInfo.Sizes.size(); I < E; ++I) {
11303 bool IsNonContigEntry =
11304 IsNonContiguous &&
11305 (static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11306 CombinedInfo.Types[I] &
11307 OpenMPOffloadMappingFlags::OMP_MAP_NON_CONTIG) != 0);
11308 // For NON_CONTIG entries, ArgSizes stores the dimension count (number of
11309 // descriptor_dim records), not the byte size.
11310 if (IsNonContigEntry) {
11311 assert(I < CombinedInfo.NonContigInfo.Dims.size() &&
11312 "Index must be in-bounds for NON_CONTIG Dims array");
11313 const uint64_t DimCount = CombinedInfo.NonContigInfo.Dims[I];
11314 assert(DimCount > 0 && "NON_CONTIG DimCount must be > 0");
11315 ConstSizes[I] = ConstantInt::get(Int64Ty, DimCount);
11316 continue;
11317 }
11318 if (auto *CI = dyn_cast<Constant>(CombinedInfo.Sizes[I])) {
11319 if (!isa<ConstantExpr>(CI) && !isa<GlobalValue>(CI)) {
11320 ConstSizes[I] = CI;
11321 continue;
11322 }
11323 }
11324 RuntimeSizes.set(I);
11325 }
11326
11327 if (RuntimeSizes.all()) {
11328 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11329 Info.RTArgs.SizesArray = Builder.CreateAlloca(
11330 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11331 restoreIPandDebugLoc(Builder, CodeGenIP);
11332 } else {
11333 auto *SizesArrayInit = ConstantArray::get(
11334 ArrayType::get(Int64Ty, ConstSizes.size()), ConstSizes);
11335 std::string Name = createPlatformSpecificName({"offload_sizes"});
11336 auto *SizesArrayGbl =
11337 new GlobalVariable(M, SizesArrayInit->getType(), /*isConstant=*/true,
11338 GlobalValue::PrivateLinkage, SizesArrayInit, Name);
11339 SizesArrayGbl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
11340
11341 if (!RuntimeSizes.any()) {
11342 Info.RTArgs.SizesArray = SizesArrayGbl;
11343 } else {
11344 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11345 Align OffloadSizeAlign = M.getDataLayout().getABIIntegerTypeAlignment(64);
11346 ArrayType *SizeArrayType = ArrayType::get(Int64Ty, Info.NumberOfPtrs);
11347 AllocaInst *Buffer = Builder.CreateAlloca(
11348 SizeArrayType, /* ArraySize = */ nullptr, ".offload_sizes");
11349 Buffer->setAlignment(OffloadSizeAlign);
11350 restoreIPandDebugLoc(Builder, CodeGenIP);
11351 Builder.CreateMemCpy(
11352 Buffer, M.getDataLayout().getPrefTypeAlign(Buffer->getType()),
11353 SizesArrayGbl, OffloadSizeAlign,
11354 Builder.getIntN(
11355 IndexSize,
11356 Buffer->getAllocationSize(M.getDataLayout())->getFixedValue()));
11357
11358 Info.RTArgs.SizesArray = Buffer;
11359 }
11360 restoreIPandDebugLoc(Builder, CodeGenIP);
11361 }
11362
11363 // The map types are always constant so we don't need to generate code to
11364 // fill arrays. Instead, we create an array constant.
11366 for (auto mapFlag : CombinedInfo.Types)
11367 Mapping.push_back(
11368 static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11369 mapFlag));
11370 std::string MaptypesName = createPlatformSpecificName({"offload_maptypes"});
11371 auto *MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11372 Info.RTArgs.MapTypesArray = MapTypesArrayGbl;
11373
11374 // The information types are only built if provided.
11375 if (!CombinedInfo.Names.empty()) {
11376 auto *MapNamesArrayGbl = createOffloadMapnames(
11377 CombinedInfo.Names, createPlatformSpecificName({"offload_mapnames"}));
11378 Info.RTArgs.MapNamesArray = MapNamesArrayGbl;
11379 Info.EmitDebug = true;
11380 } else {
11381 Info.RTArgs.MapNamesArray =
11383 Info.EmitDebug = false;
11384 }
11385
11386 // If there's a present map type modifier, it must not be applied to the end
11387 // of a region, so generate a separate map type array in that case.
11388 if (Info.separateBeginEndCalls()) {
11389 bool EndMapTypesDiffer = false;
11390 for (uint64_t &Type : Mapping) {
11391 if (Type & static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11392 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT)) {
11393 Type &= ~static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
11394 OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
11395 EndMapTypesDiffer = true;
11396 }
11397 }
11398 if (EndMapTypesDiffer) {
11399 MapTypesArrayGbl = createOffloadMaptypes(Mapping, MaptypesName);
11400 Info.RTArgs.MapTypesArrayEnd = MapTypesArrayGbl;
11401 }
11402 }
11403
11404 PointerType *PtrTy = Builder.getPtrTy();
11405 for (unsigned I = 0; I < Info.NumberOfPtrs; ++I) {
11406 Value *BPVal = CombinedInfo.BasePointers[I];
11407 Value *BP = Builder.CreateConstInBoundsGEP2_32(
11408 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.BasePointersArray,
11409 0, I);
11410 Builder.CreateAlignedStore(BPVal, BP,
11411 M.getDataLayout().getPrefTypeAlign(PtrTy));
11412
11413 if (Info.requiresDevicePointerInfo()) {
11414 if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Pointer) {
11415 CodeGenIP = Builder.saveIP();
11416 Builder.restoreIP(AllocaIP);
11417 Info.DevicePtrInfoMap[BPVal] = {BP, Builder.CreateAlloca(PtrTy)};
11418 restoreIPandDebugLoc(Builder, CodeGenIP);
11419 if (DeviceAddrCB)
11420 DeviceAddrCB(I, Info.DevicePtrInfoMap[BPVal].second);
11421 } else if (CombinedInfo.DevicePointers[I] == DeviceInfoTy::Address) {
11422 Info.DevicePtrInfoMap[BPVal] = {BP, BP};
11423 if (DeviceAddrCB)
11424 DeviceAddrCB(I, BP);
11425 }
11426 }
11427
11428 Value *PVal = CombinedInfo.Pointers[I];
11429 Value *P = Builder.CreateConstInBoundsGEP2_32(
11430 ArrayType::get(PtrTy, Info.NumberOfPtrs), Info.RTArgs.PointersArray, 0,
11431 I);
11432 // TODO: Check alignment correct.
11433 Builder.CreateAlignedStore(PVal, P,
11434 M.getDataLayout().getPrefTypeAlign(PtrTy));
11435
11436 if (RuntimeSizes.test(I)) {
11437 Value *S = Builder.CreateConstInBoundsGEP2_32(
11438 ArrayType::get(Int64Ty, Info.NumberOfPtrs), Info.RTArgs.SizesArray,
11439 /*Idx0=*/0,
11440 /*Idx1=*/I);
11441 Builder.CreateAlignedStore(Builder.CreateIntCast(CombinedInfo.Sizes[I],
11442 Int64Ty,
11443 /*isSigned=*/true),
11444 S, M.getDataLayout().getPrefTypeAlign(PtrTy));
11445 }
11446 // Fill up the mapper array.
11447 unsigned IndexSize = M.getDataLayout().getIndexSizeInBits(0);
11448 Value *MFunc = ConstantPointerNull::get(PtrTy);
11449
11450 auto CustomMFunc = CustomMapperCB(I);
11451 if (!CustomMFunc)
11452 return CustomMFunc.takeError();
11453 if (*CustomMFunc)
11454 MFunc = Builder.CreatePointerCast(*CustomMFunc, PtrTy);
11455
11456 Value *MAddr = Builder.CreateInBoundsGEP(
11457 PointerArrayType, MappersArray,
11458 {Builder.getIntN(IndexSize, 0), Builder.getIntN(IndexSize, I)});
11459 Builder.CreateAlignedStore(
11460 MFunc, MAddr, M.getDataLayout().getPrefTypeAlign(MAddr->getType()));
11461 }
11462
11463 if (!IsNonContiguous || CombinedInfo.NonContigInfo.Offsets.empty() ||
11464 Info.NumberOfPtrs == 0)
11465 return Error::success();
11466 emitNonContiguousDescriptor(AllocaIP, CodeGenIP, CombinedInfo, Info);
11467 return Error::success();
11468}
11469
11471 BasicBlock *CurBB = Builder.GetInsertBlock();
11472
11473 if (!CurBB || CurBB->hasTerminator()) {
11474 // If there is no insert point or the previous block is already
11475 // terminated, don't touch it.
11476 } else {
11477 // Otherwise, create a fall-through branch.
11478 Builder.CreateBr(Target);
11479 }
11480
11481 Builder.ClearInsertionPoint();
11482}
11483
11485 bool IsFinished) {
11486 BasicBlock *CurBB = Builder.GetInsertBlock();
11487
11488 // Fall out of the current block (if necessary).
11489 emitBranch(BB);
11490
11491 if (IsFinished && BB->use_empty()) {
11492 BB->eraseFromParent();
11493 return;
11494 }
11495
11496 // Place the block after the current block, if possible, or else at
11497 // the end of the function.
11498 if (CurBB && CurBB->getParent())
11499 CurFn->insert(std::next(CurBB->getIterator()), BB);
11500 else
11501 CurFn->insert(CurFn->end(), BB);
11502 Builder.SetInsertPoint(BB);
11503}
11504
11506 BodyGenCallbackTy ElseGen,
11507 InsertPointTy AllocaIP,
11508 ArrayRef<BasicBlock *> DeallocBlocks) {
11509 // If the condition constant folds and can be elided, try to avoid emitting
11510 // the condition and the dead arm of the if/else.
11511 if (auto *CI = dyn_cast<ConstantInt>(Cond)) {
11512 auto CondConstant = CI->getSExtValue();
11513 if (CondConstant)
11514 return ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11515
11516 return ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
11517 }
11518
11519 Function *CurFn = Builder.GetInsertBlock()->getParent();
11520
11521 // Otherwise, the condition did not fold, or we couldn't elide it. Just
11522 // emit the conditional branch.
11523 BasicBlock *ThenBlock = BasicBlock::Create(M.getContext(), "omp_if.then");
11524 BasicBlock *ElseBlock = BasicBlock::Create(M.getContext(), "omp_if.else");
11525 BasicBlock *ContBlock = BasicBlock::Create(M.getContext(), "omp_if.end");
11526 Builder.CreateCondBr(Cond, ThenBlock, ElseBlock);
11527 // Emit the 'then' code.
11528 emitBlock(ThenBlock, CurFn);
11529 if (Error Err = ThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11530 return Err;
11531 emitBranch(ContBlock);
11532 // Emit the 'else' code if present.
11533 // There is no need to emit line number for unconditional branch.
11534 emitBlock(ElseBlock, CurFn);
11535 if (Error Err = ElseGen(AllocaIP, Builder.saveIP(), DeallocBlocks))
11536 return Err;
11537 // There is no need to emit line number for unconditional branch.
11538 emitBranch(ContBlock);
11539 // Emit the continuation block for code after the if.
11540 emitBlock(ContBlock, CurFn, /*IsFinished=*/true);
11541 return Error::success();
11542}
11543
11544bool OpenMPIRBuilder::checkAndEmitFlushAfterAtomic(
11545 const LocationDescription &Loc, llvm::AtomicOrdering AO, AtomicKind AK) {
11548 "Unexpected Atomic Ordering.");
11549
11550 bool Flush = false;
11552
11553 switch (AK) {
11554 case Read:
11557 FlushAO = AtomicOrdering::Acquire;
11558 Flush = true;
11559 }
11560 break;
11561 case Write:
11562 case Compare:
11563 case Update:
11566 FlushAO = AtomicOrdering::Release;
11567 Flush = true;
11568 }
11569 break;
11570 case Capture:
11571 switch (AO) {
11573 FlushAO = AtomicOrdering::Acquire;
11574 Flush = true;
11575 break;
11577 FlushAO = AtomicOrdering::Release;
11578 Flush = true;
11579 break;
11583 Flush = true;
11584 break;
11585 default:
11586 // do nothing - leave silently.
11587 break;
11588 }
11589 }
11590
11591 if (Flush) {
11592 // Currently Flush RT call still doesn't take memory_ordering, so for when
11593 // that happens, this tries to do the resolution of which atomic ordering
11594 // to use with but issue the flush call
11595 // TODO: pass `FlushAO` after memory ordering support is added
11596 (void)FlushAO;
11597 emitFlush(Loc);
11598 }
11599
11600 // for AO == AtomicOrdering::Monotonic and all other case combinations
11601 // do nothing
11602 return Flush;
11603}
11604
11608 AtomicOrdering AO, InsertPointTy AllocaIP) {
11609 if (!updateToLocation(Loc))
11610 return Loc.IP;
11611
11612 assert(X.Var->getType()->isPointerTy() &&
11613 "OMP Atomic expects a pointer to target memory");
11614 Type *XElemTy = X.ElemTy;
11615 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11616 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11617 "OMP atomic read expected a scalar type");
11618
11619 Value *XRead = nullptr;
11620
11621 if (XElemTy->isIntegerTy()) {
11622 LoadInst *XLD =
11623 Builder.CreateLoad(XElemTy, X.Var, X.IsVolatile, "omp.atomic.read");
11624 XLD->setAtomic(AO);
11625 XRead = cast<Value>(XLD);
11626 } else if (XElemTy->isStructTy()) {
11627 // FIXME: Add checks to ensure __atomic_load is emitted iff the
11628 // target does not support `atomicrmw` of the size of the struct
11629 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11630 OldVal->setAtomic(AO);
11631 const DataLayout &DL = OldVal->getDataLayout();
11632 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11633 OpenMPIRBuilder::AtomicInfo atomicInfo(
11634 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11635 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11636 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11637 XRead = AtomicLoadRes.first;
11638 OldVal->eraseFromParent();
11639 } else {
11640 // We need to perform atomic op as integer
11641 IntegerType *IntCastTy =
11642 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11643 LoadInst *XLoad =
11644 Builder.CreateLoad(IntCastTy, X.Var, X.IsVolatile, "omp.atomic.load");
11645 XLoad->setAtomic(AO);
11646 if (XElemTy->isFloatingPointTy()) {
11647 XRead = Builder.CreateBitCast(XLoad, XElemTy, "atomic.flt.cast");
11648 } else {
11649 XRead = Builder.CreateIntToPtr(XLoad, XElemTy, "atomic.ptr.cast");
11650 }
11651 }
11652 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Read);
11653 Builder.CreateStore(XRead, V.Var, V.IsVolatile);
11654 return Builder.saveIP();
11655}
11656
11659 AtomicOpValue &X, Value *Expr,
11660 AtomicOrdering AO, InsertPointTy AllocaIP) {
11661 if (!updateToLocation(Loc))
11662 return Loc.IP;
11663
11664 assert(X.Var->getType()->isPointerTy() &&
11665 "OMP Atomic expects a pointer to target memory");
11666 Type *XElemTy = X.ElemTy;
11667 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11668 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11669 "OMP atomic write expected a scalar type");
11670
11671 if (XElemTy->isIntegerTy()) {
11672 StoreInst *XSt = Builder.CreateStore(Expr, X.Var, X.IsVolatile);
11673 XSt->setAtomic(AO);
11674 } else if (XElemTy->isStructTy()) {
11675 LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
11676 const DataLayout &DL = OldVal->getDataLayout();
11677 unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
11678 OpenMPIRBuilder::AtomicInfo atomicInfo(
11679 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11680 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
11681 atomicInfo.EmitAtomicStoreLibcall(AO, Expr);
11682 OldVal->eraseFromParent();
11683 } else {
11684 // We need to bitcast and perform atomic op as integers
11685 IntegerType *IntCastTy =
11686 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11687 Value *ExprCast =
11688 Builder.CreateBitCast(Expr, IntCastTy, "atomic.src.int.cast");
11689 StoreInst *XSt = Builder.CreateStore(ExprCast, X.Var, X.IsVolatile);
11690 XSt->setAtomic(AO);
11691 }
11692
11693 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Write);
11694 return Builder.saveIP();
11695}
11696
11699 Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
11700 AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
11701 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11702 assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
11703 if (!updateToLocation(Loc))
11704 return Loc.IP;
11705
11706 LLVM_DEBUG({
11707 Type *XTy = X.Var->getType();
11708 assert(XTy->isPointerTy() &&
11709 "OMP Atomic expects a pointer to target memory");
11710 Type *XElemTy = X.ElemTy;
11711 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11712 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11713 "OMP atomic update expected a scalar or struct type");
11714 });
11715
11716 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11717 AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp, X.IsVolatile,
11718 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11719 if (!AtomicResult)
11720 return AtomicResult.takeError();
11721 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
11722 return Builder.saveIP();
11723}
11724
11725// FIXME: Duplicating AtomicExpand
11726Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
11727 AtomicRMWInst::BinOp RMWOp) {
11728 switch (RMWOp) {
11729 case AtomicRMWInst::Add:
11730 return Builder.CreateAdd(Src1, Src2);
11731 case AtomicRMWInst::Sub:
11732 return Builder.CreateSub(Src1, Src2);
11733 case AtomicRMWInst::And:
11734 return Builder.CreateAnd(Src1, Src2);
11736 return Builder.CreateNeg(Builder.CreateAnd(Src1, Src2));
11737 case AtomicRMWInst::Or:
11738 return Builder.CreateOr(Src1, Src2);
11739 case AtomicRMWInst::Xor:
11740 return Builder.CreateXor(Src1, Src2);
11741 case AtomicRMWInst::Max:
11742 return Builder.CreateBinaryIntrinsic(Intrinsic::smax, Src1, Src2);
11743 case AtomicRMWInst::Min:
11744 return Builder.CreateBinaryIntrinsic(Intrinsic::smin, Src1, Src2);
11746 return Builder.CreateBinaryIntrinsic(Intrinsic::umax, Src1, Src2);
11748 return Builder.CreateBinaryIntrinsic(Intrinsic::umin, Src1, Src2);
11750 return Builder.CreateFAdd(Src1, Src2);
11752 return Builder.CreateFSub(Src1, Src2);
11754 return Builder.CreateMaxNum(Src1, Src2);
11756 return Builder.CreateMinNum(Src1, Src2);
11758 return Builder.CreateMaximum(Src1, Src2);
11760 return Builder.CreateMinimum(Src1, Src2);
11769 llvm_unreachable("Unsupported atomic update operation");
11770 }
11771 llvm_unreachable("Unsupported atomic update operation");
11772}
11773
11775 // Loads cannot use Release or AcquireRelease ordering. This load is
11776 // just the initial value for the cmpxchg loop; the cmpxchg itself
11777 // retains the original ordering.
11778 AtomicOrdering LoadAO = AO;
11779
11780 if (AO == AtomicOrdering::Release) {
11782 } else if (AO == AtomicOrdering::AcquireRelease) {
11783 LoadAO = AtomicOrdering::Acquire;
11784 }
11785
11786 return LoadAO;
11787}
11788
11789Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
11790 InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
11792 AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
11793 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11794 // TODO: handle the case where XElemTy is not byte-sized or not a power of 2.
11795 bool emitRMWOp = false;
11796 switch (RMWOp) {
11797 case AtomicRMWInst::Add:
11798 case AtomicRMWInst::And:
11800 case AtomicRMWInst::Or:
11801 case AtomicRMWInst::Xor:
11803 case AtomicRMWInst::Max:
11804 case AtomicRMWInst::Min:
11812 emitRMWOp = XElemTy;
11813 break;
11814 case AtomicRMWInst::Sub:
11816 emitRMWOp = (IsXBinopExpr && XElemTy);
11817 break;
11818 default:
11819 emitRMWOp = false;
11820 }
11821 emitRMWOp &=
11822 (XElemTy->isIntegerTy() || XElemTy->isFloatTy() || XElemTy->isDoubleTy());
11823
11824 std::pair<Value *, Value *> Res;
11825 if (emitRMWOp) {
11826 AtomicRMWInst *RMWInst =
11827 Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
11828 if (IsIgnoreDenormalMode)
11829 RMWInst->setMetadata(llvm::LLVMContext::MD_atomic_ignore_denormal_mode,
11830 llvm::MDNode::get(Builder.getContext(), {}));
11831 if (T.isAMDGPU()) {
11832 if (!IsFineGrainedMemory)
11833 RMWInst->setMetadata("amdgpu.no.fine.grained.memory",
11834 llvm::MDNode::get(Builder.getContext(), {}));
11835 if (!IsRemoteMemory)
11836 RMWInst->setMetadata("amdgpu.no.remote.memory",
11837 llvm::MDNode::get(Builder.getContext(), {}));
11838 }
11839 Res.first = RMWInst;
11840 // not needed except in case of postfix captures. Generate anyway for
11841 // consistency with the else part. Will be removed with any DCE pass.
11842 // AtomicRMWInst::Xchg does not have a coressponding instruction.
11843 if (RMWOp == AtomicRMWInst::Xchg)
11844 Res.second = Res.first;
11845 else
11846 Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
11847 } else if (XElemTy->isStructTy()) {
11848 LoadInst *OldVal =
11849 Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
11851 OldVal->setAtomic(LoadAO);
11852 const DataLayout &LoadDL = OldVal->getDataLayout();
11853 unsigned LoadSize = LoadDL.getTypeStoreSize(XElemTy);
11854
11855 OpenMPIRBuilder::AtomicInfo atomicInfo(
11856 &Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
11857 OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X);
11858 auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
11859 BasicBlock *CurBB = Builder.GetInsertBlock();
11860 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11861 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11862 BasicBlock *ExitBB =
11863 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11864 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11865 X->getName() + ".atomic.cont");
11866 ContBB->getTerminator()->eraseFromParent();
11867 Builder.restoreIP(AllocaIP);
11868 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11869 NewAtomicAddr->setName(X->getName() + "x.new.val");
11870 Builder.SetInsertPoint(ContBB);
11871 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11872 PHI->addIncoming(AtomicLoadRes.first, CurBB);
11873 Value *OldExprVal = PHI;
11874 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11875 if (!CBResult)
11876 return CBResult.takeError();
11877 Value *Upd = *CBResult;
11878 Builder.CreateStore(Upd, NewAtomicAddr);
11881 auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
11882 AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
11883 LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
11884 PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
11885 Builder.CreateCondBr(Result.second, ExitBB, ContBB);
11886 OldVal->eraseFromParent();
11887 Res.first = OldExprVal;
11888 Res.second = Upd;
11889
11890 if (UnreachableInst *ExitTI =
11892 CurBBTI->eraseFromParent();
11893 Builder.SetInsertPoint(ExitBB);
11894 } else {
11895 Builder.SetInsertPoint(ExitTI);
11896 }
11897 } else {
11898 IntegerType *IntCastTy =
11899 IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());
11900 LoadInst *OldVal =
11901 Builder.CreateLoad(IntCastTy, X, X->getName() + ".atomic.load");
11903 OldVal->setAtomic(LoadAO);
11904 // CurBB
11905 // | /---\
11906 // ContBB |
11907 // | \---/
11908 // ExitBB
11909 BasicBlock *CurBB = Builder.GetInsertBlock();
11910 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
11911 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
11912 BasicBlock *ExitBB =
11913 CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
11914 BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
11915 X->getName() + ".atomic.cont");
11916 ContBB->getTerminator()->eraseFromParent();
11917 Builder.restoreIP(AllocaIP);
11918 AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
11919 NewAtomicAddr->setName(X->getName() + "x.new.val");
11920 Builder.SetInsertPoint(ContBB);
11921 llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
11922 PHI->addIncoming(OldVal, CurBB);
11923 bool IsIntTy = XElemTy->isIntegerTy();
11924 Value *OldExprVal = PHI;
11925 if (!IsIntTy) {
11926 if (XElemTy->isFloatingPointTy()) {
11927 OldExprVal = Builder.CreateBitCast(PHI, XElemTy,
11928 X->getName() + ".atomic.fltCast");
11929 } else {
11930 OldExprVal = Builder.CreateIntToPtr(PHI, XElemTy,
11931 X->getName() + ".atomic.ptrCast");
11932 }
11933 }
11934
11935 Expected<Value *> CBResult = UpdateOp(OldExprVal, Builder);
11936 if (!CBResult)
11937 return CBResult.takeError();
11938 Value *Upd = *CBResult;
11939 Builder.CreateStore(Upd, NewAtomicAddr);
11940 LoadInst *DesiredVal = Builder.CreateLoad(IntCastTy, NewAtomicAddr);
11943 AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
11944 X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
11945 Result->setVolatile(VolatileX);
11946 Value *PreviousVal = Builder.CreateExtractValue(Result, /*Idxs=*/0);
11947 Value *SuccessFailureVal = Builder.CreateExtractValue(Result, /*Idxs=*/1);
11948 PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
11949 Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
11950
11951 Res.first = OldExprVal;
11952 Res.second = Upd;
11953
11954 // set Insertion point in exit block
11955 if (UnreachableInst *ExitTI =
11957 CurBBTI->eraseFromParent();
11958 Builder.SetInsertPoint(ExitBB);
11959 } else {
11960 Builder.SetInsertPoint(ExitTI);
11961 }
11962 }
11963
11964 return Res;
11965}
11966
11969 AtomicOpValue &V, Value *Expr, AtomicOrdering AO,
11970 AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp,
11971 bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr,
11972 bool IsIgnoreDenormalMode, bool IsFineGrainedMemory, bool IsRemoteMemory) {
11973 if (!updateToLocation(Loc))
11974 return Loc.IP;
11975
11976 LLVM_DEBUG({
11977 Type *XTy = X.Var->getType();
11978 assert(XTy->isPointerTy() &&
11979 "OMP Atomic expects a pointer to target memory");
11980 Type *XElemTy = X.ElemTy;
11981 assert((XElemTy->isFloatingPointTy() || XElemTy->isIntegerTy() ||
11982 XElemTy->isPointerTy() || XElemTy->isStructTy()) &&
11983 "OMP atomic capture expected a scalar or struct type");
11984 });
11985
11986 // If UpdateExpr is 'x' updated with some `expr` not based on 'x',
11987 // 'x' is simply atomically rewritten with 'expr'.
11988 AtomicRMWInst::BinOp AtomicOp = (UpdateExpr ? RMWOp : AtomicRMWInst::Xchg);
11989 Expected<std::pair<Value *, Value *>> AtomicResult = emitAtomicUpdate(
11990 AllocaIP, X.Var, X.ElemTy, Expr, AO, AtomicOp, UpdateOp, X.IsVolatile,
11991 IsXBinopExpr, IsIgnoreDenormalMode, IsFineGrainedMemory, IsRemoteMemory);
11992 if (!AtomicResult)
11993 return AtomicResult.takeError();
11994 Value *CapturedVal =
11995 (IsPostfixUpdate ? AtomicResult->first : AtomicResult->second);
11996 Builder.CreateStore(CapturedVal, V.Var, V.IsVolatile);
11997
11998 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Capture);
11999 return Builder.saveIP();
12000}
12001
12005 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
12006 bool IsFailOnly, bool IsWeak) {
12007
12009 return createAtomicCompare(Loc, X, V, R, E, D, AO, Op, IsXBinopExpr,
12010 IsPostfixUpdate, IsFailOnly, Failure, IsWeak);
12011}
12012
12016 omp::OMPAtomicCompareOp Op, bool IsXBinopExpr, bool IsPostfixUpdate,
12017 bool IsFailOnly, AtomicOrdering Failure, bool IsWeak) {
12018
12019 if (!updateToLocation(Loc))
12020 return Loc.IP;
12021
12022 assert(X.Var->getType()->isPointerTy() &&
12023 "OMP atomic expects a pointer to target memory");
12024 // compare capture
12025 if (V.Var) {
12026 assert(V.Var->getType()->isPointerTy() && "v.var must be of pointer type");
12027 assert(V.ElemTy == X.ElemTy && "x and v must be of same type");
12028 }
12029
12030 bool IsInteger = E->getType()->isIntegerTy();
12031
12032 if (Op == OMPAtomicCompareOp::EQ) {
12033 // OldValue and SuccessOrFail are set below and used in the shared V.Var /
12034 // R.Var handling.
12035 Value *OldValue = nullptr;
12036 Value *SuccessOrFail = nullptr;
12037
12038 if (!IsInteger && HandleFPNegZero) {
12039 // IEEE 754 special cases for cmpxchg (which is bitwise):
12040 // 1. -0.0 == +0.0 but they have different bit patterns.
12041 // 2. NaN != NaN but identical NaN bit patterns would match.
12042 //
12043 // CurBB:
12044 // %e_int = bitcast E to intN
12045 // %d_int = bitcast D to intN
12046 // %x_curr = load atomic intN, X
12047 // %x_fp = bitcast %x_curr to FP
12048 // %e_is_nan = fcmp uno E, E
12049 // %x_is_nan = fcmp uno %x_fp, %x_fp
12050 // %either_nan = or %e_is_nan, %x_is_nan
12051 // br %either_nan, NaNBB, NotNaNBB
12052 // NaNBB: ; NaN == anything is always false
12053 // br ExitBB
12054 // NotNaNBB:
12055 // %x_is_zero = fcmp oeq %x_fp, 0.0
12056 // %e_is_zero = fcmp oeq E, 0.0
12057 // %both_zero = and %x_is_zero, %e_is_zero
12058 // br %both_zero, ZeroBB, NormalBB
12059 // ZeroBB: ; both ±0.0 → x = d
12060 // cmpxchg X, %x_curr, %d_int
12061 // br ExitBB
12062 // NormalBB: ; original path
12063 // cmpxchg X, %e_int, %d_int
12064 // br ExitBB
12065 // ExitBB:
12066 // phi merge
12067 IntegerType *IntCastTy =
12068 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
12069 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
12070 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
12071
12072 // Load X atomically.
12073 LoadInst *XCurr = Builder.CreateLoad(IntCastTy, X.Var,
12074 X.Var->getName() + ".atomic.load");
12076 Value *XFP = Builder.CreateBitCast(XCurr, X.ElemTy);
12077
12078 // IEEE 754: NaN != NaN, but cmpxchg would succeed if E and X have
12079 // the same NaN bit pattern. Skip cmpxchg when either is NaN.
12080 Value *EIsNaN = Builder.CreateFCmpUNO(E, E, "atomic.e.isnan");
12081 Value *XIsNaN = Builder.CreateFCmpUNO(XFP, XFP, "atomic.x.isnan");
12082 Value *EitherNaN = Builder.CreateOr(EIsNaN, XIsNaN, "atomic.either.nan");
12083
12084 BasicBlock *CurBB = Builder.GetInsertBlock();
12085 Function *F = CurBB->getParent();
12086 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
12087 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
12088 BasicBlock *ExitBB =
12089 CurBB->splitBasicBlock(CurBBTI, X.Var->getName() + ".atomic.exit");
12091 M.getContext(), X.Var->getName() + ".atomic.nan", F, ExitBB);
12092 BasicBlock *NotNaNBB = BasicBlock::Create(
12093 M.getContext(), X.Var->getName() + ".atomic.notnan", F, ExitBB);
12095 M.getContext(), X.Var->getName() + ".atomic.zero", F, ExitBB);
12096 BasicBlock *NormalBB = BasicBlock::Create(
12097 M.getContext(), X.Var->getName() + ".atomic.normal", F, ExitBB);
12098
12099 // If either E or X is NaN → NaNBB (always fails), else check for ±0.0.
12100 CurBB->getTerminator()->eraseFromParent();
12101 Builder.SetInsertPoint(CurBB);
12102 Builder.CreateCondBr(EitherNaN, NaNBB, NotNaNBB);
12103
12104 // NaNBB: NaN == anything is always false; skip cmpxchg.
12105 Builder.SetInsertPoint(NaNBB);
12106 Builder.CreateBr(ExitBB);
12107
12108 // NotNaNBB: check both X and E for ±0.0.
12109 Builder.SetInsertPoint(NotNaNBB);
12110 Value *XIsZero =
12111 Builder.CreateFCmpOEQ(XFP, ConstantFP::getZero(X.ElemTy),
12112 X.Var->getName() + ".atomic.xiszero");
12113 Value *EIsZero = Builder.CreateFCmpOEQ(E, ConstantFP::getZero(X.ElemTy),
12114 "atomic.e.iszero");
12115 Value *BothZero = Builder.CreateAnd(XIsZero, EIsZero, "atomic.both.zero");
12116 Builder.CreateCondBr(BothZero, ZeroBB, NormalBB);
12117
12118 // ZeroBB: cmpxchg with X's loaded bit-pattern.
12119 Builder.SetInsertPoint(ZeroBB);
12120 AtomicCmpXchgInst *ResZero = Builder.CreateAtomicCmpXchg(
12121 X.Var, XCurr, DBCast, MaybeAlign(), AO, Failure);
12122 ResZero->setWeak(IsWeak);
12123 Value *OldZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/0);
12124 Value *OkZero = Builder.CreateExtractValue(ResZero, /*Idxs=*/1);
12125 Builder.CreateBr(ExitBB);
12126
12127 // NormalBB: original bitwise cmpxchg.
12128 Builder.SetInsertPoint(NormalBB);
12129 AtomicCmpXchgInst *ResNormal = Builder.CreateAtomicCmpXchg(
12130 X.Var, EBCast, DBCast, MaybeAlign(), AO, Failure);
12131 ResNormal->setWeak(IsWeak);
12132 Value *OldNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/0);
12133 Value *OkNormal = Builder.CreateExtractValue(ResNormal, /*Idxs=*/1);
12134 Builder.CreateBr(ExitBB);
12135
12136 // ExitBB: merge results from NaN, Zero, and Normal paths.
12137 Builder.SetInsertPoint(ExitBB->begin());
12138 PHINode *OldIntPHI =
12139 Builder.CreatePHI(IntCastTy, 3, X.Var->getName() + ".atomic.old");
12140 OldIntPHI->addIncoming(XCurr, NaNBB);
12141 OldIntPHI->addIncoming(OldZero, ZeroBB);
12142 OldIntPHI->addIncoming(OldNormal, NormalBB);
12143 PHINode *SuccessPHI = Builder.CreatePHI(Builder.getInt1Ty(), 3,
12144 X.Var->getName() + ".atomic.ok");
12145 SuccessPHI->addIncoming(Builder.getFalse(), NaNBB);
12146 SuccessPHI->addIncoming(OkZero, ZeroBB);
12147 SuccessPHI->addIncoming(OkNormal, NormalBB);
12148
12149 if (isa<UnreachableInst>(ExitBB->getTerminator())) {
12150 CurBBTI->eraseFromParent();
12151 Builder.SetInsertPoint(ExitBB);
12152 } else {
12153 Builder.SetInsertPoint(&*ExitBB->getFirstNonPHIIt());
12154 }
12155
12156 OldValue = Builder.CreateBitCast(OldIntPHI, X.ElemTy,
12157 X.Var->getName() + ".atomic.old.fp");
12158 SuccessOrFail = SuccessPHI;
12159 } else {
12160 AtomicCmpXchgInst *Result = nullptr;
12161 if (!IsInteger) {
12162 IntegerType *IntCastTy =
12163 IntegerType::get(M.getContext(), X.ElemTy->getScalarSizeInBits());
12164 Value *EBCast = Builder.CreateBitCast(E, IntCastTy);
12165 Value *DBCast = Builder.CreateBitCast(D, IntCastTy);
12166 Result = Builder.CreateAtomicCmpXchg(X.Var, EBCast, DBCast,
12167 MaybeAlign(), AO, Failure);
12168 } else {
12169 Result =
12170 Builder.CreateAtomicCmpXchg(X.Var, E, D, MaybeAlign(), AO, Failure);
12171 }
12172 Result->setWeak(IsWeak);
12173
12174 if (V.Var) {
12175 OldValue = Builder.CreateExtractValue(Result, /*Idxs=*/0);
12176 if (!IsInteger)
12177 OldValue = Builder.CreateBitCast(OldValue, X.ElemTy);
12178 assert(OldValue->getType() == V.ElemTy &&
12179 "OldValue and V must be of same type");
12180 if (IsPostfixUpdate) {
12181 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
12182 } else {
12183 SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
12184 if (IsFailOnly) {
12185 BasicBlock *CurBB = Builder.GetInsertBlock();
12186 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
12187 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
12188 BasicBlock *ExitBB = CurBB->splitBasicBlock(
12189 CurBBTI, X.Var->getName() + ".atomic.exit");
12190 BasicBlock *ContBB = CurBB->splitBasicBlock(
12191 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
12192 ContBB->getTerminator()->eraseFromParent();
12193 CurBB->getTerminator()->eraseFromParent();
12194
12195 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
12196
12197 Builder.SetInsertPoint(ContBB);
12198 Builder.CreateStore(OldValue, V.Var);
12199 Builder.CreateBr(ExitBB);
12200
12201 if (UnreachableInst *ExitTI =
12203 CurBBTI->eraseFromParent();
12204 Builder.SetInsertPoint(ExitBB);
12205 } else {
12206 Builder.SetInsertPoint(ExitTI);
12207 }
12208 } else {
12209 Value *CapturedValue =
12210 Builder.CreateSelect(SuccessOrFail, E, OldValue);
12211 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12212 }
12213 }
12214 }
12215 // The comparison result has to be stored.
12216 if (R.Var) {
12217 assert(R.Var->getType()->isPointerTy() &&
12218 "r.var must be of pointer type");
12219 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12220
12221 Value *SuccessFailureVal =
12222 Builder.CreateExtractValue(Result, /*Idxs=*/1);
12223 Value *ResultCast =
12224 R.IsSigned ? Builder.CreateSExt(SuccessFailureVal, R.ElemTy)
12225 : Builder.CreateZExt(SuccessFailureVal, R.ElemTy);
12226 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12227 }
12228 }
12229
12230 // For the HandleFPNegZero path, handle V.Var and R.Var using the
12231 // pre-computed OldValue and SuccessOrFail.
12232 if (HandleFPNegZero && !IsInteger) {
12233 if (V.Var) {
12234 assert(OldValue->getType() == V.ElemTy &&
12235 "OldValue and V must be of same type");
12236 if (IsPostfixUpdate) {
12237 Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
12238 } else {
12239 if (IsFailOnly) {
12240 BasicBlock *CurBB = Builder.GetInsertBlock();
12241 Instruction *CurBBTI = CurBB->getTerminatorOrNull();
12242 CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
12243 BasicBlock *ExitBB = CurBB->splitBasicBlock(
12244 CurBBTI, X.Var->getName() + ".atomic.exit");
12245 BasicBlock *ContBB = CurBB->splitBasicBlock(
12246 CurBB->getTerminator(), X.Var->getName() + ".atomic.cont");
12247 ContBB->getTerminator()->eraseFromParent();
12248 CurBB->getTerminator()->eraseFromParent();
12249
12250 Builder.CreateCondBr(SuccessOrFail, ExitBB, ContBB);
12251
12252 Builder.SetInsertPoint(ContBB);
12253 Builder.CreateStore(OldValue, V.Var);
12254 Builder.CreateBr(ExitBB);
12255
12256 if (UnreachableInst *ExitTI =
12258 CurBBTI->eraseFromParent();
12259 Builder.SetInsertPoint(ExitBB);
12260 } else {
12261 Builder.SetInsertPoint(ExitTI);
12262 }
12263 } else {
12264 Value *CapturedValue =
12265 Builder.CreateSelect(SuccessOrFail, E, OldValue);
12266 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12267 }
12268 }
12269 }
12270 // The comparison result has to be stored.
12271 if (R.Var) {
12272 assert(R.Var->getType()->isPointerTy() &&
12273 "r.var must be of pointer type");
12274 assert(R.ElemTy->isIntegerTy() && "r must be of integral type");
12275
12276 Value *ResultCast = R.IsSigned
12277 ? Builder.CreateSExt(SuccessOrFail, R.ElemTy)
12278 : Builder.CreateZExt(SuccessOrFail, R.ElemTy);
12279 Builder.CreateStore(ResultCast, R.Var, R.IsVolatile);
12280 }
12281 }
12282 } else {
12283 assert((Op == OMPAtomicCompareOp::MAX || Op == OMPAtomicCompareOp::MIN) &&
12284 "Op should be either max or min at this point");
12285 assert(!IsFailOnly && "IsFailOnly is only valid when the comparison is ==");
12286
12287 // Reverse the ordop as the OpenMP forms are different from LLVM forms.
12288 // Let's take max as example.
12289 // OpenMP form:
12290 // x = x > expr ? expr : x;
12291 // LLVM form:
12292 // *ptr = *ptr > val ? *ptr : val;
12293 // We need to transform to LLVM form.
12294 // x = x <= expr ? x : expr;
12296 if (IsXBinopExpr) {
12297 if (IsInteger) {
12298 if (X.IsSigned)
12299 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Min
12301 else
12302 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMin
12304 } else {
12305 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMin
12307 }
12308 } else {
12309 if (IsInteger) {
12310 if (X.IsSigned)
12311 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::Max
12313 else
12314 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::UMax
12316 } else {
12317 NewOp = Op == OMPAtomicCompareOp::MAX ? AtomicRMWInst::FMax
12319 }
12320 }
12321
12322 AtomicRMWInst *OldValue =
12323 Builder.CreateAtomicRMW(NewOp, X.Var, E, MaybeAlign(), AO);
12324 if (V.Var) {
12325 Value *CapturedValue = nullptr;
12326 if (IsPostfixUpdate) {
12327 CapturedValue = OldValue;
12328 } else {
12329 CmpInst::Predicate Pred;
12330 switch (NewOp) {
12331 case AtomicRMWInst::Max:
12332 Pred = CmpInst::ICMP_SGT;
12333 break;
12335 Pred = CmpInst::ICMP_UGT;
12336 break;
12338 Pred = CmpInst::FCMP_OGT;
12339 break;
12340 case AtomicRMWInst::Min:
12341 Pred = CmpInst::ICMP_SLT;
12342 break;
12344 Pred = CmpInst::ICMP_ULT;
12345 break;
12347 Pred = CmpInst::FCMP_OLT;
12348 break;
12349 default:
12350 llvm_unreachable("unexpected comparison op");
12351 }
12352 Value *NonAtomicCmp = Builder.CreateCmp(Pred, OldValue, E);
12353 CapturedValue = Builder.CreateSelect(NonAtomicCmp, E, OldValue);
12354 }
12355 Builder.CreateStore(CapturedValue, V.Var, V.IsVolatile);
12356 }
12357 }
12358
12359 checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Compare);
12360
12361 return Builder.saveIP();
12362}
12363
12366 BodyGenCallbackTy BodyGenCB, Value *NumTeamsLower,
12367 Value *NumTeamsUpper, Value *ThreadLimit,
12368 Value *IfExpr) {
12369 if (!updateToLocation(Loc))
12370 return InsertPointTy();
12371
12372 uint32_t SrcLocStrSize;
12373 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
12374 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
12375 Function *CurrentFunction = Builder.GetInsertBlock()->getParent();
12376
12377 // Outer allocation basicblock is the entry block of the current function.
12378 BasicBlock &OuterAllocaBB = CurrentFunction->getEntryBlock();
12379 if (&OuterAllocaBB == Builder.GetInsertBlock()) {
12380 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.entry");
12381 Builder.SetInsertPoint(BodyBB->begin());
12382 }
12383
12384 // The current basic block is split into four basic blocks. After outlining,
12385 // they will be mapped as follows:
12386 // ```
12387 // def current_fn() {
12388 // current_basic_block:
12389 // br label %teams.exit
12390 // teams.exit:
12391 // ; instructions after teams
12392 // }
12393 //
12394 // def outlined_fn() {
12395 // teams.alloca:
12396 // br label %teams.body
12397 // teams.body:
12398 // ; instructions within teams body
12399 // }
12400 // ```
12401 BasicBlock *ExitBB = splitBB(Builder, /*CreateBranch=*/true, "teams.exit");
12402 BasicBlock *BodyBB = splitBB(Builder, /*CreateBranch=*/true, "teams.body");
12403 BasicBlock *AllocaBB =
12404 splitBB(Builder, /*CreateBranch=*/true, "teams.alloca");
12405
12406 bool SubClausesPresent =
12407 (NumTeamsLower || NumTeamsUpper || ThreadLimit || IfExpr);
12408 // Push num_teams
12409 if (!Config.isTargetDevice() && SubClausesPresent) {
12410 assert((NumTeamsLower == nullptr || NumTeamsUpper != nullptr) &&
12411 "if lowerbound is non-null, then upperbound must also be non-null "
12412 "for bounds on num_teams");
12413
12414 if (NumTeamsUpper == nullptr)
12415 NumTeamsUpper = Builder.getInt32(0);
12416
12417 if (NumTeamsLower == nullptr)
12418 NumTeamsLower = NumTeamsUpper;
12419
12420 if (IfExpr) {
12421 assert(IfExpr->getType()->isIntegerTy() &&
12422 "argument to if clause must be an integer value");
12423
12424 // upper = ifexpr ? upper : 1
12425 if (IfExpr->getType() != Int1)
12426 IfExpr = Builder.CreateICmpNE(IfExpr,
12427 ConstantInt::get(IfExpr->getType(), 0));
12428 NumTeamsUpper = Builder.CreateSelect(
12429 IfExpr, NumTeamsUpper, Builder.getInt32(1), "numTeamsUpper");
12430
12431 // lower = ifexpr ? lower : 1
12432 NumTeamsLower = Builder.CreateSelect(
12433 IfExpr, NumTeamsLower, Builder.getInt32(1), "numTeamsLower");
12434 }
12435
12436 if (ThreadLimit == nullptr)
12437 ThreadLimit = Builder.getInt32(0);
12438
12439 // The __kmpc_push_num_teams_51 function expects int32 as the arguments. So,
12440 // truncate or sign extend the passed values to match the int32 parameters.
12441 Value *NumTeamsLowerInt32 =
12442 Builder.CreateSExtOrTrunc(NumTeamsLower, Builder.getInt32Ty());
12443 Value *NumTeamsUpperInt32 =
12444 Builder.CreateSExtOrTrunc(NumTeamsUpper, Builder.getInt32Ty());
12445 Value *ThreadLimitInt32 =
12446 Builder.CreateSExtOrTrunc(ThreadLimit, Builder.getInt32Ty());
12447
12448 Value *ThreadNum = getOrCreateThreadID(Ident);
12449
12451 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_teams_51),
12452 {Ident, ThreadNum, NumTeamsLowerInt32, NumTeamsUpperInt32,
12453 ThreadLimitInt32});
12454 }
12455 // Generate the body of teams.
12456 InsertPointTy AllocaIP(AllocaBB->begin());
12457 InsertPointTy CodeGenIP(BodyBB->begin());
12458 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12459 return Err;
12460
12461 auto OI = std::make_unique<OutlineInfo>();
12462 OI->EntryBB = AllocaBB;
12463 OI->ExitBB = ExitBB;
12464 OI->OuterAllocBB = &OuterAllocaBB;
12465
12466 // Insert fake values for global tid and bound tid.
12468 InsertPointTy OuterAllocaIP(OuterAllocaBB.begin());
12469 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12470 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "gid", true));
12471 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
12472 Builder, OuterAllocaIP, ToBeDeleted, AllocaIP, "tid", true));
12473
12474 auto HostPostOutlineCB = [this, Ident,
12475 ToBeDeleted](Function &OutlinedFn) mutable {
12476 // The stale call instruction will be replaced with a new call instruction
12477 // for runtime call with the outlined function.
12478
12479 assert(OutlinedFn.hasOneUse() &&
12480 "there must be a single user for the outlined function");
12481 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
12482 ToBeDeleted.push_back(StaleCI);
12483
12484 assert((OutlinedFn.arg_size() == 2 || OutlinedFn.arg_size() == 3) &&
12485 "Outlined function must have two or three arguments only");
12486
12487 bool HasShared = OutlinedFn.arg_size() == 3;
12488
12489 OutlinedFn.getArg(0)->setName("global.tid.ptr");
12490 OutlinedFn.getArg(1)->setName("bound.tid.ptr");
12491 if (HasShared)
12492 OutlinedFn.getArg(2)->setName("data");
12493
12494 // Call to the runtime function for teams in the current function.
12495 assert(StaleCI && "Error while outlining - no CallInst user found for the "
12496 "outlined function.");
12497 Builder.SetInsertPoint(StaleCI);
12498 SmallVector<Value *> Args = {
12499 Ident, Builder.getInt32(StaleCI->arg_size() - 2), &OutlinedFn};
12500 if (HasShared)
12501 Args.push_back(StaleCI->getArgOperand(2));
12504 omp::RuntimeFunction::OMPRTL___kmpc_fork_teams),
12505 Args);
12506
12507 Builder.ClearInsertionPoint();
12508 for (Instruction *I : llvm::reverse(ToBeDeleted))
12509 I->eraseFromParent();
12510 };
12511
12512 if (!Config.isTargetDevice())
12513 OI->PostOutlineCB = HostPostOutlineCB;
12514
12515 addOutlineInfo(std::move(OI));
12516
12517 Builder.SetInsertPoint(ExitBB);
12518
12519 return Builder.saveIP();
12520}
12521
12523 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
12524 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB) {
12525 if (!updateToLocation(Loc))
12526 return InsertPointTy();
12527
12528 BasicBlock *OuterAllocaBB = OuterAllocIP.getNodeParent();
12529
12530 if (OuterAllocaBB == Builder.GetInsertBlock()) {
12531 BasicBlock *BodyBB =
12532 splitBB(Builder, /*CreateBranch=*/true, "distribute.entry");
12533 Builder.SetInsertPoint(BodyBB->begin());
12534 }
12535 BasicBlock *ExitBB =
12536 splitBB(Builder, /*CreateBranch=*/true, "distribute.exit");
12537 BasicBlock *BodyBB =
12538 splitBB(Builder, /*CreateBranch=*/true, "distribute.body");
12539 BasicBlock *AllocaBB =
12540 splitBB(Builder, /*CreateBranch=*/true, "distribute.alloca");
12541
12542 // Generate the body of distribute clause
12543 InsertPointTy AllocaIP(AllocaBB->begin());
12544 InsertPointTy CodeGenIP(BodyBB->begin());
12545 if (Error Err = BodyGenCB(AllocaIP, CodeGenIP, ExitBB))
12546 return Err;
12547
12548 // When using target we use different runtime functions which require a
12549 // callback.
12550 if (Config.isTargetDevice()) {
12551 auto OI = std::make_unique<OutlineInfo>();
12552 OI->OuterAllocBB = OuterAllocIP.getNodeParent();
12553 OI->EntryBB = AllocaBB;
12554 OI->ExitBB = ExitBB;
12555 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
12556 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
12557
12558 addOutlineInfo(std::move(OI));
12559 }
12560 Builder.SetInsertPoint(ExitBB);
12561
12562 return Builder.saveIP();
12563}
12564
12567 std::string VarName) {
12568 llvm::Constant *MapNamesArrayInit = llvm::ConstantArray::get(
12570 Names.size()),
12571 Names);
12572 auto *MapNamesArrayGlobal = new llvm::GlobalVariable(
12573 M, MapNamesArrayInit->getType(),
12574 /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, MapNamesArrayInit,
12575 VarName);
12576 return MapNamesArrayGlobal;
12577}
12578
12579// Create all simple and struct types exposed by the runtime and remember
12580// the llvm::PointerTypes of them for easy access later.
12581void OpenMPIRBuilder::initializeTypes(Module &M) {
12582 LLVMContext &Ctx = M.getContext();
12583 StructType *T;
12584 unsigned DefaultTargetAS = Config.getDefaultTargetAS();
12585 unsigned ProgramAS = M.getDataLayout().getProgramAddressSpace();
12586#define OMP_TYPE(VarName, InitValue) VarName = InitValue;
12587#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \
12588 VarName##Ty = ArrayType::get(ElemTy, ArraySize); \
12589 VarName##PtrTy = PointerType::get(Ctx, DefaultTargetAS);
12590#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \
12591 VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \
12592 VarName##Ptr = PointerType::get(Ctx, ProgramAS);
12593#define OMP_STRUCT_TYPE(VarName, StructName, Packed, ...) \
12594 T = StructType::getTypeByName(Ctx, StructName); \
12595 if (!T) \
12596 T = StructType::create(Ctx, {__VA_ARGS__}, StructName, Packed); \
12597 VarName = T; \
12598 VarName##Ptr = PointerType::get(Ctx, DefaultTargetAS);
12599#include "llvm/Frontend/OpenMP/OMPKinds.def"
12600}
12601
12604 SmallVectorImpl<BasicBlock *> &BlockVector) {
12606 BlockSet.insert(EntryBB);
12607 BlockSet.insert(ExitBB);
12608
12609 Worklist.push_back(EntryBB);
12610 while (!Worklist.empty()) {
12611 BasicBlock *BB = Worklist.pop_back_val();
12612 BlockVector.push_back(BB);
12613 for (BasicBlock *SuccBB : successors(BB))
12614 if (BlockSet.insert(SuccBB).second)
12615 Worklist.push_back(SuccBB);
12616 }
12617}
12618
12619std::unique_ptr<CodeExtractor>
12621 bool ArgsInZeroAddressSpace,
12622 Twine Suffix) {
12623 return std::make_unique<CodeExtractor>(
12624 Blocks, /* DominatorTree */ nullptr,
12625 /* AggregateArgs */ true,
12626 /* BlockFrequencyInfo */ nullptr,
12627 /* BranchProbabilityInfo */ nullptr,
12628 /* AssumptionCache */ nullptr,
12629 /* AllowVarArgs */ true,
12630 /* AllowAlloca */ true,
12631 /* AllocationBlock*/ OuterAllocBB,
12632 /* DeallocationBlocks */ ArrayRef<BasicBlock *>(),
12633 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12634}
12635
12636std::unique_ptr<CodeExtractor> DeviceSharedMemOutlineInfo::createCodeExtractor(
12637 ArrayRef<BasicBlock *> Blocks, bool ArgsInZeroAddressSpace, Twine Suffix) {
12638 return std::make_unique<DeviceSharedMemCodeExtractor>(
12639 OMPBuilder, Blocks, /* DominatorTree */ nullptr,
12640 /* AggregateArgs */ true,
12641 /* BlockFrequencyInfo */ nullptr,
12642 /* BranchProbabilityInfo */ nullptr,
12643 /* AssumptionCache */ nullptr,
12644 /* AllowVarArgs */ true,
12645 /* AllowAlloca */ true,
12646 /* AllocationBlock*/ OuterAllocBB,
12647 /* DeallocationBlocks */ OuterDeallocBBs.empty()
12649 : OuterDeallocBBs,
12650 /* Suffix */ Suffix.str(), ArgsInZeroAddressSpace);
12651}
12652
12654 uint64_t Size, int32_t Flags,
12656 StringRef Name) {
12657 if (!Config.isGPU()) {
12660 Name.empty() ? Addr->getName() : Name, Size, Flags, /*Data=*/0);
12661 return;
12662 }
12663 // TODO: Add support for global variables on the device after declare target
12664 // support.
12665 Function *Fn = dyn_cast<Function>(Addr);
12666 if (!Fn)
12667 return;
12668
12669 // Add a function attribute for the kernel.
12670 Fn->addFnAttr("kernel");
12671 if (T.isAMDGCN())
12672 Fn->addFnAttr("uniform-work-group-size");
12673 Fn->addFnAttr(Attribute::MustProgress);
12674}
12675
12676// We only generate metadata for function that contain target regions.
12679
12680 // If there are no entries, we don't need to do anything.
12681 if (OffloadInfoManager.empty())
12682 return;
12683
12684 LLVMContext &C = M.getContext();
12687 16>
12688 OrderedEntries(OffloadInfoManager.size());
12689
12690 // Auxiliary methods to create metadata values and strings.
12691 auto &&GetMDInt = [this](unsigned V) {
12692 return ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), V));
12693 };
12694
12695 auto &&GetMDString = [&C](StringRef V) { return MDString::get(C, V); };
12696
12697 // Create the offloading info metadata node.
12698 NamedMDNode *MD = M.getOrInsertNamedMetadata("omp_offload.info");
12699 auto &&TargetRegionMetadataEmitter =
12700 [&C, MD, &OrderedEntries, &GetMDInt, &GetMDString](
12701 const TargetRegionEntryInfo &EntryInfo,
12703 // Generate metadata for target regions. Each entry of this metadata
12704 // contains:
12705 // - Entry 0 -> Kind of this type of metadata (0).
12706 // - Entry 1 -> Device ID of the file where the entry was identified.
12707 // - Entry 2 -> File ID of the file where the entry was identified.
12708 // - Entry 3 -> Mangled name of the function where the entry was
12709 // identified.
12710 // - Entry 4 -> Line in the file where the entry was identified.
12711 // - Entry 5 -> Count of regions at this DeviceID/FilesID/Line.
12712 // - Entry 6 -> Order the entry was created.
12713 // The first element of the metadata node is the kind.
12714 Metadata *Ops[] = {
12715 GetMDInt(E.getKind()), GetMDInt(EntryInfo.DeviceID),
12716 GetMDInt(EntryInfo.FileID), GetMDString(EntryInfo.ParentName),
12717 GetMDInt(EntryInfo.Line), GetMDInt(EntryInfo.Count),
12718 GetMDInt(E.getOrder())};
12719
12720 // Save this entry in the right position of the ordered entries array.
12721 OrderedEntries[E.getOrder()] = std::make_pair(&E, EntryInfo);
12722
12723 // Add metadata to the named metadata node.
12724 MD->addOperand(MDNode::get(C, Ops));
12725 };
12726
12727 OffloadInfoManager.actOnTargetRegionEntriesInfo(TargetRegionMetadataEmitter);
12728
12729 // Create function that emits metadata for each device global variable entry;
12730 auto &&DeviceGlobalVarMetadataEmitter =
12731 [&C, &OrderedEntries, &GetMDInt, &GetMDString, MD](
12732 StringRef MangledName,
12734 // Generate metadata for global variables. Each entry of this metadata
12735 // contains:
12736 // - Entry 0 -> Kind of this type of metadata (1).
12737 // - Entry 1 -> Mangled name of the variable.
12738 // - Entry 2 -> Declare target kind.
12739 // - Entry 3 -> Order the entry was created.
12740 // The first element of the metadata node is the kind.
12741 Metadata *Ops[] = {GetMDInt(E.getKind()), GetMDString(MangledName),
12742 GetMDInt(E.getFlags()), GetMDInt(E.getOrder())};
12743
12744 // Save this entry in the right position of the ordered entries array.
12745 TargetRegionEntryInfo varInfo(MangledName, 0, 0, 0);
12746 OrderedEntries[E.getOrder()] = std::make_pair(&E, varInfo);
12747
12748 // Add metadata to the named metadata node.
12749 MD->addOperand(MDNode::get(C, Ops));
12750 };
12751
12752 OffloadInfoManager.actOnDeviceGlobalVarEntriesInfo(
12753 DeviceGlobalVarMetadataEmitter);
12754
12755 for (const auto &E : OrderedEntries) {
12756 assert(E.first && "All ordered entries must exist!");
12757 if (const auto *CE =
12759 E.first)) {
12760 if (!CE->getID() || !CE->getAddress()) {
12761 // Do not blame the entry if the parent funtion is not emitted.
12762 TargetRegionEntryInfo EntryInfo = E.second;
12763 StringRef FnName = EntryInfo.ParentName;
12764 if (!M.getNamedValue(FnName))
12765 continue;
12766 ErrorFn(EMIT_MD_TARGET_REGION_ERROR, EntryInfo);
12767 continue;
12768 }
12769 createOffloadEntry(CE->getID(), CE->getAddress(),
12770 /*Size=*/0, CE->getFlags(),
12772 } else if (const auto *CE = dyn_cast<
12774 E.first)) {
12777 CE->getFlags());
12778 switch (Flags) {
12781 if (Config.isTargetDevice() && Config.hasRequiresUnifiedSharedMemory())
12782 continue;
12783 if (!CE->getAddress()) {
12784 ErrorFn(EMIT_MD_DECLARE_TARGET_ERROR, E.second);
12785 continue;
12786 }
12787 // The vaiable has no definition - no need to add the entry.
12788 if (CE->getVarSize() == 0)
12789 continue;
12790 break;
12792 assert(((Config.isTargetDevice() && !CE->getAddress()) ||
12793 (!Config.isTargetDevice() && CE->getAddress())) &&
12794 "Declaret target link address is set.");
12795 if (Config.isTargetDevice())
12796 continue;
12797 if (!CE->getAddress()) {
12799 continue;
12800 }
12801 break;
12804 if (!CE->getAddress()) {
12805 ErrorFn(EMIT_MD_GLOBAL_VAR_INDIRECT_ERROR, E.second);
12806 continue;
12807 }
12808 break;
12809 default:
12810 break;
12811 }
12812
12813 // Hidden or internal symbols on the device are not externally visible.
12814 // We should not attempt to register them by creating an offloading
12815 // entry. Indirect variables are handled separately on the device.
12816 if (auto *GV = dyn_cast<GlobalValue>(CE->getAddress()))
12817 if ((GV->hasLocalLinkage() || GV->hasHiddenVisibility()) &&
12818 (Flags !=
12820 Flags != OffloadEntriesInfoManager::
12821 OMPTargetGlobalVarEntryIndirectVTable))
12822 continue;
12823
12824 // Indirect globals need to use a special name that doesn't match the name
12825 // of the associated host global.
12827 Flags ==
12829 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12830 Flags, CE->getLinkage(), CE->getVarName());
12831 else
12832 createOffloadEntry(CE->getAddress(), CE->getAddress(), CE->getVarSize(),
12833 Flags, CE->getLinkage());
12834
12835 } else {
12836 llvm_unreachable("Unsupported entry kind.");
12837 }
12838 }
12839
12840 // Emit requires directive globals to a special entry so the runtime can
12841 // register them when the device image is loaded.
12842 // TODO: This reduces the offloading entries to a 32-bit integer. Offloading
12843 // entries should be redesigned to better suit this use-case.
12844 if (Config.hasRequiresFlags() && !Config.isTargetDevice())
12848 ".requires", /*Size=*/0,
12850 Config.getRequiresFlags());
12851}
12852
12855 unsigned FileID, unsigned Line, unsigned Count) {
12856 raw_svector_ostream OS(Name);
12857 OS << KernelNamePrefix << llvm::format("%x", DeviceID)
12858 << llvm::format("_%x_", FileID) << ParentName << "_l" << Line;
12859 if (Count)
12860 OS << "_" << Count;
12861}
12862
12864 SmallVectorImpl<char> &Name, const TargetRegionEntryInfo &EntryInfo) {
12865 unsigned NewCount = getTargetRegionEntryInfoCount(EntryInfo);
12867 Name, EntryInfo.ParentName, EntryInfo.DeviceID, EntryInfo.FileID,
12868 EntryInfo.Line, NewCount);
12869}
12870
12873 vfs::FileSystem &VFS,
12874 StringRef ParentName) {
12875 sys::fs::UniqueID ID(0xdeadf17e, 0);
12876 auto FileIDInfo = CallBack();
12877 uint64_t FileID = 0;
12878 if (ErrorOr<vfs::Status> Status = VFS.status(std::get<0>(FileIDInfo))) {
12879 ID = Status->getUniqueID();
12880 FileID = Status->getUniqueID().getFile();
12881 } else {
12882 // If the inode ID could not be determined, create a hash value
12883 // the current file name and use that as an ID.
12884 FileID = hash_value(std::get<0>(FileIDInfo));
12885 }
12886
12887 return TargetRegionEntryInfo(ParentName, ID.getDevice(), FileID,
12888 std::get<1>(FileIDInfo));
12889}
12890
12892 unsigned Offset = 0;
12893 for (uint64_t Remain =
12894 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12896 !(Remain & 1); Remain = Remain >> 1)
12897 Offset++;
12898 return Offset;
12899}
12900
12903 // Rotate by getFlagMemberOffset() bits.
12904 return static_cast<omp::OpenMPOffloadMappingFlags>(((uint64_t)Position + 1)
12905 << getFlagMemberOffset());
12906}
12907
12910 omp::OpenMPOffloadMappingFlags MemberOfFlag) {
12911 // If the entry is PTR_AND_OBJ but has not been marked with the special
12912 // placeholder value 0xFFFF in the MEMBER_OF field, then it should not be
12913 // marked as MEMBER_OF.
12914 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12916 static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12919 return;
12920
12921 // Entries with ATTACH are not members-of anything. They are handled
12922 // separately by the runtime after other maps have been handled.
12923 if (static_cast<std::underlying_type_t<omp::OpenMPOffloadMappingFlags>>(
12925 return;
12926
12927 // Reset the placeholder value to prepare the flag for the assignment of the
12928 // proper MEMBER_OF value.
12929 Flags &= ~omp::OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF;
12930 Flags |= MemberOfFlag;
12931}
12932
12936 bool IsDeclaration, bool IsExternallyVisible,
12937 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12938 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12939 std::vector<Triple> TargetTriple, Type *LlvmPtrTy,
12940 std::function<Constant *()> GlobalInitializer,
12941 std::function<GlobalValue::LinkageTypes()> VariableLinkage) {
12942 // TODO: convert this to utilise the IRBuilder Config rather than
12943 // a passed down argument.
12944 if (OpenMPSIMD)
12945 return nullptr;
12946
12949 CaptureClause ==
12951 Config.hasRequiresUnifiedSharedMemory())) {
12952 SmallString<64> PtrName;
12953 {
12954 raw_svector_ostream OS(PtrName);
12955 OS << MangledName;
12956 if (!IsExternallyVisible)
12957 OS << format("_%x", EntryInfo.FileID);
12958 OS << "_decl_tgt_ref_ptr";
12959 }
12960
12961 Value *Ptr = M.getNamedValue(PtrName);
12962
12963 if (!Ptr) {
12964 GlobalValue *GlobalValue = M.getNamedValue(MangledName);
12965 Ptr = getOrCreateInternalVariable(LlvmPtrTy, PtrName);
12966
12967 auto *GV = cast<GlobalVariable>(Ptr);
12968 GV->setLinkage(GlobalValue::WeakAnyLinkage);
12969
12970 if (!Config.isTargetDevice()) {
12971 if (GlobalInitializer)
12972 GV->setInitializer(GlobalInitializer());
12973 else
12974 GV->setInitializer(GlobalValue);
12975 }
12976
12978 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
12979 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
12980 GlobalInitializer, VariableLinkage, LlvmPtrTy, cast<Constant>(Ptr));
12981 }
12982
12983 return cast<Constant>(Ptr);
12984 }
12985
12986 return nullptr;
12987}
12988
12992 bool IsDeclaration, bool IsExternallyVisible,
12993 TargetRegionEntryInfo EntryInfo, StringRef MangledName,
12994 std::vector<GlobalVariable *> &GeneratedRefs, bool OpenMPSIMD,
12995 std::vector<Triple> TargetTriple,
12996 std::function<Constant *()> GlobalInitializer,
12997 std::function<GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy,
12998 Constant *Addr) {
13000 (TargetTriple.empty() && !Config.isTargetDevice()))
13001 return;
13002
13004 StringRef VarName;
13005 int64_t VarSize;
13007
13009 CaptureClause ==
13011 !Config.hasRequiresUnifiedSharedMemory()) {
13013 VarName = MangledName;
13014 GlobalValue *LlvmVal = M.getNamedValue(VarName);
13015
13016 if (!IsDeclaration)
13017 VarSize = divideCeil(
13018 M.getDataLayout().getTypeSizeInBits(LlvmVal->getValueType()), 8);
13019 else
13020 VarSize = 0;
13021 Linkage = (VariableLinkage) ? VariableLinkage() : LlvmVal->getLinkage();
13022
13023 // This is a workaround carried over from Clang which prevents undesired
13024 // optimisation of internal variables.
13025 if (Config.isTargetDevice() &&
13026 (!IsExternallyVisible || Linkage == GlobalValue::LinkOnceODRLinkage)) {
13027 // Do not create a "ref-variable" if the original is not also available
13028 // on the host.
13029 if (!OffloadInfoManager.hasDeviceGlobalVarEntryInfo(VarName))
13030 return;
13031
13032 std::string RefName = createPlatformSpecificName({VarName, "ref"});
13033
13034 if (!M.getNamedValue(RefName)) {
13035 Constant *AddrRef =
13036 getOrCreateInternalVariable(Addr->getType(), RefName);
13037 auto *GvAddrRef = cast<GlobalVariable>(AddrRef);
13038 GvAddrRef->setConstant(true);
13039 GvAddrRef->setLinkage(GlobalValue::InternalLinkage);
13040 GvAddrRef->setInitializer(Addr);
13041 GeneratedRefs.push_back(GvAddrRef);
13042 }
13043 }
13044 } else {
13047 else
13049
13050 if (Config.isTargetDevice()) {
13051 VarName = (Addr) ? Addr->getName() : "";
13052 Addr = nullptr;
13053 } else {
13055 CaptureClause, DeviceClause, IsDeclaration, IsExternallyVisible,
13056 EntryInfo, MangledName, GeneratedRefs, OpenMPSIMD, TargetTriple,
13057 LlvmPtrTy, GlobalInitializer, VariableLinkage);
13058 VarName = (Addr) ? Addr->getName() : "";
13059 }
13060 VarSize = M.getDataLayout().getPointerSize();
13062 }
13063
13064 OffloadInfoManager.registerDeviceGlobalVarEntryInfo(VarName, Addr, VarSize,
13065 Flags, Linkage);
13066}
13067
13068/// Loads all the offload entries information from the host IR
13069/// metadata.
13071 // If we are in target mode, load the metadata from the host IR. This code has
13072 // to match the metadata creation in createOffloadEntriesAndInfoMetadata().
13073
13074 NamedMDNode *MD = M.getNamedMetadata(ompOffloadInfoName);
13075 if (!MD)
13076 return;
13077
13078 for (MDNode *MN : MD->operands()) {
13079 auto &&GetMDInt = [MN](unsigned Idx) {
13080 auto *V = cast<ConstantAsMetadata>(MN->getOperand(Idx));
13081 return cast<ConstantInt>(V->getValue())->getZExtValue();
13082 };
13083
13084 auto &&GetMDString = [MN](unsigned Idx) {
13085 auto *V = cast<MDString>(MN->getOperand(Idx));
13086 return V->getString();
13087 };
13088
13089 switch (GetMDInt(0)) {
13090 default:
13091 llvm_unreachable("Unexpected metadata!");
13092 break;
13093 case OffloadEntriesInfoManager::OffloadEntryInfo::
13094 OffloadingEntryInfoTargetRegion: {
13095 TargetRegionEntryInfo EntryInfo(/*ParentName=*/GetMDString(3),
13096 /*DeviceID=*/GetMDInt(1),
13097 /*FileID=*/GetMDInt(2),
13098 /*Line=*/GetMDInt(4),
13099 /*Count=*/GetMDInt(5));
13100 OffloadInfoManager.initializeTargetRegionEntryInfo(EntryInfo,
13101 /*Order=*/GetMDInt(6));
13102 break;
13103 }
13104 case OffloadEntriesInfoManager::OffloadEntryInfo::
13105 OffloadingEntryInfoDeviceGlobalVar:
13106 OffloadInfoManager.initializeDeviceGlobalVarEntryInfo(
13107 /*MangledName=*/GetMDString(1),
13109 /*Flags=*/GetMDInt(2)),
13110 /*Order=*/GetMDInt(3));
13111 break;
13112 }
13113 }
13114}
13115
13117 StringRef HostFilePath) {
13118 if (HostFilePath.empty())
13119 return;
13120
13121 auto Buf = VFS.getBufferForFile(HostFilePath);
13122 if (std::error_code Err = Buf.getError()) {
13123 report_fatal_error(("error opening host file from host file path inside of "
13124 "OpenMPIRBuilder: " +
13125 Err.message())
13126 .c_str());
13127 }
13128
13129 LLVMContext Ctx;
13131 Ctx, parseBitcodeFile(Buf.get()->getMemBufferRef(), Ctx));
13132 if (std::error_code Err = M.getError()) {
13134 ("error parsing host file inside of OpenMPIRBuilder: " + Err.message())
13135 .c_str());
13136 }
13137
13138 loadOffloadInfoMetadata(*M.get());
13139}
13140
13143 llvm::StringRef Name) {
13144 Builder.restoreIP(Loc.IP);
13145
13146 BasicBlock *CurBB = Builder.GetInsertBlock();
13147 assert(CurBB &&
13148 "expected a valid insertion block for creating an iterator loop");
13149 Function *F = CurBB->getParent();
13150
13151 InsertPointTy SplitIP = Builder.saveIP();
13152 if (SplitIP == CurBB->end())
13153 if (Instruction *Terminator = CurBB->getTerminatorOrNull())
13154 SplitIP = Terminator->getIterator();
13155
13156 BasicBlock *ContBB =
13157 splitBB(SplitIP, /*CreateBranch=*/false,
13158 Builder.getCurrentDebugLocation(), "omp.it.cont");
13159
13160 CanonicalLoopInfo *CLI =
13161 createLoopSkeleton(Builder.getCurrentDebugLocation(), TripCount, F,
13162 /*PreInsertBefore=*/ContBB,
13163 /*PostInsertBefore=*/ContBB, Name);
13164
13165 // Enter loop from original block.
13166 redirectTo(CurBB, CLI->getPreheader(), Builder.getCurrentDebugLocation());
13167
13168 // Remove the unconditional branch inserted by createLoopSkeleton in the body
13169 if (Instruction *T = CLI->getBody()->getTerminatorOrNull())
13170 T->eraseFromParent();
13171
13172 InsertPointTy BodyIP = CLI->getBodyIP();
13173 // Blocks that exist before BodyGen, other than the body and latch, are
13174 // outside the loop body.
13175 SmallPtrSet<BasicBlock *, 32> ExistingBlocks;
13176 for (BasicBlock &Block : *F)
13177 ExistingBlocks.insert(&Block);
13178 if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
13179 return Err;
13180
13181 // The body may span several blocks. Branch its single unterminated block to
13182 // the latch; otherwise some block must already branch there.
13183 BasicBlock *Latch = CLI->getLatch();
13184 BasicBlock *OpenBB = nullptr;
13185 bool ReachesLatch = false;
13186 SmallVector<BasicBlock *> Worklist{CLI->getBody()};
13187 SmallPtrSet<BasicBlock *, 8> Visited{CLI->getBody()};
13188 while (!Worklist.empty()) {
13189 BasicBlock *BB = Worklist.pop_back_val();
13190 if (!BB->hasTerminator()) {
13191 if (OpenBB)
13193 "iterator bodygen must leave at most one unterminated block",
13195 OpenBB = BB;
13196 continue;
13197 }
13198 for (BasicBlock *Succ : successors(BB)) {
13199 if (Succ == Latch) {
13200 ReachesLatch = true;
13201 continue;
13202 }
13203 if (Succ != CLI->getBody() && ExistingBlocks.contains(Succ))
13205 "iterator bodygen must not branch out of the loop body",
13207 if (Visited.insert(Succ).second)
13208 Worklist.push_back(Succ);
13209 }
13210 }
13211
13212 if (OpenBB) {
13213 Builder.SetInsertPoint(OpenBB);
13214 Builder.CreateBr(Latch);
13215 } else if (!ReachesLatch) {
13216 return make_error<StringError>("iterator bodygen must reach the loop latch",
13218 }
13219
13220 // Link After -> ContBB
13221 Builder.SetInsertPoint(CLI->getAfter()->begin());
13222 if (!CLI->getAfter()->hasTerminator())
13223 Builder.CreateBr(ContBB);
13224
13225 return ContBB->begin();
13226}
13227
13228/// Mangle the parameter part of the vector function name according to
13229/// their OpenMP classification. The mangling function is defined in
13230/// section 4.5 of the AAVFABI(2021Q1).
13231static std::string mangleVectorParameters(
13233 SmallString<256> Buffer;
13234 llvm::raw_svector_ostream Out(Buffer);
13235 for (const auto &ParamAttr : ParamAttrs) {
13236 switch (ParamAttr.Kind) {
13238 Out << 'l';
13239 break;
13241 Out << 'R';
13242 break;
13244 Out << 'U';
13245 break;
13247 Out << 'L';
13248 break;
13250 Out << 'u';
13251 break;
13253 Out << 'v';
13254 break;
13255 }
13256 if (ParamAttr.HasVarStride)
13257 Out << "s" << ParamAttr.StrideOrArg;
13258 else if (ParamAttr.Kind ==
13260 ParamAttr.Kind ==
13262 ParamAttr.Kind ==
13264 ParamAttr.Kind ==
13266 // Don't print the step value if it is not present or if it is
13267 // equal to 1.
13268 if (ParamAttr.StrideOrArg < 0)
13269 Out << 'n' << -ParamAttr.StrideOrArg;
13270 else if (ParamAttr.StrideOrArg != 1)
13271 Out << ParamAttr.StrideOrArg;
13272 }
13273
13274 if (!!ParamAttr.Alignment)
13275 Out << 'a' << ParamAttr.Alignment;
13276 }
13277
13278 return std::string(Out.str());
13279}
13280
13282 llvm::Function *Fn, unsigned NumElts, const llvm::APSInt &VLENVal,
13284 struct ISADataTy {
13285 char ISA;
13286 unsigned VecRegSize;
13287 };
13288 ISADataTy ISAData[] = {
13289 {'b', 128}, // SSE
13290 {'c', 256}, // AVX
13291 {'d', 256}, // AVX2
13292 {'e', 512}, // AVX512
13293 };
13295 switch (Branch) {
13297 Masked.push_back('N');
13298 Masked.push_back('M');
13299 break;
13301 Masked.push_back('N');
13302 break;
13304 Masked.push_back('M');
13305 break;
13306 }
13307 for (char Mask : Masked) {
13308 for (const ISADataTy &Data : ISAData) {
13310 llvm::raw_svector_ostream Out(Buffer);
13311 Out << "_ZGV" << Data.ISA << Mask;
13312 if (!VLENVal) {
13313 assert(NumElts && "Non-zero simdlen/cdtsize expected");
13314 Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
13315 } else {
13316 Out << VLENVal;
13317 }
13318 Out << mangleVectorParameters(ParamAttrs);
13319 Out << '_' << Fn->getName();
13320 Fn->addFnAttr(Out.str());
13321 }
13322 }
13323}
13324
13325// Function used to add the attribute. The parameter `VLEN` is templated to
13326// allow the use of `x` when targeting scalable functions for SVE.
13327template <typename T>
13328static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix,
13329 char ISA, StringRef ParSeq,
13330 StringRef MangledName, bool OutputBecomesInput,
13331 llvm::Function *Fn) {
13332 SmallString<256> Buffer;
13333 llvm::raw_svector_ostream Out(Buffer);
13334 Out << Prefix << ISA << LMask << VLEN;
13335 if (OutputBecomesInput)
13336 Out << 'v';
13337 Out << ParSeq << '_' << MangledName;
13338 Fn->addFnAttr(Out.str());
13339}
13340
13341// Helper function to generate the Advanced SIMD names depending on the value
13342// of the NDS when simdlen is not present.
13343static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
13344 StringRef Prefix, char ISA,
13345 StringRef ParSeq, StringRef MangledName,
13346 bool OutputBecomesInput,
13347 llvm::Function *Fn) {
13348 switch (NDS) {
13349 case 8:
13350 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13351 OutputBecomesInput, Fn);
13352 addAArch64VectorName(16, Mask, Prefix, ISA, ParSeq, MangledName,
13353 OutputBecomesInput, Fn);
13354 break;
13355 case 16:
13356 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13357 OutputBecomesInput, Fn);
13358 addAArch64VectorName(8, Mask, Prefix, ISA, ParSeq, MangledName,
13359 OutputBecomesInput, Fn);
13360 break;
13361 case 32:
13362 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13363 OutputBecomesInput, Fn);
13364 addAArch64VectorName(4, Mask, Prefix, ISA, ParSeq, MangledName,
13365 OutputBecomesInput, Fn);
13366 break;
13367 case 64:
13368 case 128:
13369 addAArch64VectorName(2, Mask, Prefix, ISA, ParSeq, MangledName,
13370 OutputBecomesInput, Fn);
13371 break;
13372 default:
13373 llvm_unreachable("Scalar type is too wide.");
13374 }
13375}
13376
13377/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
13379 llvm::Function *Fn, unsigned UserVLEN,
13381 char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput) {
13382 assert((ISA == 'n' || ISA == 's') && "Expected ISA either 's' or 'n'.");
13383
13384 // Sort out parameter sequence.
13385 const std::string ParSeq = mangleVectorParameters(ParamAttrs);
13386 StringRef Prefix = "_ZGV";
13387 StringRef MangledName = Fn->getName();
13388
13389 // Generate simdlen from user input (if any).
13390 if (UserVLEN) {
13391 if (ISA == 's') {
13392 // SVE generates only a masked function.
13393 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13394 OutputBecomesInput, Fn);
13395 return;
13396 }
13397
13398 switch (Branch) {
13400 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13401 OutputBecomesInput, Fn);
13402 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13403 OutputBecomesInput, Fn);
13404 break;
13406 addAArch64VectorName(UserVLEN, "M", Prefix, ISA, ParSeq, MangledName,
13407 OutputBecomesInput, Fn);
13408 break;
13410 addAArch64VectorName(UserVLEN, "N", Prefix, ISA, ParSeq, MangledName,
13411 OutputBecomesInput, Fn);
13412 break;
13413 }
13414 return;
13415 }
13416
13417 if (ISA == 's') {
13418 // SVE, section 3.4.1, item 1.
13419 addAArch64VectorName("x", "M", Prefix, ISA, ParSeq, MangledName,
13420 OutputBecomesInput, Fn);
13421 return;
13422 }
13423
13424 switch (Branch) {
13426 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13427 MangledName, OutputBecomesInput, Fn);
13428 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13429 MangledName, OutputBecomesInput, Fn);
13430 break;
13432 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "M", Prefix, ISA, ParSeq,
13433 MangledName, OutputBecomesInput, Fn);
13434 break;
13436 addAArch64AdvSIMDNDSNames(NarrowestDataSize, "N", Prefix, ISA, ParSeq,
13437 MangledName, OutputBecomesInput, Fn);
13438 break;
13439 }
13440}
13441
13442//===----------------------------------------------------------------------===//
13443// OffloadEntriesInfoManager
13444//===----------------------------------------------------------------------===//
13445
13447 return OffloadEntriesTargetRegion.empty() &&
13448 OffloadEntriesDeviceGlobalVar.empty();
13449}
13450
13451unsigned OffloadEntriesInfoManager::getTargetRegionEntryInfoCount(
13452 const TargetRegionEntryInfo &EntryInfo) const {
13453 auto It = OffloadEntriesTargetRegionCount.find(
13454 getTargetRegionEntryCountKey(EntryInfo));
13455 if (It == OffloadEntriesTargetRegionCount.end())
13456 return 0;
13457 return It->second;
13458}
13459
13460void OffloadEntriesInfoManager::incrementTargetRegionEntryInfoCount(
13461 const TargetRegionEntryInfo &EntryInfo) {
13462 OffloadEntriesTargetRegionCount[getTargetRegionEntryCountKey(EntryInfo)] =
13463 EntryInfo.Count + 1;
13464}
13465
13466/// Initialize target region entry.
13468 const TargetRegionEntryInfo &EntryInfo, unsigned Order) {
13469 OffloadEntriesTargetRegion[EntryInfo] =
13470 OffloadEntryInfoTargetRegion(Order, /*Addr=*/nullptr, /*ID=*/nullptr,
13472 ++OffloadingEntriesNum;
13473}
13474
13476 TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID,
13478 assert(EntryInfo.Count == 0 && "expected default EntryInfo");
13479
13480 // Update the EntryInfo with the next available count for this location.
13481 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13482
13483 // If we are emitting code for a target, the entry is already initialized,
13484 // only has to be registered.
13485 if (OMPBuilder->Config.isTargetDevice()) {
13486 // This could happen if the device compilation is invoked standalone.
13487 if (!hasTargetRegionEntryInfo(EntryInfo)) {
13488 return;
13489 }
13490 auto &Entry = OffloadEntriesTargetRegion[EntryInfo];
13491 Entry.setAddress(Addr);
13492 Entry.setID(ID);
13493 Entry.setFlags(Flags);
13494 } else {
13496 hasTargetRegionEntryInfo(EntryInfo, /*IgnoreAddressId*/ true))
13497 return;
13498 assert(!hasTargetRegionEntryInfo(EntryInfo) &&
13499 "Target region entry already registered!");
13500 OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags);
13501 OffloadEntriesTargetRegion[EntryInfo] = Entry;
13502 ++OffloadingEntriesNum;
13503 }
13504 incrementTargetRegionEntryInfoCount(EntryInfo);
13505}
13506
13508 TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId) const {
13509
13510 // Update the EntryInfo with the next available count for this location.
13511 EntryInfo.Count = getTargetRegionEntryInfoCount(EntryInfo);
13512
13513 auto It = OffloadEntriesTargetRegion.find(EntryInfo);
13514 if (It == OffloadEntriesTargetRegion.end()) {
13515 return false;
13516 }
13517 // Fail if this entry is already registered.
13518 if (!IgnoreAddressId && (It->second.getAddress() || It->second.getID()))
13519 return false;
13520 return true;
13521}
13522
13524 const OffloadTargetRegionEntryInfoActTy &Action) {
13525 // Scan all target region entries and perform the provided action.
13526 for (const auto &It : OffloadEntriesTargetRegion) {
13527 Action(It.first, It.second);
13528 }
13529}
13530
13532 StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order) {
13533 OffloadEntriesDeviceGlobalVar.try_emplace(Name, Order, Flags);
13534 ++OffloadingEntriesNum;
13535}
13536
13538 StringRef VarName, Constant *Addr, int64_t VarSize,
13540 if (OMPBuilder->Config.isTargetDevice()) {
13541 // This could happen if the device compilation is invoked standalone.
13542 if (!hasDeviceGlobalVarEntryInfo(VarName))
13543 return;
13544 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13545 if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) {
13546 if (Entry.getVarSize() == 0) {
13547 Entry.setVarSize(VarSize);
13548 Entry.setLinkage(Linkage);
13549 }
13550 return;
13551 }
13552 Entry.setVarSize(VarSize);
13553 Entry.setLinkage(Linkage);
13554 Entry.setAddress(Addr);
13555 } else {
13556 if (hasDeviceGlobalVarEntryInfo(VarName)) {
13557 auto &Entry = OffloadEntriesDeviceGlobalVar[VarName];
13558 assert(Entry.isValid() && Entry.getFlags() == Flags &&
13559 "Entry not initialized!");
13560 if (Entry.getVarSize() == 0) {
13561 Entry.setVarSize(VarSize);
13562 Entry.setLinkage(Linkage);
13563 }
13564 return;
13565 }
13567 Flags ==
13569 OffloadEntriesDeviceGlobalVar.try_emplace(VarName, OffloadingEntriesNum,
13570 Addr, VarSize, Flags, Linkage,
13571 VarName.str());
13572 else
13573 OffloadEntriesDeviceGlobalVar.try_emplace(
13574 VarName, OffloadingEntriesNum, Addr, VarSize, Flags, Linkage, "");
13575 ++OffloadingEntriesNum;
13576 }
13577}
13578
13581 // Scan all target region entries and perform the provided action.
13582 for (const auto &E : OffloadEntriesDeviceGlobalVar)
13583 Action(E.getKey(), E.getValue());
13584}
13585
13586//===----------------------------------------------------------------------===//
13587// CanonicalLoopInfo
13588//===----------------------------------------------------------------------===//
13589
13590void CanonicalLoopInfo::reportUnsupportedVirtualCollapse() const {
13591 assert(isValid() && "Requires a valid canonical loop");
13592 assert(VirtualCollapse && "Requires Virtual collapse state");
13594 "virtual collapse must be the final structural loop transformation");
13595}
13596
13597void CanonicalLoopInfo::collectControlBlocks(
13599 if (VirtualCollapse)
13600 reportUnsupportedVirtualCollapse();
13601 // We only count those BBs as control block for which we do not need to
13602 // reverse the CFG, i.e. not the loop body which can contain arbitrary control
13603 // flow. For consistency, this also means we do not add the Body block, which
13604 // is just the entry to the body code.
13605 BBs.reserve(BBs.size() + 6);
13606 BBs.append({getPreheader(), Header, Cond, Latch, Exit, getAfter()});
13607}
13608
13609BasicBlock *CanonicalLoopInfo::getVirtualCollapseInit() const {
13610 return VirtualCollapse ? VirtualCollapse->Count->getParent() : nullptr;
13611}
13612
13614 assert(isValid() && "Requires a valid canonical loop");
13615 if (VirtualCollapse)
13616 return getVirtualCollapseInit()->getSinglePredecessor();
13617
13618 for (BasicBlock *Pred : predecessors(Header)) {
13619 if (Pred != Latch)
13620 return Pred;
13621 }
13622 llvm_unreachable("Missing preheader");
13623}
13624
13625void CanonicalLoopInfo::setTripCount(Value *TripCount) {
13626 assert(isValid() && "Requires a valid canonical loop");
13627
13628 Instruction *CmpI = &getCond()->front();
13629 assert(isa<CmpInst>(CmpI) && "First inst must compare IV with TripCount");
13630 CmpI->setOperand(1, TripCount);
13631
13632#ifndef NDEBUG
13633 assertOK();
13634#endif
13635}
13636
13637void CanonicalLoopInfo::mapIndVar(
13638 llvm::function_ref<Value *(Instruction *)> Updater) {
13639 assert(isValid() && "Requires a valid canonical loop");
13640
13641 Instruction *OldIV = getIndVar();
13642
13643 // Record all uses excluding those introduced by the updater. Uses by the
13644 // CanonicalLoopInfo itself to keep track of the number of iterations are
13645 // excluded.
13646 SmallVector<Use *> ReplacableUses;
13647 for (Use &U : OldIV->uses()) {
13648 auto *User = dyn_cast<Instruction>(U.getUser());
13649 if (!User)
13650 continue;
13651 if (User->getParent() == getCond())
13652 continue;
13653 if (User->getParent() == getLatch())
13654 continue;
13655 ReplacableUses.push_back(&U);
13656 }
13657
13658 // Run the updater that may introduce new uses
13659 Value *NewIV = Updater(OldIV);
13660
13661 // Replace the old uses with the value returned by the updater.
13662 for (Use *U : ReplacableUses)
13663 U->set(NewIV);
13664
13665#ifndef NDEBUG
13666 assertOK();
13667#endif
13668}
13669
13671#ifndef NDEBUG
13672 // No constraints if this object currently does not describe a loop.
13673 if (!isValid())
13674 return;
13675
13676 bool IsVirtual = VirtualCollapse.has_value();
13677 BasicBlock *Preheader = getPreheader();
13678 BasicBlock *Init = getVirtualCollapseInit();
13679 BasicBlock *IndVarEntry = Preheader;
13680 if (IsVirtual) {
13681 auto *Guard = cast<CondBrInst>(Init->getTerminator());
13682 IndVarEntry = Guard->getSuccessor(0);
13683 assert(IndVarEntry->getSinglePredecessor() == Init);
13684 assert(IndVarEntry->getSingleSuccessor() == Header);
13685 assert(Guard->getSuccessor(1) == Exit);
13686 assert(VirtualCollapse->Begin->getParent() == Init);
13687 for (BinaryOperator *Copy :
13688 {VirtualCollapse->Begin, VirtualCollapse->Count}) {
13689 auto *Zero = dyn_cast<ConstantInt>(Copy->getOperand(1));
13690 assert(Copy->getOpcode() == Instruction::Add && Zero && Zero->isZero() &&
13691 "Virtual range copies must add zero");
13692 }
13693 auto *NonEmpty = cast<ICmpInst>(Guard->getCondition());
13694 assert(NonEmpty->getPredicate() == ICmpInst::ICMP_NE &&
13695 NonEmpty->getOperand(0) == VirtualCollapse->Count &&
13696 cast<ConstantInt>(NonEmpty->getOperand(1))->isZero() &&
13697 "Collapsed guard must skip empty ranges with zero count");
13698 }
13699
13700 // Validate the retained physical loop without exposing a logical body or IV.
13701 BasicBlock *Body =
13702 IsVirtual ? cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0)
13703 : getBody();
13704 BasicBlock *After = getAfter();
13705
13706 // Verify standard control-flow we use for OpenMP loops.
13707 assert(Preheader);
13708 assert(isa<UncondBrInst>(Preheader->getTerminator()) &&
13709 "Preheader must terminate with unconditional branch");
13710 if (IsVirtual)
13711 assert(Preheader->getSingleSuccessor() == Init &&
13712 "Preheader must jump to collapsed initialization");
13713 else
13714 assert(Preheader->getSingleSuccessor() == Header &&
13715 "Preheader must jump to header");
13716
13717 assert(Header);
13718 assert(isa<UncondBrInst>(Header->getTerminator()) &&
13719 "Header must terminate with unconditional branch");
13720 assert(Header->getSingleSuccessor() == Cond &&
13721 "Header must jump to exiting block");
13722
13723 assert(Cond);
13724 assert(Cond->getSinglePredecessor() == Header &&
13725 "Exiting block only reachable from header");
13726
13727 assert(isa<CondBrInst>(Cond->getTerminator()) &&
13728 "Exiting block must terminate with conditional branch");
13729 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(0) == Body &&
13730 "Exiting block's first successor jump to the body");
13731 assert(cast<CondBrInst>(Cond->getTerminator())->getSuccessor(1) == Exit &&
13732 "Exiting block's second successor must exit the loop");
13733
13734 assert(Body);
13735 assert(Body->getSinglePredecessor() == Cond &&
13736 "Body only reachable from exiting block");
13737 assert(!isa<PHINode>(Body->front()));
13738
13739 assert(Latch);
13740 assert(isa<UncondBrInst>(Latch->getTerminator()) &&
13741 "Latch must terminate with unconditional branch");
13742 assert(Latch->getSingleSuccessor() == Header && "Latch must jump to header");
13743 // TODO: To support simple redirecting of the end of the body code that has
13744 // multiple; introduce another auxiliary basic block like preheader and after.
13745 assert(Latch->getSinglePredecessor() != nullptr);
13746 assert(!isa<PHINode>(Latch->front()));
13747
13748 assert(Exit);
13749 assert(isa<UncondBrInst>(Exit->getTerminator()) &&
13750 "Exit block must terminate with unconditional branch");
13751 assert(Exit->getSingleSuccessor() == After &&
13752 "Exit block must jump to after block");
13753
13754 assert(After);
13755 assert(After->getSinglePredecessor() == Exit &&
13756 "After block only reachable from exit block");
13757 assert(After->empty() || !isa<PHINode>(After->front()));
13758
13759 Instruction *IndVar = IsVirtual ? &Header->front() : getIndVar();
13760 assert(IndVar && "Canonical induction variable not found?");
13761 assert(isa<IntegerType>(IndVar->getType()) &&
13762 "Induction variable must be an integer");
13763 assert(cast<PHINode>(IndVar)->getParent() == Header &&
13764 "Induction variable must be a PHI in the loop header");
13765 assert(cast<PHINode>(IndVar)->getIncomingBlock(0) == IndVarEntry);
13766 if (!IsVirtual)
13767 assert(cast<ConstantInt>(cast<PHINode>(IndVar)->getIncomingValue(0))
13768 ->isZero());
13769 assert(cast<PHINode>(IndVar)->getIncomingBlock(1) == Latch);
13770
13771 auto *NextIndVar = cast<PHINode>(IndVar)->getIncomingValue(1);
13772 assert(cast<Instruction>(NextIndVar)->getParent() == Latch);
13773 assert(cast<BinaryOperator>(NextIndVar)->getOpcode() == BinaryOperator::Add);
13774 assert(cast<BinaryOperator>(NextIndVar)->getOperand(0) == IndVar);
13775 assert(cast<ConstantInt>(cast<BinaryOperator>(NextIndVar)->getOperand(1))
13776 ->isOne());
13777
13778 Value *TripCount =
13779 IsVirtual ? cast<CmpInst>(&Cond->front())->getOperand(1) : getTripCount();
13780 assert(TripCount && "Loop trip count not found?");
13781 assert(IndVar->getType() == TripCount->getType() &&
13782 "Trip count and induction variable must have the same type");
13783
13784 auto *CmpI = cast<CmpInst>(&Cond->front());
13785 assert(CmpI->getPredicate() == CmpInst::ICMP_ULT &&
13786 "Exit condition must be a signed less-than comparison");
13787 assert(CmpI->getOperand(0) == IndVar &&
13788 "Exit condition must compare the induction variable");
13789 assert(CmpI->getOperand(1) == TripCount &&
13790 "Exit condition must compare with the trip count");
13791#endif
13792}
13793
13795 Header = nullptr;
13796 Cond = nullptr;
13797 Latch = nullptr;
13798 Exit = nullptr;
13799 VirtualCollapse.reset();
13800}
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 LVOptions Options
Definition LVOptions.cpp:25
static bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
Definition Lint.cpp:540
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file contains the declarations for metadata subclasses.
#define T
uint64_t IntrinsicInst * II
#define OMP_KERNEL_ARG_VERSION
Provides definitions for Target specific Grid Values.
static void emitTargetCall(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Value *RTLocOverride, OpenMPIRBuilder::InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, OpenMPIRBuilder::TargetDataInfo &Info, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, const OpenMPIRBuilder::TargetKernelRuntimeAttrs &RuntimeAttrs, Value *IfCond, Function *OutlinedFn, Constant *OutlinedFnID, SmallVectorImpl< Value * > &Args, OpenMPIRBuilder::GenMapInfoCallbackTy GenMapInfoCB, OpenMPIRBuilder::CustomMapperCallbackTy CustomMapperCB, const OpenMPIRBuilder::DependenciesInfo &Dependencies, bool HasNoWait, Value *DynCGroupMem, OMPDynGroupprivateFallbackType DynCGroupMemFallback)
static Value * removeASCastIfPresent(Value *V)
static void createTargetLoopWorkshareCall(OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType, BasicBlock *InsertBlock, Value *Ident, Value *LoopBodyArg, Value *TripCount, Function &LoopBodyFn, bool NoLoop)
Value * createFakeIntVal(IRBuilderBase &Builder, OpenMPIRBuilder::InsertPointTy OuterAllocaIP, llvm::SmallVectorImpl< Instruction * > &ToBeDeleted, OpenMPIRBuilder::InsertPointTy InnerAllocaIP, const Twine &Name="", bool AsPtr=true, bool Is64Bit=false)
static Function * createTargetParallelWrapper(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn)
Create wrapper function used to gather the outlined function's argument structure from a shared buffe...
static void redirectTo(BasicBlock *Source, BasicBlock *Target, DebugLoc DL)
Make Source branch to Target.
static FunctionCallee getKmpcDistForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void applyParallelAccessesMetadata(CanonicalLoopInfo *CLI, LLVMContext &Ctx, Loop *Loop, LoopInfo &LoopInfo, SmallVector< Metadata * > &LoopMDList)
static Expected< Function * > createOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, StringRef FuncName, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB, DebugLoc OutlinedFnLoc)
static void addAArch64VectorName(T VLEN, StringRef LMask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static FunctionCallee getKmpcForDynamicFiniForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for finalizing the dynamic loop using depending on type.
static void FixupDebugInfoForOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func, DenseMap< Value *, std::tuple< Value *, unsigned > > &ValueReplacementMap)
static OMPScheduleType getOpenMPOrderingScheduleType(OMPScheduleType BaseScheduleType, bool HasOrderedClause)
Adds ordering modifier flags to schedule type.
static OMPScheduleType getOpenMPMonotonicityScheduleType(OMPScheduleType ScheduleType, bool HasSimdModifier, bool HasMonotonic, bool HasNonmonotonic, bool HasOrderedClause)
Adds monotonicity modifier flags to schedule type.
static std::string mangleVectorParameters(ArrayRef< llvm::OpenMPIRBuilder::DeclareSimdAttrTy > ParamAttrs)
Mangle the parameter part of the vector function name according to their OpenMP classification.
static bool isGenericKernel(Function &Fn)
static void workshareLoopTargetCallback(OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident, Function &OutlinedFn, const SmallVector< Instruction *, 4 > &ToBeDeleted, WorksharingLoopType LoopType, bool NoLoop)
static bool isValidWorkshareLoopScheduleType(OMPScheduleType SchedType)
static bool isAtomicableReductionSet(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos)
static llvm::CallInst * emitNoUnwindRuntimeCall(IRBuilder<> &Builder, llvm::FunctionCallee Callee, ArrayRef< llvm::Value * > Args, const llvm::Twine &Name)
static Error populateReductionFunction(Function *ReductionFunc, ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, IRBuilder<> &Builder, ArrayRef< bool > IsByRef, bool IsGPU)
static Function * getFreshReductionFunc(Module &M)
static void raiseUserConstantDataAllocasToEntryBlock(IRBuilderBase &Builder, Function *Function)
static FunctionCallee getKmpcForDynamicNextForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for updating the next loop using OpenMP dynamic scheduling depending...
static bool isConflictIP(IRBuilder<>::InsertPoint IP1, IRBuilder<>::InsertPoint IP2)
Return whether IP1 and IP2 are ambiguous, i.e.
static void checkReductionInfos(ArrayRef< OpenMPIRBuilder::ReductionInfo > ReductionInfos, bool IsGPU)
static Type * getOffloadingArrayType(Value *V)
static OMPScheduleType getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasDistScheduleChunks)
Determine which scheduling algorithm to use, determined from schedule clause arguments.
static OMPScheduleType computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks, bool HasSimdModifier, bool HasMonotonicModifier, bool HasNonmonotonicModifier, bool HasOrderedClause, bool HasDistScheduleChunks)
Determine the schedule type using schedule and ordering clause arguments.
static FunctionCallee getKmpcForDynamicInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
Returns an LLVM function to call for initializing loop bounds using OpenMP dynamic scheduling dependi...
static std::optional< omp::OMPTgtExecModeFlags > getTargetKernelExecMode(Function &Kernel)
Given a function, if it represents the entry point of a target kernel, this returns the execution mod...
static StructType * createTaskWithPrivatesTy(OpenMPIRBuilder &OMPIRBuilder, ArrayRef< Value * > OffloadingArraysToPrivatize)
static cl::opt< double > UnrollThresholdFactor("openmp-ir-builder-unroll-threshold-factor", cl::Hidden, cl::desc("Factor for the unroll threshold to account for code " "simplifications still taking place"), cl::init(1.5))
static cl::opt< bool > UseDefaultMaxThreads("openmp-ir-builder-use-default-max-threads", cl::Hidden, cl::desc("Use a default max threads if none is provided."), cl::init(true))
static int32_t computeHeuristicUnrollFactor(CanonicalLoopInfo *CLI)
Heuristically determine the best-performant unroll factor for CLI.
static Error emitTargetOutlinedFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, bool IsOffloadEntry, TargetRegionEntryInfo &EntryInfo, const OpenMPIRBuilder::TargetKernelDefaultAttrs &DefaultAttrs, Function *&OutlinedFn, Constant *&OutlinedFnID, SmallVectorImpl< Value * > &Inputs, OpenMPIRBuilder::TargetBodyGenCallbackTy &CBFunc, OpenMPIRBuilder::TargetGenArgAccessorsCallbackTy &ArgAccessorFuncCB, DebugLoc OutlinedFnLoc)
static Value * emitTaskDependencies(OpenMPIRBuilder &OMPBuilder, const SmallVectorImpl< OpenMPIRBuilder::DependData > &Dependencies)
static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value, bool Min)
static OpenMPIRBuilder::InsertPointTy getInsertPointAfterInstr(Instruction *I)
static void redirectAllPredecessorsTo(BasicBlock *OldTarget, BasicBlock *NewTarget, DebugLoc DL)
Redirect all edges that branch to OldTarget to NewTarget.
static void hoistNonEntryAllocasToEntryBlock(llvm::BasicBlock &Block)
static std::unique_ptr< TargetMachine > createTargetMachine(Function *F, CodeGenOptLevel OptLevel)
Create the TargetMachine object to query the backend for optimization preferences.
static FunctionCallee getKmpcForStaticInitForType(Type *Ty, Module &M, OpenMPIRBuilder &OMPBuilder)
static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup, LoopInfo &LI)
Attach llvm.access.group metadata to the memref instructions of Block.
static void addBasicBlockMetadata(BasicBlock *BB, ArrayRef< Metadata * > Properties)
Attach metadata Properties to the basic block described by BB.
static void restoreIPandDebugLoc(llvm::IRBuilderBase &Builder, llvm::IRBuilderBase::InsertPoint IP)
This is a wrapper over IRBuilderBase::restoreIP that also restores a current debug location when the ...
static LoadInst * loadSharedDataFromTaskDescriptor(OpenMPIRBuilder &OMPIRBuilder, IRBuilderBase &Builder, Value *TaskWithPrivates, Type *TaskWithPrivatesTy)
Given a task descriptor, TaskWithPrivates, return the pointer to the block of pointers containing sha...
static cl::opt< bool > OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden, cl::desc("Use optimistic attributes describing " "'as-if' properties of runtime calls."), cl::init(false))
static bool hasGridValue(const Triple &T)
static FunctionCallee getKmpcForStaticLoopForType(Type *Ty, OpenMPIRBuilder *OMPBuilder, WorksharingLoopType LoopType)
static const omp::GV & getGridValue(const Triple &T, Function *Kernel)
static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask, StringRef Prefix, char ISA, StringRef ParSeq, StringRef MangledName, bool OutputBecomesInput, llvm::Function *Fn)
static Function * emitTargetTaskProxyFunction(OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, CallInst *StaleCI, StructType *PrivatesTy, StructType *TaskWithPrivatesTy, const size_t NumOffloadingArrays, const int SharedArgsOperandNo)
Create an entry point for a target task with the following.
static void addLoopMetadata(CanonicalLoopInfo *Loop, ArrayRef< Metadata * > Properties)
Attach loop metadata Properties to the loop described by Loop.
static AtomicOrdering TransformReleaseAcquireRelease(AtomicOrdering AO)
static void removeUnusedBlocksFromParent(ArrayRef< BasicBlock * > BBs)
static void targetParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition, Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr, Value *ThreadID, const SmallVector< Instruction *, 4 > &ToBeDeleted)
static void hostParallelCallback(OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn, Value *Ident, Value *IfCondition, Instruction *PrivTID, AllocaInst *PrivTIDAddr, const SmallVector< Instruction *, 4 > &ToBeDeleted)
#define P(N)
FunctionAnalysisManager FAM
Function * Fun
This file defines the Pass Instrumentation classes that provide instrumentation points into the pass ...
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.
A function analysis which provides an AssumptionCache.
LLVM_ABI AssumptionCache run(Function &F, FunctionAnalysisManager &)
A cache of @llvm.assume calls within a function.
An instruction that atomically checks whether a specified value is in a memory location,...
void setWeak(bool IsWeak)
static AtomicOrdering getStrongestFailureOrdering(AtomicOrdering SuccessOrdering)
Returns the strongest permitted ordering on failure, given the desired ordering on success.
LLVM_ABI std::pair< LoadInst *, AllocaInst * > EmitAtomicLoadLibcall(AtomicOrdering AO)
Definition Atomic.cpp:109
LLVM_ABI void EmitAtomicStoreLibcall(AtomicOrdering AO, Value *Source)
Definition Atomic.cpp:150
an instruction that atomically reads a memory location, combines it with another value,...
BinOp
This enumeration lists the possible modifications atomicrmw can make.
@ Add
*p = old + v
@ FAdd
*p = old + v
@ USubCond
Subtract only if no unsigned overflow.
@ FMinimum
*p = minimum(old, v) minimum matches the behavior of llvm.minimum.
@ Min
*p = old <signed v ? old : v
@ Sub
*p = old - v
@ And
*p = old & v
@ Xor
*p = old ^ v
@ USubSat
*p = usub.sat(old, v) usub.sat matches the behavior of llvm.usub.sat.
@ FMaximum
*p = maximum(old, v) maximum matches the behavior of llvm.maximum.
@ FSub
*p = old - v
@ UIncWrap
Increment one up to a maximum value.
@ Max
*p = old >signed v ? old : v
@ UMin
*p = old <unsigned v ? old : v
@ FMin
*p = minnum(old, v) minnum matches the behavior of llvm.minnum.
@ UMax
*p = old >unsigned v ? old : v
@ FMaximumNum
*p = maximumnum(old, v) maximumnum matches the behavior of llvm.maximumnum.
@ FMax
*p = maxnum(old, v) maxnum matches the behavior of llvm.maxnum.
@ UDecWrap
Decrement one until a minimum value or zero.
@ FMinimumNum
*p = minimumnum(old, v) minimumnum matches the behavior of llvm.minimumnum.
@ Nand
*p = ~(old & v)
This class holds the attributes for a particular argument, parameter, function, or return value.
Definition Attributes.h:406
LLVM_ABI AttributeSet addAttributes(LLVMContext &C, AttributeSet AS) const
Add attributes to the attribute set.
LLVM_ABI AttributeSet addAttribute(LLVMContext &C, Attribute::AttrKind Kind) const
Add an argument attribute.
static LLVM_ABI Attribute getWithAlignment(LLVMContext &Context, Align Alignment)
Return a uniquified Attribute object that has the specific alignment set.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
LLVM_ABI void replaceSuccessorsPhiUsesWith(BasicBlock *Old, BasicBlock *New)
Update all phi nodes in this basic block's successors to refer to basic block New instead of basic bl...
iterator end()
Definition BasicBlock.h:453
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:440
LLVM_ABI const_iterator getFirstInsertionPt() const
Returns an iterator to the first instruction in this block that is suitable for inserting a non-PHI i...
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction.
const Function * getParent() const
Return the enclosing method, or null if none.
Definition BasicBlock.h:213
reverse_iterator rbegin()
Definition BasicBlock.h:456
bool hasTerminator() const LLVM_READONLY
Returns whether the block has a terminator.
Definition BasicBlock.h:232
bool empty() const
Definition BasicBlock.h:462
const Instruction & back() const
Definition BasicBlock.h:465
LLVM_ABI BasicBlock * splitBasicBlockBefore(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction and insert the new basic blo...
LLVM_ABI InstListType::const_iterator getFirstNonPHIIt() const
Returns an iterator to the first instruction in this block that is not a PHINode instruction.
LLVM_ABI void insertDbgRecordBefore(DbgRecord *DR, InstListType::iterator Here)
Insert a DbgRecord into a block at the position given by Here.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
LLVM_ABI const BasicBlock * getUniqueSuccessor() const
Return the successor of this block if it has a unique successor.
LLVM_ABI const BasicBlock * getSinglePredecessor() const
Return the predecessor of this block if it has a single predecessor block.
const Instruction & front() const
Definition BasicBlock.h:463
InstListType::reverse_iterator reverse_iterator
Definition BasicBlock.h:172
LLVM_ABI const BasicBlock * getUniquePredecessor() const
Return the predecessor of this block if it has a unique predecessor block.
const Instruction * getTerminatorOrNull() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
Definition BasicBlock.h:248
LLVM_ABI const BasicBlock * getSingleSuccessor() const
Return the successor of this block if it has a single successor.
LLVM_ABI InstListType::const_iterator getFirstNonPHIOrPseudoProbe() const
Returns an iterator to the first instruction in this block that is not a PHINode or a pseudo operatio...
LLVM_ABI SymbolTableList< BasicBlock >::iterator eraseFromParent()
Unlink 'this' from the containing function and delete it.
reverse_iterator rend()
Definition BasicBlock.h:458
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
LLVM_ABI LLVMContext & getContext() const
Get the context in which this basic block lives.
void moveBefore(BasicBlock *MovePos)
Unlink this basic block from its current function and insert it into the function that MovePos lives ...
Definition BasicBlock.h:367
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
void splice(BasicBlock::iterator ToIt, BasicBlock *FromBB)
Transfer all instructions from FromBB to this basic block at ToIt.
Definition BasicBlock.h:638
LLVM_ABI void removePredecessor(BasicBlock *Pred, bool KeepOneInputPHIs=false)
Update PHI nodes in this BasicBlock before removal of predecessor Pred.
void setDoesNotThrow()
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Value * getArgOperand(unsigned i) const
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
unsigned arg_size() const
This class represents a function call, abstracting a target machine's calling convention.
Class to represented the control flow structure of an OpenMP canonical loop.
Value * getTripCount() const
Returns the llvm::Value containing the number of loop iterations.
BasicBlock * getHeader() const
The header is the entry for each iteration.
LLVM_ABI void assertOK() const
Consistency self-check.
Type * getIndVarType() const
Return the type of the induction variable (and the trip count).
BasicBlock * getBody() const
The body block is the single entry for a loop iteration and not controlled by CanonicalLoopInfo.
bool isValid() const
Returns whether this object currently represents the IR of a loop.
void setLastIter(Value *IterVar)
Sets the last iteration variable for this loop.
OpenMPIRBuilder::InsertPointTy getAfterIP() const
Return the insertion point for user code after the loop.
OpenMPIRBuilder::InsertPointTy getBodyIP() const
Return the insertion point for user code in the body.
BasicBlock * getAfter() const
The after block is intended for clean-up code such as lifetime end markers.
Function * getFunction() const
LLVM_ABI void invalidate()
Invalidate this loop.
BasicBlock * getLatch() const
Reaching the latch indicates the end of the loop body code.
OpenMPIRBuilder::InsertPointTy getPreheaderIP() const
Return the insertion point for user code before the loop.
BasicBlock * getCond() const
The condition block computes whether there is another loop iteration.
BasicBlock * getExit() const
Reaching the exit indicates no more iterations are being executed.
LLVM_ABI BasicBlock * getPreheader() const
The preheader ensures that there is only a single edge entering the loop.
Instruction * getIndVar() const
Returns the instruction representing the current logical induction variable.
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
LLVM_ABI DominatorTree run(Function &F, FunctionAnalysisManager &)
Run the analysis pass over a function and produce a dominator tree.
bool properlyDominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
properlyDominates - Returns true iff A dominates B and A != B.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:122
Represents either an error or a value T.
Definition ErrorOr.h:56
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
reference get()
Returns a reference to the stored T value.
Definition Error.h:582
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Class to represent function types.
Type * getParamType(unsigned i) const
Parameter type accessors.
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition Function.cpp:641
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:169
const BasicBlock & getEntryBlock() const
Definition Function.h:794
Argument * arg_iterator
Definition Function.h:73
bool empty() const
Definition Function.h:844
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:212
void removeFromParent()
removeFromParent - This method unlinks 'this' from the containing module, but does not delete it.
Definition Function.cpp:447
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:766
DISubprogram * getSubprogram() const
Get the attached subprogram.
AttributeList getAttributes() const
Return the attribute list for this Function.
Definition Function.h:329
const Function & getFunction() const
Definition Function.h:167
iterator begin()
Definition Function.h:838
arg_iterator arg_begin()
Definition Function.h:853
void setAttributes(AttributeList Attrs)
Set the attribute list for this Function.
Definition Function.h:332
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:352
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
adds the attribute to the list of attributes for the given arg.
Definition Function.cpp:669
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition Function.h:740
size_t arg_size() const
Definition Function.h:886
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:217
iterator end()
Definition Function.h:840
void setCallingConv(CallingConv::ID CC)
Definition Function.h:277
Argument * getArg(unsigned i) const
Definition Function.h:871
bool hasMetadata() const
Return true if this GlobalObject has any metadata attached to it.
LLVM_ABI void addMetadata(unsigned KindID, MDNode &MD)
Add a metadata attachment.
LinkageTypes getLinkage() const
void setLinkage(LinkageTypes LT)
Module * getParent()
Get the module that this global value is contained inside of...
void setDSOLocal(bool Local)
PointerType * getType() const
Global values are always pointers.
@ HiddenVisibility
The GV is hidden.
Definition GlobalValue.h:69
@ ProtectedVisibility
The GV is protected.
Definition GlobalValue.h:70
void setVisibility(VisibilityTypes V)
LinkageTypes
An enumeration for the kinds of linkage for global values.
Definition GlobalValue.h:52
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
@ CommonLinkage
Tentative definitions.
Definition GlobalValue.h:63
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ WeakODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:58
@ WeakAnyLinkage
Keep one copy of named function when linking (weak)
Definition GlobalValue.h:57
@ AppendingLinkage
Special purpose, only applies to global arrays.
Definition GlobalValue.h:59
@ LinkOnceODRLinkage
Same, but only replaced by something equivalent.
Definition GlobalValue.h:56
Type * getValueType() const
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
Common base class shared among various IRBuilders.
Definition IRBuilder.h:111
InsertPoint saveIP() const
Returns the current insert point.
Definition IRBuilder.h:256
BasicBlock::iterator InsertPoint
InsertPoint - A saved insertion point.
Definition IRBuilder.h:253
void restoreIP(InsertPoint IP)
Sets the current insert point to a previously-saved location.
Definition IRBuilder.h:266
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2909
LLVM_ABI void removeFromParent()
This method unlinks 'this' from the containing basic block, but does not delete it.
LLVM_ABI unsigned getNumSuccessors() const LLVM_READONLY
Return the number of successors that this instruction has.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI void moveBefore(InstListType::iterator InsertPos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI BasicBlock * getSuccessor(unsigned Idx) const LLVM_READONLY
Return the specified successor. This instruction must be a terminator.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
user_iterator user_begin()
user_iterator user_end()
LLVM_ABI void moveBeforePreserving(InstListType::iterator MovePos)
Perform a moveBefore operation, while signalling that the caller intends to preserve the original ord...
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
LLVM_ABI void insertAfter(Instruction *InsertPos)
Insert an unlinked instruction into a basic block immediately after the specified instruction.
LLVM_ABI const DataLayout & getDataLayout() const
Get the data layout of the module this instruction belongs to.
Class to represent integer types.
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:338
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:76
An instruction for reading from memory.
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this load instruction.
Align getAlign() const
Return the alignment of the access that is being performed.
Analysis pass that exposes the LoopInfo for a function.
Definition LoopInfo.h:594
LLVM_ABI LoopInfo run(Function &F, FunctionAnalysisManager &AM)
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
This class represents a loop nest and can be used to query its properties.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
LLVM_ABI MDNode * createCallbackEncoding(unsigned CalleeArgNo, ArrayRef< int > Arguments, bool VarArgsArePassed)
Return metadata describing a callback (see llvm::AbstractCallSite).
Metadata node.
Definition Metadata.h:1081
LLVM_ABI void replaceOperandWith(unsigned I, Metadata *New)
Replace a specific operand.
static MDTuple * getDistinct(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1587
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1435
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Definition Metadata.cpp:597
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
size_type size() const
Definition MapVector.h:58
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:332
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Definition Module.h:325
A tuple of MDNodes.
Definition Metadata.h:1797
iterator_range< op_iterator > operands()
Definition Metadata.h:1893
LLVM_ABI void addOperand(MDNode *M)
Class that manages information about offload code regions and data.
function_ref< void(StringRef, const OffloadEntryInfoDeviceGlobalVar &)> OffloadDeviceGlobalVarEntryInfoActTy
Applies action Action on all registered entries.
OMPTargetDeviceClauseKind
Kind of device clause for declare target variables and functions NOTE: Currently not used as a part o...
@ OMPTargetDeviceClauseAny
The target is marked for all devices.
LLVM_ABI void registerDeviceGlobalVarEntryInfo(StringRef VarName, Constant *Addr, int64_t VarSize, OMPTargetGlobalVarEntryKind Flags, GlobalValue::LinkageTypes Linkage)
Register device global variable entry.
LLVM_ABI void initializeDeviceGlobalVarEntryInfo(StringRef Name, OMPTargetGlobalVarEntryKind Flags, unsigned Order)
Initialize device global variable entry.
LLVM_ABI void actOnDeviceGlobalVarEntriesInfo(const OffloadDeviceGlobalVarEntryInfoActTy &Action)
OMPTargetRegionEntryKind
Kind of the target registry entry.
@ OMPTargetRegionEntryTargetRegion
Mark the entry as target region.
LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, const TargetRegionEntryInfo &EntryInfo)
LLVM_ABI bool hasTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, bool IgnoreAddressId=false) const
Return true if a target region entry with the provided information exists.
LLVM_ABI void registerTargetRegionEntryInfo(TargetRegionEntryInfo EntryInfo, Constant *Addr, Constant *ID, OMPTargetRegionEntryKind Flags)
Register target region entry.
LLVM_ABI void actOnTargetRegionEntriesInfo(const OffloadTargetRegionEntryInfoActTy &Action)
LLVM_ABI void initializeTargetRegionEntryInfo(const TargetRegionEntryInfo &EntryInfo, unsigned Order)
Initialize target region entry.
OMPTargetGlobalVarEntryKind
Kind of the global variable entry..
@ OMPTargetGlobalVarEntryEnter
Mark the entry as a declare target enter.
@ OMPTargetGlobalRegisterRequires
Mark the entry as a register requires global.
@ OMPTargetGlobalVarEntryIndirect
Mark the entry as a declare target indirect global.
@ OMPTargetGlobalVarEntryLink
Mark the entry as a to declare target link.
@ OMPTargetGlobalVarEntryTo
Mark the entry as a to declare target.
@ OMPTargetGlobalVarEntryIndirectVTable
Mark the entry as a declare target indirect vtable.
function_ref< void(const TargetRegionEntryInfo &EntryInfo, const OffloadEntryInfoTargetRegion &)> OffloadTargetRegionEntryInfoActTy
brief Applies action Action on all registered entries.
bool hasDeviceGlobalVarEntryInfo(StringRef VarName) const
Checks if the variable with the given name has been registered already.
LLVM_ABI bool empty() const
Return true if a there are no entries defined.
std::optional< bool > IsTargetDevice
Flag to define whether to generate code for the role of the OpenMP host (if set to false) or device (...
std::optional< bool > IsGPU
Flag for specifying if the compilation is done for an accelerator.
LLVM_ABI int64_t getRequiresFlags() const
Returns requires directive clauses as flags compatible with those expected by libomptarget.
std::optional< bool > OpenMPOffloadMandatory
Flag for specifying if offloading is mandatory.
LLVM_ABI void setHasRequiresReverseOffload(bool Value)
LLVM_ABI bool hasRequiresUnifiedSharedMemory() const
LLVM_ABI void setHasRequiresUnifiedSharedMemory(bool Value)
unsigned getDefaultTargetAS() const
LLVM_ABI bool hasRequiresDynamicAllocators() const
LLVM_ABI void setHasRequiresUnifiedAddress(bool Value)
LLVM_ABI void setHasRequiresDynamicAllocators(bool Value)
LLVM_ABI bool hasRequiresReverseOffload() const
LLVM_ABI bool hasRequiresUnifiedAddress() const
Struct that keeps the information that should be kept throughout a 'target data' region.
An interface to create LLVM-IR for OpenMP directives.
LLVM_ABI InsertPointOrErrorTy createOrderedThreadsSimd(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool IsThreads)
Generator for 'omp ordered [threads | simd]'.
LLVM_ABI void emitAArch64DeclareSimdFunction(llvm::Function *Fn, unsigned VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch, char ISA, unsigned NarrowestDataSize, bool OutputBecomesInput)
Emit AArch64 vector-function ABI attributes for a declare simd function.
LLVM_ABI Constant * getOrCreateIdent(Constant *SrcLocStr, uint32_t SrcLocStrSize, omp::IdentFlag Flags=omp::IdentFlag(0), unsigned Reserve2Flags=0)
Return an ident_t* encoding the source location SrcLocStr and Flags.
LLVM_ABI void registerDeclareTargetGlobalReplacement(GlobalValue *Original, GlobalValue *Replacement)
Register a module-scope replacement of a declare target global variable.
LLVM_ABI FunctionCallee getOrCreateRuntimeFunction(Module &M, omp::RuntimeFunction FnID)
Return the function declaration for the runtime function with FnID.
LLVM_ABI InsertPointOrErrorTy createCancel(const LocationDescription &Loc, Value *IfCondition, omp::Directive CanceledDirective)
Generator for 'omp cancel'.
std::function< Expected< Function * >(StringRef FunctionName)> FunctionGenCallback
Functions used to generate a function with the given name.
LLVM_ABI CallInst * createOMPAllocShared(const LocationDescription &Loc, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_alloc_shared.
ReductionGenCBKind
Enum class for the RedctionGen CallBack type to be used.
LLVM_ABI CanonicalLoopInfo * collapseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, InsertPointTy ComputeIP)
Collapse a loop nest 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.
Module & M
The underlying LLVM-IR module.
StringMap< Constant * > SrcLocStrMap
Map to remember source location strings.
LLVM_ABI void createMapperAllocas(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumOperands, struct MapperAllocas &MapperAllocas)
Create the allocas instruction used in call to mapper functions.
SmallVector< DeclareTargetGlobalReplacement, 8 > DeclareTargetGlobalReplacements
Collection of declare target globals to rewrite uses of during device module finalizaiton.
LLVM_ABI Constant * getOrCreateSrcLocStr(StringRef LocStr, uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the source location LocStr.
LLVM_ABI Error emitTargetRegionFunction(TargetRegionEntryInfo &EntryInfo, FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry, Function *&OutlinedFn, Constant *&OutlinedFnID)
Create a unique name for the entry function using the source location information of the current targ...
LLVM_ABI InsertPointOrErrorTy createIteratorLoop(LocationDescription Loc, llvm::Value *TripCount, IteratorBodyGenTy BodyGen, llvm::StringRef Name="iterator")
Create a canonical iterator loop at the current insertion point.
LLVM_ABI Expected< SmallVector< llvm::CanonicalLoopInfo * > > createCanonicalScanLoops(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo)
Generator for the control flow structure of an OpenMP canonical loops if the parent directive has an ...
LLVM_ABI FunctionCallee createDispatchFiniFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_fini_* runtime function for the specified size IVSize and sign IVSigned.
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> TargetBodyGenCallbackTy
LLVM_ABI void unrollLoopPartial(DebugLoc DL, CanonicalLoopInfo *Loop, int32_t Factor, CanonicalLoopInfo **UnrolledCLI)
Partially unroll a loop.
function_ref< Error(Value *DeviceID, Value *RTLoc, IRBuilderBase::InsertPoint TargetTaskAllocaIP)> TargetTaskBodyCallbackTy
Callback type for generating the bodies of device directives that require outer target tasks (e....
Expected< MapInfosTy & > MapInfosOrErrorTy
bool HandleFPNegZero
Emit atomic compare for constructs: — Only scalar data types cond-expr-stmt: x = x ordop expr ?
LLVM_ABI void emitTaskyieldImpl(const LocationDescription &Loc)
Generate a taskyield runtime call.
LLVM_ABI void emitMapperCall(const LocationDescription &Loc, Function *MapperFunc, Value *SrcLocInfo, Value *MaptypesArg, Value *MapnamesArg, struct MapperAllocas &MapperAllocas, int64_t DeviceID, unsigned NumOperands)
Create the call for the target mapper function.
LLVM_ABI InsertPointOrErrorTy createDistribute(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for #omp distribute
function_ref< Expected< Function * >(unsigned int)> CustomMapperCallbackTy
LLVM_ABI InsertPointTy createOrderedDepend(const LocationDescription &Loc, InsertPointTy AllocaIP, unsigned NumLoops, ArrayRef< llvm::Value * > StoreValues, const Twine &Name, bool IsDependSource)
Generator for 'omp ordered depend (source | sink)'.
LLVM_ABI InsertPointTy createCopyinClauseBlocks(InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr, llvm::IntegerType *IntPtrTy, bool BranchtoEnd=true)
Generate conditional branch and relevant BasicBlocks through which private threads copy the 'copyin' ...
function_ref< InsertPointOrErrorTy( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Original, Value &Inner, Value *&ReplVal)> PrivatizeCallbackTy
Callback type for variable privatization (think copy & default constructor).
LLVM_ABI bool isFinalized()
Check whether the finalize function has already run.
SmallVector< FinalizationInfo, 8 > FinalizationStack
The finalization stack made up of finalize callbacks currently in-flight, wrapped into FinalizationIn...
LLVM_ABI std::vector< CanonicalLoopInfo * > tileLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops, ArrayRef< Value * > TileSizes)
Tile a loop nest.
LLVM_ABI CallInst * createOMPInteropInit(const LocationDescription &Loc, Value *InteropVar, omp::OMPInteropType InteropType, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_init.
LLVM_ABI Error emitIfClause(Value *Cond, BodyGenCallbackTy ThenGen, BodyGenCallbackTy ElseGen, InsertPointTy AllocaIP={}, ArrayRef< BasicBlock * > DeallocBlocks={})
Emits code for OpenMP 'if' clause using specified BodyGenCallbackTy Here is the logic: if (Cond) { Th...
LLVM_ABI void finalize(Function *Fn=nullptr)
Finalize the underlying module, e.g., by outlining regions.
LLVM_ABI Function * getOrCreateRuntimeFunctionPtr(omp::RuntimeFunction FnID)
void addOutlineInfo(std::unique_ptr< OutlineInfo > &&OI)
Add a new region that will be outlined later.
LLVM_ABI InsertPointTy createTargetInit(const LocationDescription &Loc, const llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
Create a runtime call for kmpc_target_init.
LLVM_ABI InsertPointOrErrorTy createReductions(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false)
Generator for 'omp reduction'.
const Triple T
The target triple of the underlying module.
DenseMap< std::pair< Constant *, uint64_t >, Constant * > IdentMap
Map to remember existing ident_t*.
LLVM_ABI CallInst * createOMPFree(const LocationDescription &Loc, Value *Addr, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_free.
LLVM_ABI InsertPointOrErrorTy createReductionsGPU(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< ReductionInfo > ReductionInfos, ArrayRef< bool > IsByRef, bool IsNoWait=false, bool IsTeamsReduction=false, bool IsSPMD=false, ReductionGenCBKind ReductionGenCBKind=ReductionGenCBKind::MLIR, std::optional< omp::GV > GridValue={}, Value *SrcLocInfo=nullptr)
Design of OpenMP reductions on the GPU.
LLVM_ABI FunctionCallee createForStaticInitFunction(unsigned IVSize, bool IVSigned, bool IsGPUDistribute)
Returns __kmpc_for_static_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI CallInst * createOMPAlloc(const LocationDescription &Loc, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_alloc.
LLVM_ABI void emitNonContiguousDescriptor(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, MapInfosTy &CombinedInfo, TargetDataInfo &Info)
Emit an array of struct descriptors to be assigned to the offload args.
LLVM_ABI InsertPointOrErrorTy createSection(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp section'.
LLVM_ABI InsertPointOrErrorTy createTaskgroup(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB)
Generator for the taskgroup construct.
LLVM_ABI InsertPointOrErrorTy createParallel(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition, Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable)
Generator for 'omp parallel'.
function_ref< InsertPointOrErrorTy(InsertPointTy)> EmitFallbackCallbackTy
Callback function type for functions emitting the host fallback code that is executed when the kernel...
static LLVM_ABI TargetRegionEntryInfo getTargetEntryUniqueInfo(FileIdentifierInfoCallbackTy CallBack, vfs::FileSystem &VFS, StringRef ParentName="")
Creates a unique info for a target entry when provided a filename and line number from.
LLVM_ABI void emitTaskDependency(IRBuilderBase &Builder, Value *Entry, const DependData &Dep)
Store one kmp_depend_info entry at the given Entry pointer.
LLVM_ABI void emitBlock(BasicBlock *BB, Function *CurFn, bool IsFinished=false)
LLVM_ABI Value * getOrCreateThreadID(Value *Ident)
Return the current thread ID.
LLVM_ABI InsertPointOrErrorTy createMaster(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB)
Generator for 'omp master'.
LLVM_ABI InsertPointOrErrorTy createTargetData(const LocationDescription &Loc, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks, Value *DeviceID, Value *IfCond, TargetDataInfo &Info, GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB, omp::RuntimeFunction *MapperFunc=nullptr, function_ref< InsertPointOrErrorTy(InsertPointTy CodeGenIP, BodyGenTy BodyGenType)> BodyGenCB=nullptr, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr, Value *SrcLocInfo=nullptr)
Generator for 'omp target data'.
LLVM_ABI CallInst * createRuntimeFunctionCall(FunctionCallee Callee, ArrayRef< Value * > Args, StringRef Name="")
LLVM_ABI InsertPointOrErrorTy emitKernelLaunch(const LocationDescription &Loc, Value *OutlinedFnID, EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args, Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP)
Generate a target region entry call and host fallback call.
StringMap< GlobalVariable *, BumpPtrAllocator > InternalVars
An ordered map of auto-generated variables to their unique names.
LLVM_ABI InsertPointOrErrorTy createCancellationPoint(const LocationDescription &Loc, omp::Directive CanceledDirective)
Generator for 'omp cancellation point'.
LLVM_ABI CallInst * createOMPAlignedAlloc(const LocationDescription &Loc, Value *Align, Value *Size, Value *Allocator, std::string Name="")
Create a runtime call for kmpc_align_alloc.
LLVM_ABI FunctionCallee createDispatchInitFunction(unsigned IVSize, bool IVSigned)
Returns __kmpc_dispatch_init_* runtime function for the specified size IVSize and sign IVSigned.
LLVM_ABI InsertPointOrErrorTy createScan(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< llvm::Value * > ScanVars, ArrayRef< llvm::Type * > ScanVarsType, bool IsInclusive, ScanInfo *ScanRedInfo)
This directive split and directs the control flow to input phase blocks or scan phase blocks based on...
LLVM_ABI CallInst * createOMPFreeShared(const LocationDescription &Loc, Value *Addr, Value *Size, const Twine &Name=Twine(""))
Create a runtime call for kmpc_free_shared.
LLVM_ABI CallInst * createOMPInteropUse(const LocationDescription &Loc, Value *InteropVar, Value *Device, Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause)
Create a runtime call for __tgt_interop_use.
IRBuilder<>::InsertPoint InsertPointTy
Type used throughout for insertion points.
LLVM_ABI GlobalVariable * getOrCreateInternalVariable(Type *Ty, const StringRef &Name, std::optional< unsigned > AddressSpace={})
Gets (if variable with the given name already exist) or creates internal global variable with the spe...
LLVM_ABI GlobalVariable * createOffloadMapnames(SmallVectorImpl< llvm::Constant * > &Names, std::string VarName)
Create the global variable holding the offload names information.
LLVM_ABI InsertPointOrErrorTy createTask(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< BasicBlock * > DeallocBlocks, BodyGenCallbackTy BodyGenCB, bool Tied=true, Value *Final=nullptr, Value *IfCondition=nullptr, const DependenciesInfo &Dependencies={}, const AffinityData &Affinities={}, bool Mergeable=false, Value *EventHandle=nullptr, Value *Priority=nullptr, bool FreeAgent=false)
Generator for #omp taskloop
std::forward_list< ScanInfo > ScanInfos
Collection of owned ScanInfo objects that eventually need to be free'd.
static LLVM_ABI void writeTeamsForKernel(const Triple &T, Function &Kernel, int32_t LB, int32_t UB)
LLVM_ABI Value * calculateCanonicalLoopTripCount(const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop, const Twine &Name="loop")
Calculate the trip count of a canonical loop.
LLVM_ABI InsertPointOrErrorTy createBarrier(const LocationDescription &Loc, omp::Directive Kind, bool ForceSimpleCall=false, bool CheckCancelFlag=true)
Emitter methods for OpenMP directives.
LLVM_ABI void setCorrectMemberOfFlag(omp::OpenMPOffloadMappingFlags &Flags, omp::OpenMPOffloadMappingFlags MemberOfFlag)
Given an initial flag set, this function modifies it to contain the passed in MemberOfFlag generated ...
LLVM_ABI Error emitOffloadingArraysAndArgs(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, TargetDataInfo &Info, TargetDataRTArgs &RTArgs, MapInfosTy &CombinedInfo, CustomMapperCallbackTy CustomMapperCB, bool IsNonContiguous=false, bool ForEndCall=false, function_ref< void(unsigned int, Value *)> DeviceAddrCB=nullptr)
Allocates memory for and populates the arrays required for offloading (offload_{baseptrs|ptrs|mappers...
LLVM_ABI Constant * getOrCreateDefaultSrcLocStr(uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the default source location.
LLVM_ABI InsertPointOrErrorTy createCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst)
Generator for 'omp critical'.
LLVM_ABI void createError(const LocationDescription &Loc, bool IsFatal, Value *Message)
Generate a call to the runtime to emit the diagnostic of an OpenMP error directive with at(execution)...
LLVM_ABI void createOffloadEntry(Constant *ID, Constant *Addr, uint64_t Size, int32_t Flags, GlobalValue::LinkageTypes, StringRef Name="")
Creates offloading entry for the provided entry ID ID, address Addr, size Size, and flags Flags.
static LLVM_ABI unsigned getOpenMPDefaultSimdAlign(const Triple &TargetTriple, const StringMap< bool > &Features)
Get the default alignment value for given target.
LLVM_ABI unsigned getFlagMemberOffset()
Get the offset of the OMP_MAP_MEMBER_OF field.
LLVM_ABI InsertPointOrErrorTy createAtomicCapture(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, AtomicOpValue &V, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool UpdateExpr, bool IsPostfixUpdate, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: — Only Scalar data types V = X; X = X BinOp Expr ,...
LLVM_ABI CanonicalLoopInfo * createLoopSkeleton(DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore, BasicBlock *PostInsertBefore, const Twine &Name={}, bool IsCollapsed=false)
Create the control flow structure of a canonical OpenMP loop.
LLVM_ABI void createOffloadEntriesAndInfoMetadata(EmitMetadataErrorReportFunctionTy &ErrorReportFunction)
LLVM_ABI void applySimd(CanonicalLoopInfo *Loop, MapVector< Value *, Value * > AlignedVars, Value *IfCond, omp::OrderKind Order, ConstantInt *Simdlen, ConstantInt *Safelen)
Add metadata to simd-ize a loop.
SmallVector< std::unique_ptr< OutlineInfo >, 16 > OutlineInfos
Collection of regions that need to be outlined during finalization.
LLVM_ABI InsertPointOrErrorTy createAtomicUpdate(const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X, Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp, AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr, bool IsIgnoreDenormalMode=false, bool IsFineGrainedMemory=false, bool IsRemoteMemory=false)
Emit atomic update for constructs: X = X BinOp Expr ,or X = Expr BinOp X For complex Operations: X = ...
std::function< std::tuple< std::string, uint64_t >()> FileIdentifierInfoCallbackTy
bool isLastFinalizationInfoCancellable(omp::Directive DK)
Return true if the last entry in the finalization stack is of kind DK and cancellable.
LLVM_ABI InsertPointTy emitTargetKernel(const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return, Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads, Value *HostPtr, ArrayRef< Value * > KernelArgs)
Generate a target region entry call.
LLVM_ABI GlobalVariable * createOffloadMaptypes(SmallVectorImpl< uint64_t > &Mappings, std::string VarName)
Create the global variable holding the offload mappings information.
LLVM_ABI Expected< Function * > emitUserDefinedMapper(function_ref< MapInfosOrErrorTy(InsertPointTy CodeGenIP, llvm::Value *PtrPHI, llvm::Value *BeginArg)> PrivAndGenMapInfoCB, llvm::Type *ElemTy, StringRef FuncName, CustomMapperCallbackTy CustomMapperCB, bool PreserveMemberOfFlags=false, bool PropagatePresentToPointee=false)
Emit the user-defined mapper function.
LLVM_ABI CallInst * createCachedThreadPrivate(const LocationDescription &Loc, llvm::Value *Pointer, llvm::ConstantInt *Size, const llvm::Twine &Name=Twine(""))
Create a runtime call for kmpc_threadprivate_cached.
IRBuilder Builder
The LLVM-IR Builder used to create IR.
LLVM_ABI GlobalValue * createGlobalFlag(unsigned Value, StringRef Name)
Create a hidden global flag Name in the module with initial value Value.
LLVM_ABI void emitOffloadingArraysArgument(IRBuilderBase &Builder, OpenMPIRBuilder::TargetDataRTArgs &RTArgs, OpenMPIRBuilder::TargetDataInfo &Info, bool ForEndCall=false)
Emit the arguments to be passed to the runtime library based on the arrays of base pointers,...
LLVM_ABI InsertPointOrErrorTy createMasked(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, Value *Filter)
Generator for 'omp masked'.
LLVM_ABI Expected< CanonicalLoopInfo * > createCanonicalLoop(const LocationDescription &Loc, LoopBodyGenCallbackTy BodyGenCB, Value *TripCount, const Twine &Name="loop")
Generator for the control flow structure of an OpenMP canonical loop.
function_ref< Expected< InsertPointTy >( InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value *DestPtr, Value *SrcPtr)> TaskDupCallbackTy
Callback type for task duplication function code generation.
LLVM_ABI Value * getSizeInBytes(Value *BasePtr)
Computes the size of type in bytes.
llvm::function_ref< llvm::Error( InsertPointTy BodyIP, llvm::Value *LinearIV)> IteratorBodyGenTy
LLVM_ABI FunctionCallee createDispatchDeinitFunction()
Returns __kmpc_dispatch_deinit runtime function.
LLVM_ABI void registerTargetGlobalVariable(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage, Type *LlvmPtrTy, Constant *Addr)
Registers a target variable for device or host.
LLVM_ABI void createTargetDeinit(const LocationDescription &Loc, int32_t TeamsReductionDataSize=0)
Create a runtime call for kmpc_target_deinit.
BodyGenTy
Type of BodyGen to use for region codegen.
LLVM_ABI CanonicalLoopInfo * fuseLoops(DebugLoc DL, ArrayRef< CanonicalLoopInfo * > Loops)
Fuse a sequence of loops.
LLVM_ABI void emitX86DeclareSimdFunction(llvm::Function *Fn, unsigned NumElements, const llvm::APSInt &VLENVal, llvm::ArrayRef< DeclareSimdAttrTy > ParamAttrs, DeclareSimdBranch Branch)
Emit x86 vector-function ABI attributes for a declare simd function.
SmallVector< llvm::Function *, 16 > ConstantAllocaRaiseCandidates
A collection of candidate target functions that's constant allocas will attempt to be raised on a cal...
OffloadEntriesInfoManager OffloadInfoManager
Info manager to keep track of target regions.
static LLVM_ABI std::pair< int32_t, int32_t > readTeamBoundsForKernel(const Triple &T, Function &Kernel)
Read/write a bounds on teams for Kernel.
const std::string ompOffloadInfoName
OMP Offload Info Metadata name string.
LLVM_ABI InsertPointOrErrorTy applyWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP, bool NeedsBarrier, llvm::omp::ScheduleKind SchedKind=llvm::omp::OMP_SCHEDULE_Default, Value *ChunkSize=nullptr, bool HasSimdModifier=false, bool HasMonotonicModifier=false, bool HasNonmonotonicModifier=false, bool HasOrderedClause=false, omp::WorksharingLoopType LoopType=omp::WorksharingLoopType::ForStaticLoop, bool NoLoop=false, bool HasDistSchedule=false, Value *DistScheduleChunkSize=nullptr, bool NeedsLastIter=false)
Modifies the canonical loop to be a workshare loop.
Expected< InsertPointTy > InsertPointOrErrorTy
Type used to represent an insertion point or an error value.
LLVM_ABI InsertPointTy createCopyPrivate(const LocationDescription &Loc, llvm::Value *BufSize, llvm::Value *CpyBuf, llvm::Value *CpyFn, llvm::Value *DidIt)
Generator for __kmpc_copyprivate.
LLVM_ABI InsertPointOrErrorTy createSections(const LocationDescription &Loc, InsertPointTy AllocaIP, ArrayRef< StorableBodyGenCallbackTy > SectionCBs, PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait)
Generator for 'omp sections'.
std::function< void(EmitMetadataErrorKind, TargetRegionEntryInfo)> EmitMetadataErrorReportFunctionTy
Callback function type.
function_ref< InsertPointOrErrorTy( Argument &Arg, Value *Input, Value *&RetVal, InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< InsertPointTy > DeallocIPs)> TargetGenArgAccessorsCallbackTy
LLVM_ABI Expected< ScanInfo * > scanInfoInitialize()
Creates a ScanInfo object, allocates and returns the pointer.
LLVM_ABI InsertPointOrErrorTy emitTargetTask(TargetTaskBodyCallbackTy TaskBodyCB, Value *DeviceID, Value *RTLoc, OpenMPIRBuilder::InsertPointTy AllocaIP, const DependenciesInfo &Dependencies, const TargetDataRTArgs &RTArgs, bool HasNoWait)
Generate a target-task for the target construct.
LLVM_ABI InsertPointTy createAtomicRead(const LocationDescription &Loc, AtomicOpValue &X, AtomicOpValue &V, AtomicOrdering AO, InsertPointTy AllocaIP)
Emit atomic Read for : V = X — Only Scalar data types.
function_ref< Error(InsertPointTy AllocaIP, InsertPointTy CodeGenIP, ArrayRef< BasicBlock * > DeallocBlocks)> BodyGenCallbackTy
Callback type for body (=inner region) code generation.
bool updateToLocation(const LocationDescription &Loc)
Update the internal location to Loc.
LLVM_ABI void createFlush(const LocationDescription &Loc)
Generator for 'omp flush'.
LLVM_ABI Constant * getAddrOfDeclareTargetVar(OffloadEntriesInfoManager::OMPTargetGlobalVarEntryKind CaptureClause, OffloadEntriesInfoManager::OMPTargetDeviceClauseKind DeviceClause, bool IsDeclaration, bool IsExternallyVisible, TargetRegionEntryInfo EntryInfo, StringRef MangledName, std::vector< GlobalVariable * > &GeneratedRefs, bool OpenMPSIMD, std::vector< Triple > TargetTriple, Type *LlvmPtrTy, std::function< Constant *()> GlobalInitializer, std::function< GlobalValue::LinkageTypes()> VariableLinkage)
Retrieve (or create if non-existent) the address of a declare target variable, used in conjunction wi...
LLVM_ABI void createTaskwait(const LocationDescription &Loc, DependenciesInfo Dependencies={}, bool IsNowait=false)
Generator for 'omp taskwait'.
origPtr *with the address space normalization required by the runtime entry point *The NULL descriptor makes the runtime walk the enclosing taskgroups to *find the matching task_reduction registration for the item The lookups *are emitted at p Loc
EmitMetadataErrorKind
The kind of errors that can occur when emitting the offload entries and metadata.
unsigned getOpcode() const
Return the opcode for this Instruction or ConstantExpr.
Definition Operator.h:43
The optimization diagnostic interface.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
Pseudo-analysis pass that exposes the PassInstrumentation to pass managers.
Class to represent pointers.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:887
PostDominatorTree Class - Concrete subclass of DominatorTree that is used to compute the post-dominat...
Analysis pass that exposes the ScalarEvolution for a function.
LLVM_ABI ScalarEvolution run(Function &F, FunctionAnalysisManager &AM)
The main scalar evolution driver.
ScanInfo holds the information to assist in lowering of Scan reduction.
llvm::SmallDenseMap< llvm::Value *, llvm::Value * > * ScanBuffPtrs
Maps the private reduction variable to the pointer of the temporary buffer.
llvm::BasicBlock * OMPScanLoopExit
Exit block of loop body.
llvm::Value * IV
Keeps track of value of iteration variable for input/scan loop to be used for Scan directive lowering...
llvm::BasicBlock * OMPAfterScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanInit
Block before loop body where scan initializations are done.
llvm::BasicBlock * OMPBeforeScanBlock
Dominates the body of the loop before scan directive.
llvm::BasicBlock * OMPScanFinish
Block after loop body where scan finalizations are done.
llvm::Value * Span
Stores the span of canonical loop being lowered to be used for temporary buffer allocation or Finaliz...
bool OMPFirstScanLoop
If true, it indicates Input phase is lowered; else it indicates ScanPhase is lowered.
llvm::BasicBlock * OMPScanDispatch
Controls the flow to before or after scan blocks.
A vector that has set insertion semantics.
Definition SetVector.h:57
bool remove_if(UnaryPredicate P)
Remove items from the set vector based on a predicate function.
Definition SetVector.h:236
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallBitVector & set()
bool test(unsigned Idx) const
Returns true if bit Idx is set.
bool all() const
Returns true if all bits are set.
bool any() const
Returns true if any bit is set.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
void append(StringRef RHS)
Append from a StringRef.
Definition SmallString.h:68
StringRef str() const
Explicit conversion to StringRef.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void resize(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
void setAlignment(Align Align)
void setAtomic(AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System)
Sets the ordering constraint and the synchronization scope ID of this store instruction.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:129
ValueTy lookup(StringRef Key) const
lookup - Return the entry for the specified key, or a default constructed value if no such entry exis...
Definition StringMap.h:250
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
size_t count(char C) const
Return the number of occurrences of C in the string.
Definition StringRef.h:471
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition StringRef.h:270
StringRef drop_back(size_t N=1) const
Return a StringRef equal to 'this' but with the last N elements dropped.
Definition StringRef.h:642
Class to represent struct types.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:467
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:662
Type * getElementType(unsigned N) const
Multiway switch.
LLVM_ABI void addCase(ConstantInt *OnVal, BasicBlock *Dest)
Add an entry to the switch instruction.
Analysis pass providing the TargetTransformInfo.
LLVM_ABI Result run(const Function &F, FunctionAnalysisManager &)
Analysis pass providing the TargetLibraryInfo.
Target - Wrapper for Target specific information.
TargetMachine * createTargetMachine(const Triple &TT, StringRef CPU, StringRef Features, const TargetOptions &Options, std::optional< Reloc::Model > RM, std::optional< CodeModel::Model > CM=std::nullopt, CodeGenOptLevel OL=CodeGenOptLevel::Default, bool JIT=false) const
createTargetMachine - Create a target specific machine implementation for the specified Triple.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isPPC() const
Tests whether the target is PowerPC (32- or 64-bit LE or BE).
Definition Triple.h:1141
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
Definition Triple.h:1198
bool isWasm() const
Tests whether the target is wasm (32- and 64-bit).
Definition Triple.h:1212
bool isSystemZ() const
Tests whether the target is SystemZ.
Definition Triple.h:1195
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM_ABI std::string str() const
Return the twine contents as a std::string.
Definition Twine.cpp:17
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
Definition Type.cpp:300
LLVM_ABI unsigned getIntegerBitWidth() const
LLVM_ABI Type * getStructElementType(unsigned N) const
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:277
bool isFloatTy() const
Return true if this is 'float', a 32-bit IEEE fp type.
Definition Type.h:155
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
bool isStructTy() const
True if this is an instance of StructType.
Definition Type.h:271
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:222
bool isDoubleTy() const
Return true if this is 'double', a 64-bit IEEE fp type.
Definition Type.h:158
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:252
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:303
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
Unconditional Branch instruction.
static UncondBrInst * Create(BasicBlock *Target, InsertPosition InsertBefore=nullptr)
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
This function has undefined behavior.
Produce an estimate of the unrolled cost of the specified loop.
Definition UnrollLoop.h:150
LLVM_ABI bool canUnroll(OptimizationRemarkEmitter *ORE=nullptr, const Loop *L=nullptr) const
Whether it is legal to unroll this loop.
uint64_t getRolledLoopSize() const
Definition UnrollLoop.h:174
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
void setOperand(unsigned i, Value *Val)
Definition User.h:212
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:390
bool hasOneUse() const
Return true if there is exactly one use of this value.
Definition Value.h:441
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:549
iterator_range< user_iterator > users()
Definition Value.h:428
User * user_back()
Definition Value.h:414
LLVM_ABI Align getPointerAlignment(const DataLayout &DL) const
Returns an alignment of the pointer value.
Definition Value.cpp:986
LLVM_ABI bool hasNUses(unsigned N) const
Return true if this Value has exactly N uses.
Definition Value.cpp:143
LLVM_ABI User * getUniqueUndroppableUser()
Return true if there is exactly one unique user of this value that cannot be dropped (that user can h...
Definition Value.cpp:181
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:708
bool use_empty() const
Definition Value.h:348
LLVM_ABI bool replaceUsesWithIf(Value *New, llvm::function_ref< bool(Use &U)> ShouldReplace)
Go through the uses list for this definition and make each use point to "V" if the callback ShouldRep...
Definition Value.cpp:557
iterator_range< use_iterator > uses()
Definition Value.h:382
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:315
An efficient, type-erasing, non-owning reference to a callable.
const ParentTy * getParent() const
Definition ilist_node.h:34
self_iterator getIterator()
Definition ilist_node.h:123
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
A raw_ostream that writes to an SmallVector or SmallString.
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
CallInst * Call
#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()
static constexpr GV SPIRVGridValues
For generic SPIR-V GPUs.
OMPDynGroupprivateFallbackType
The fallback types for the dyn_groupprivate clause.
static constexpr GV NVPTXGridValues
For Nvidia GPUs.
@ OMP_TGT_EXEC_MODE_SPMD_NO_LOOP
Function * Kernel
Summary of a kernel (=entry point for target offloading).
Definition OpenMPOpt.h:21
WorksharingLoopType
A type of worksharing loop construct.
OMPAtomicCompareOp
Atomic compare operations. Currently OpenMP only supports ==, >, and <.
EnumSet< Property > Properties
NodeAddr< PhiNode * > Phi
Definition RDFGraph.h:390
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:316
LLVM_ABI BasicBlock * splitBBWithSuffix(IRBuilderBase &Builder, bool CreateBranch, llvm::Twine Suffix=".split")
Like splitBB, but reuses the current block's name for the new name.
@ Offset
Definition DWP.cpp:577
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
Definition STLExtras.h:846
LLVM_ABI unsigned computeUnrollCount(Loop *L, const TargetTransformInfo &TTI, DominatorTree &DT, LoopInfo *LI, AssumptionCache *AC, ScalarEvolution &SE, const SmallPtrSetImpl< const Value * > &EphValues, OptimizationRemarkEmitter *ORE, unsigned TripCount, unsigned MaxTripCount, bool MaxOrZero, unsigned TripMultiple, const UnrollCostEstimator &UCE, TargetTransformInfo::UnrollingPreferences &UP, TargetTransformInfo::PeelingPreferences &PP)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1755
hash_code hash_value(const FixedPointSemantics &Val)
LLVM_ABI Expected< std::unique_ptr< Module > > parseBitcodeFile(MemoryBufferRef Buffer, LLVMContext &Context, ParserCallbacks Callbacks={})
Read the specified bitcode file, returning the module.
detail::zippy< detail::zip_first, T, U, Args... > zip_equal(T &&t, U &&u, Args &&...args)
zip iterator that assumes that all iteratees have the same length.
Definition STLExtras.h:856
LLVM_ABI BasicBlock * CloneBasicBlock(const BasicBlock *BB, ValueToValueMapTy &VMap, const Twine &NameSuffix="", Function *F=nullptr, ClonedCodeInfo *CodeInfo=nullptr, bool MapAtoms=true)
Return a copy of the specified basic block, but without embedding the block into a particular functio...
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2570
unsigned getPointerAddressSpace(const Type *T)
Definition SPIRVUtils.h:388
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto successors(const MachineBasicBlock *BB)
@ Load
The value being inserted comes from a load (InsertElement only).
@ Store
The extracted value is stored (ExtractElement only).
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
constexpr from_range_t from_range
auto dyn_cast_if_present(const Y &Val)
dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a null (or none in the case ...
Definition Casting.h:732
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
LLVM_ABI BasicBlock * splitBB(IRBuilderBase::InsertPoint IP, bool CreateBranch, DebugLoc DL, llvm::Twine Name={})
Split a BasicBlock at an InsertPoint, even if the block is degenerate (missing the terminator).
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h:2224
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
LLVM_ABI TargetTransformInfo::UnrollingPreferences gatherUnrollingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, llvm::OptimizationRemarkEmitter &ORE, int OptLevel, std::optional< unsigned > UserThreshold, std::optional< bool > UserAllowPartial, std::optional< bool > UserRuntime, std::optional< bool > UserUpperBound, std::optional< unsigned > UserFullUnrollMaxCount)
Gather the various unrolling parameters based on the defaults, compiler flags, TTI overrides and user...
std::string utostr(uint64_t X, bool isNeg=false)
void * PointerTy
ErrorOr< T > expectedToErrorOrAndEmitErrors(LLVMContext &Ctx, Expected< T > Val)
bool isa_and_nonnull(const Y &Val)
Definition Casting.h:676
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 TargetTransformInfo::PeelingPreferences gatherPeelingPreferences(Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI, std::optional< bool > UserAllowPeeling, std::optional< bool > UserAllowProfileBasedPeeling, bool UnrollingSpecficValues=false)
LLVM_ABI void SplitBlockAndInsertIfThenElse(Value *Cond, BasicBlock::iterator SplitBefore, Instruction **ThenTerm, Instruction **ElseTerm, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr)
SplitBlockAndInsertIfThenElse is similar to SplitBlockAndInsertIfThen, but also creates the ElseBlock...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1769
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:280
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
AtomicOrdering
Atomic ordering for LLVM's memory model.
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h:389
TargetTransformInfo TTI
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
LLVM_ABI bool MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, MemoryDependenceResults *MemDep=nullptr, bool PredecessorWithTwoSuccessors=false, DominatorTree *DT=nullptr)
Attempts to merge a block into its predecessor, if possible.
@ Mul
Product of integers.
@ Add
Sum of integers.
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
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
static LLVM_ABI void collectEphemeralValues(const Loop *L, AssumptionCache *AC, SmallPtrSetImpl< const Value * > &EphValues)
Collect a loop's ephemeral values (those used only by an assume or similar intrinsics in the loop).
This struct is a compact representation of a valid (power of two) or undefined (0) alignment.
Definition Alignment.h:111
A struct to pack the relevant information for an OpenMP affinity clause.
a struct to pack relevant information while generating atomic Ops
A struct to pack the relevant information for an OpenMP depend clause.
omp::RTLDependenceKindTy DepKind
A struct to pack static and dynamic dependency information for a task.
LLVM_ABI Error mergeFiniBB(IRBuilderBase &Builder, BasicBlock *ExistingFiniBB)
For cases where there is an unavoidable existing finalization block (e.g.
LLVM_ABI Expected< BasicBlock * > getFiniBB(IRBuilderBase &Builder)
The basic block to which control should be transferred to implement the FiniCB.
Description of a LLVM-IR insertion point (IP) and a debug/source location (filename,...
This structure contains combined information generated for mappable clauses, including base pointers,...
MapDeviceInfoArrayTy DevicePointers
StructNonContiguousInfo NonContigInfo
Helper that contains information about regions we need to outline during finalization.
void collectBlocks(SmallPtrSetImpl< BasicBlock * > &BlockSet, SmallVectorImpl< BasicBlock * > &BlockVector)
Collect all blocks in between EntryBB and ExitBB in both the given vector and set.
virtual std::unique_ptr< CodeExtractor > createCodeExtractor(ArrayRef< BasicBlock * > Blocks, bool ArgsInZeroAddressSpace, Twine Suffix=Twine(""))
Create a CodeExtractor instance based on the information stored in this structure,...
Information about an OpenMP reduction.
EvalKind EvaluationKind
Reduction evaluation kind - scalar, complex or aggregate.
ReductionGenAtomicCBTy AtomicReductionGen
Callback for generating the atomic reduction body, may be null.
ReductionGenCBTy ReductionGen
Callback for generating the reduction body.
Value * Variable
Reduction variable of pointer type.
Value * PrivateVariable
Thread-private partial reduction variable.
ReductionGenClangCBTy ReductionGenClang
Clang callback for generating the reduction body.
Type * ElementType
Reduction element type, must match pointee type of variable.
ReductionGenDataPtrPtrCBTy DataPtrPtrGen
Container for the arguments used to pass data to the runtime library.
Value * SizesArray
The array of sizes passed to the runtime library.
Value * PointersArray
The array of section pointers passed to the runtime library.
Value * MappersArray
The array of user-defined mappers passed to the runtime library.
Value * MapTypesArrayEnd
The array of map types passed to the runtime library for the end of the region, or nullptr if there a...
Value * BasePointersArray
The array of base pointer passed to the runtime library.
Value * MapTypesArray
The array of map types passed to the runtime library for the beginning of the region or for the entir...
Value * MapNamesArray
The array of original declaration names of mapped pointers sent to the runtime library for debugging.
Data structure that contains the needed information to construct the kernel args vector.
bool StrictBlocks
True if the kernel strictly requires the number of blocks and threads above to run.
ArrayRef< Value * > NumThreads
The number of threads.
TargetDataRTArgs RTArgs
Arguments passed to the runtime library.
Value * NumIterations
The number of iterations.
Value * DynCGroupMem
The size of the dynamic shared memory.
unsigned NumTargetItems
Number of arguments passed to the runtime library.
bool HasNoWait
True if the kernel has 'no wait' clause.
ArrayRef< Value * > NumTeams
The number of teams.
omp::OMPDynGroupprivateFallbackType DynCGroupMemFallback
The fallback mechanism for the shared memory.
Container to pass the default attributes with which a kernel must be launched, used to set kernel att...
Container to pass LLVM IR runtime values or constants related to the number of teams and threads with...
Value * DeviceID
Device ID value used in the kernel launch.
Value * LoopTripCount
Total number of iterations of the SPMD or Generic-SPMD kernel or null if it is a generic kernel.
SmallVector< Value * > MaxThreads
'parallel' construct 'num_threads' clause value, if present and it is an SPMD kernel.
Data structure to contain the information needed to uniquely identify a target entry.
static LLVM_ABI void getTargetRegionEntryFnName(SmallVectorImpl< char > &Name, StringRef ParentName, unsigned DeviceID, unsigned FileID, unsigned Line, unsigned Count)
static constexpr const char * KernelNamePrefix
The prefix used for kernel names.
static LLVM_ABI const Target * lookupTarget(const Triple &TheTriple, std::string &Error)
lookupTarget - Lookup a target based on a target triple.
Parameters that control the generic loop unrolling transformation.
unsigned Threshold
The cost threshold for the unrolled loop.
bool Force
Apply loop unroll on any kind of loop (mainly to loops that fail runtime unrolling).
unsigned PartialOptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size, like OptSizeThreshold,...
unsigned PartialThreshold
The cost threshold for the unrolled loop, like Threshold, but used for partial/runtime unrolling (set...
unsigned OptSizeThreshold
The cost threshold for the unrolled loop when optimizing for size (set to UINT_MAX to disable).
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...