LLVM 24.0.0git
OMPIRBuilder.cpp
Go to the documentation of this file.
1//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9///
10/// This file implements the OpenMPIRBuilder class, which is used as a
11/// convenient way to create LLVM instructions for OpenMP directives.
12///
13//===----------------------------------------------------------------------===//
14
17#include "llvm/ADT/SmallSet.h"
20#include "llvm/ADT/StringRef.h"
31#include "llvm/IR/Attributes.h"
32#include "llvm/IR/BasicBlock.h"
33#include "llvm/IR/CFG.h"
34#include "llvm/IR/CallingConv.h"
35#include "llvm/IR/Constant.h"
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/DIBuilder.h"
40#include "llvm/IR/Function.h"
42#include "llvm/IR/IRBuilder.h"
45#include "llvm/IR/LLVMContext.h"
46#include "llvm/IR/MDBuilder.h"
47#include "llvm/IR/Metadata.h"
49#include "llvm/IR/PassManager.h"
51#include "llvm/IR/Value.h"
54#include "llvm/Support/Error.h"
66
67#include <cstdint>
68#include <optional>
69
70#define DEBUG_TYPE "openmp-ir-builder"
71
72using namespace llvm;
73using namespace omp;
74
75static cl::opt<bool>
76 OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden,
77 cl::desc("Use optimistic attributes describing "
78 "'as-if' properties of runtime calls."),
79 cl::init(false));
80
82 "openmp-ir-builder-unroll-threshold-factor", cl::Hidden,
83 cl::desc("Factor for the unroll threshold to account for code "
84 "simplifications still taking place"),
85 cl::init(1.5));
86
88 "openmp-ir-builder-use-default-max-threads", cl::Hidden,
89 cl::desc("Use a default max threads if none is provided."), cl::init(true));
90
91#ifndef NDEBUG
92/// Return whether IP1 and IP2 are ambiguous, i.e. that inserting instructions
93/// at position IP1 may change the meaning of IP2 or vice-versa. This is because
94/// an InsertPoint stores the instruction before something is inserted. For
95/// instance, if both point to the same instruction, two IRBuilders alternating
96/// creating instruction will cause the instructions to be interleaved.
99 if (!IP1.isSet() || !IP2.isSet())
100 return false;
101 return IP1.getBlock() == IP2.getBlock() && IP1.getPoint() == IP2.getPoint();
102}
103
105 // Valid ordered/unordered and base algorithm combinations.
106 switch (SchedType & ~OMPScheduleType::MonotonicityMask) {
107 case OMPScheduleType::UnorderedStaticChunked:
108 case OMPScheduleType::UnorderedStatic:
109 case OMPScheduleType::UnorderedDynamicChunked:
110 case OMPScheduleType::UnorderedGuidedChunked:
111 case OMPScheduleType::UnorderedRuntime:
112 case OMPScheduleType::UnorderedAuto:
113 case OMPScheduleType::UnorderedTrapezoidal:
114 case OMPScheduleType::UnorderedGreedy:
115 case OMPScheduleType::UnorderedBalanced:
116 case OMPScheduleType::UnorderedGuidedIterativeChunked:
117 case OMPScheduleType::UnorderedGuidedAnalyticalChunked:
118 case OMPScheduleType::UnorderedSteal:
119 case OMPScheduleType::UnorderedStaticBalancedChunked:
120 case OMPScheduleType::UnorderedGuidedSimd:
121 case OMPScheduleType::UnorderedRuntimeSimd:
122 case OMPScheduleType::OrderedStaticChunked:
123 case OMPScheduleType::OrderedStatic:
124 case OMPScheduleType::OrderedDynamicChunked:
125 case OMPScheduleType::OrderedGuidedChunked:
126 case OMPScheduleType::OrderedRuntime:
127 case OMPScheduleType::OrderedAuto:
128 case OMPScheduleType::OrderdTrapezoidal:
129 case OMPScheduleType::NomergeUnorderedStaticChunked:
130 case OMPScheduleType::NomergeUnorderedStatic:
131 case OMPScheduleType::NomergeUnorderedDynamicChunked:
132 case OMPScheduleType::NomergeUnorderedGuidedChunked:
133 case OMPScheduleType::NomergeUnorderedRuntime:
134 case OMPScheduleType::NomergeUnorderedAuto:
135 case OMPScheduleType::NomergeUnorderedTrapezoidal:
136 case OMPScheduleType::NomergeUnorderedGreedy:
137 case OMPScheduleType::NomergeUnorderedBalanced:
138 case OMPScheduleType::NomergeUnorderedGuidedIterativeChunked:
139 case OMPScheduleType::NomergeUnorderedGuidedAnalyticalChunked:
140 case OMPScheduleType::NomergeUnorderedSteal:
141 case OMPScheduleType::NomergeOrderedStaticChunked:
142 case OMPScheduleType::NomergeOrderedStatic:
143 case OMPScheduleType::NomergeOrderedDynamicChunked:
144 case OMPScheduleType::NomergeOrderedGuidedChunked:
145 case OMPScheduleType::NomergeOrderedRuntime:
146 case OMPScheduleType::NomergeOrderedAuto:
147 case OMPScheduleType::NomergeOrderedTrapezoidal:
148 case OMPScheduleType::OrderedDistributeChunked:
149 case OMPScheduleType::OrderedDistribute:
150 break;
151 default:
152 return false;
153 }
154
155 // Must not set both monotonicity modifiers at the same time.
156 OMPScheduleType MonotonicityFlags =
157 SchedType & OMPScheduleType::MonotonicityMask;
158 if (MonotonicityFlags == OMPScheduleType::MonotonicityMask)
159 return false;
160
161 return true;
162}
163#endif
164
165/// This is a wrapper over IRBuilderBase::restoreIP that also restores a current
166/// debug location when the insert point is at the end of a block. It picks a
167/// location scoped to the current function: the block's last instruction
168/// location if the block is non-empty, otherwise a location synthesized from
169/// the function's subprogram (when the function has debug info).
172 Builder.restoreIP(IP);
173 // When IP points at a real instruction, restoreIP (SetInsertPoint) already
174 // set the debug location from that instruction, so leave it alone.
175 llvm::BasicBlock *BB = Builder.GetInsertBlock();
176 if (Builder.GetInsertPoint() != BB->end())
177 return;
178
179 // At the end of a block, pick a location guaranteed to belong to the current
180 // insertion function's subprogram. Prefer the block's own last instruction;
181 // otherwise synthesize a location from the function's subprogram.
182 if (!BB->empty())
183 Builder.SetCurrentDebugLocation(BB->back().getStableDebugLoc());
184 else if (llvm::DISubprogram *FSP =
185 BB->getParent() ? BB->getParent()->getSubprogram() : nullptr) {
186 unsigned Line = FSP->getScopeLine() ? FSP->getScopeLine() : FSP->getLine();
187 Builder.SetCurrentDebugLocation(
188 llvm::DILocation::get(FSP->getContext(), Line, /*Column=*/0, FSP));
189 }
190}
191
192static bool hasGridValue(const Triple &T) {
193 return T.isAMDGPU() || T.isNVPTX() || T.isSPIRV();
194}
195
196static const omp::GV &getGridValue(const Triple &T, Function *Kernel) {
197 if (T.isAMDGPU()) {
198 StringRef Features =
199 Kernel->getFnAttribute("target-features").getValueAsString();
200 if (Features.count("+wavefrontsize64"))
203 }
204 if (T.isNVPTX())
206 if (T.isSPIRV())
208 llvm_unreachable("No grid value available for this architecture!");
209}
210
211/// Determine which scheduling algorithm to use, determined from schedule clause
212/// arguments.
213static OMPScheduleType
214getOpenMPBaseScheduleType(llvm::omp::ScheduleKind ClauseKind, bool HasChunks,
215 bool HasSimdModifier, bool HasDistScheduleChunks) {
216 // Currently, the default schedule it static.
217 switch (ClauseKind) {
218 case OMP_SCHEDULE_Default:
219 case OMP_SCHEDULE_Static:
220 return HasChunks ? OMPScheduleType::BaseStaticChunked
221 : OMPScheduleType::BaseStatic;
222 case OMP_SCHEDULE_Dynamic:
223 return OMPScheduleType::BaseDynamicChunked;
224 case OMP_SCHEDULE_Guided:
225 return HasSimdModifier ? OMPScheduleType::BaseGuidedSimd
226 : OMPScheduleType::BaseGuidedChunked;
227 case OMP_SCHEDULE_Auto:
229 case OMP_SCHEDULE_Runtime:
230 return HasSimdModifier ? OMPScheduleType::BaseRuntimeSimd
231 : OMPScheduleType::BaseRuntime;
232 case OMP_SCHEDULE_Distribute:
233 return HasDistScheduleChunks ? OMPScheduleType::BaseDistributeChunked
234 : OMPScheduleType::BaseDistribute;
235 }
236 llvm_unreachable("unhandled schedule clause argument");
237}
238
239/// Adds ordering modifier flags to schedule type.
240static OMPScheduleType
242 bool HasOrderedClause) {
243 assert((BaseScheduleType & OMPScheduleType::ModifierMask) ==
244 OMPScheduleType::None &&
245 "Must not have ordering nor monotonicity flags already set");
246
247 OMPScheduleType OrderingModifier = HasOrderedClause
248 ? OMPScheduleType::ModifierOrdered
249 : OMPScheduleType::ModifierUnordered;
250 OMPScheduleType OrderingScheduleType = BaseScheduleType | OrderingModifier;
251
252 // Unsupported combinations
253 if (OrderingScheduleType ==
254 (OMPScheduleType::BaseGuidedSimd | OMPScheduleType::ModifierOrdered))
255 return OMPScheduleType::OrderedGuidedChunked;
256 else if (OrderingScheduleType == (OMPScheduleType::BaseRuntimeSimd |
257 OMPScheduleType::ModifierOrdered))
258 return OMPScheduleType::OrderedRuntime;
259
260 return OrderingScheduleType;
261}
262
263/// Adds monotonicity modifier flags to schedule type.
264static OMPScheduleType
266 bool HasSimdModifier, bool HasMonotonic,
267 bool HasNonmonotonic, bool HasOrderedClause) {
268 assert((ScheduleType & OMPScheduleType::MonotonicityMask) ==
269 OMPScheduleType::None &&
270 "Must not have monotonicity flags already set");
271 assert((!HasMonotonic || !HasNonmonotonic) &&
272 "Monotonic and Nonmonotonic are contradicting each other");
273
274 if (HasMonotonic) {
275 return ScheduleType | OMPScheduleType::ModifierMonotonic;
276 } else if (HasNonmonotonic) {
277 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
278 } else {
279 // OpenMP 5.1, 2.11.4 Worksharing-Loop Construct, Description.
280 // If the static schedule kind is specified or if the ordered clause is
281 // specified, and if the nonmonotonic modifier is not specified, the
282 // effect is as if the monotonic modifier is specified. Otherwise, unless
283 // the monotonic modifier is specified, the effect is as if the
284 // nonmonotonic modifier is specified.
285 OMPScheduleType BaseScheduleType =
286 ScheduleType & ~OMPScheduleType::ModifierMask;
287 if ((BaseScheduleType == OMPScheduleType::BaseStatic) ||
288 (BaseScheduleType == OMPScheduleType::BaseStaticChunked) ||
289 HasOrderedClause) {
290 // The monotonic is used by default in openmp runtime library, so no need
291 // to set it.
292 return ScheduleType;
293 } else {
294 return ScheduleType | OMPScheduleType::ModifierNonmonotonic;
295 }
296 }
297}
298
299/// Determine the schedule type using schedule and ordering clause arguments.
300static OMPScheduleType
301computeOpenMPScheduleType(ScheduleKind ClauseKind, bool HasChunks,
302 bool HasSimdModifier, bool HasMonotonicModifier,
303 bool HasNonmonotonicModifier, bool HasOrderedClause,
304 bool HasDistScheduleChunks) {
306 ClauseKind, HasChunks, HasSimdModifier, HasDistScheduleChunks);
307 OMPScheduleType OrderedSchedule =
308 getOpenMPOrderingScheduleType(BaseSchedule, HasOrderedClause);
310 OrderedSchedule, HasSimdModifier, HasMonotonicModifier,
311 HasNonmonotonicModifier, HasOrderedClause);
312
314 return Result;
315}
316
317/// Given a function, if it represents the entry point of a target kernel, this
318/// returns the execution mode flags associated with that kernel.
319static std::optional<omp::OMPTgtExecModeFlags>
321 CallInst *TargetInitCall = nullptr;
322 for (Instruction &Inst : Kernel.getEntryBlock()) {
323 if (auto *Call = dyn_cast<CallInst>(&Inst)) {
324 if (Call->getCalledFunction()->getName() == "__kmpc_target_init") {
325 TargetInitCall = Call;
326 break;
327 }
328 }
329 }
330
331 if (!TargetInitCall)
332 return std::nullopt;
333
334 // Get the kernel mode information from the global variable associated to the
335 // first argument to the call to __kmpc_target_init. Refer to
336 // createTargetInit() to see how this is initialized.
337 Value *InitOperand = TargetInitCall->getArgOperand(0);
338 GlobalVariable *KernelEnv = nullptr;
339 if (auto *Cast = dyn_cast<ConstantExpr>(InitOperand))
340 KernelEnv = cast<GlobalVariable>(Cast->getOperand(0));
341 else
342 KernelEnv = cast<GlobalVariable>(InitOperand);
343 auto *KernelEnvInit = cast<ConstantStruct>(KernelEnv->getInitializer());
344 auto *ConfigEnv = cast<ConstantStruct>(KernelEnvInit->getOperand(0));
345 auto *KernelMode = cast<ConstantInt>(ConfigEnv->getOperand(2));
346 return static_cast<OMPTgtExecModeFlags>(KernelMode->getZExtValue());
347}
348
349static bool isGenericKernel(Function &Fn) {
350 std::optional<omp::OMPTgtExecModeFlags> ExecMode =
352 return !ExecMode || (*ExecMode & OMP_TGT_EXEC_MODE_GENERIC);
353}
354
355/// Make \p Source branch to \p Target.
356///
357/// Handles two situations:
358/// * \p Source already has an unconditional branch.
359/// * \p Source is a degenerate block (no terminator because the BB is
360/// the current head of the IR construction).
362 if (Instruction *Term = Source->getTerminatorOrNull()) {
363 auto *Br = cast<UncondBrInst>(Term);
364 BasicBlock *Succ = Br->getSuccessor();
365 Succ->removePredecessor(Source, /*KeepOneInputPHIs=*/true);
366 Br->setSuccessor(Target);
367 return;
368 }
369
370 auto *NewBr = UncondBrInst::Create(Target, Source);
371 NewBr->setDebugLoc(DL);
372}
373
375 bool CreateBranch, DebugLoc DL) {
376 assert(New->getFirstInsertionPt() == New->begin() &&
377 "Target BB must not have PHI nodes");
378
379 // Move instructions to new block.
380 BasicBlock *Old = IP.getBlock();
381 // If the `Old` block is empty then there are no instructions to move. But in
382 // the new debug scheme, it could have trailing debug records which will be
383 // moved to `New` in `spliceDebugInfoEmptyBlock`. We dont want that for 2
384 // reasons:
385 // 1. If `New` is also empty, `BasicBlock::splice` crashes.
386 // 2. Even if `New` is not empty, the rationale to move those records to `New`
387 // (in `spliceDebugInfoEmptyBlock`) does not apply here. That function
388 // assumes that `Old` is optimized out and is going away. This is not the case
389 // here. The `Old` block is still being used e.g. a branch instruction is
390 // added to it later in this function.
391 // So we call `BasicBlock::splice` only when `Old` is not empty.
392 if (!Old->empty())
393 New->splice(New->begin(), Old, IP.getPoint(), Old->end());
394
395 if (CreateBranch) {
396 auto *NewBr = UncondBrInst::Create(New, Old);
397 NewBr->setDebugLoc(DL);
398 }
399}
400
401void llvm::spliceBB(IRBuilder<> &Builder, BasicBlock *New, bool CreateBranch) {
402 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
403 BasicBlock *Old = Builder.GetInsertBlock();
404
405 spliceBB(Builder.saveIP(), New, CreateBranch, DebugLoc);
406 if (CreateBranch)
407 Builder.SetInsertPoint(Old->getTerminator());
408 else
409 Builder.SetInsertPoint(Old);
410
411 // SetInsertPoint also updates the Builder's debug location, but we want to
412 // keep the one the Builder was configured to use.
413 Builder.SetCurrentDebugLocation(DebugLoc);
414}
415
417 DebugLoc DL, llvm::Twine Name) {
418 BasicBlock *Old = IP.getBlock();
420 Old->getContext(), Name.isTriviallyEmpty() ? Old->getName() : Name,
421 Old->getParent(), Old->getNextNode());
422 spliceBB(IP, New, CreateBranch, DL);
423 New->replaceSuccessorsPhiUsesWith(Old, New);
424 return New;
425}
426
427BasicBlock *llvm::splitBB(IRBuilderBase &Builder, bool CreateBranch,
428 llvm::Twine Name) {
429 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
430 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
431 if (CreateBranch)
432 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
433 else
434 Builder.SetInsertPoint(Builder.GetInsertBlock());
435 // SetInsertPoint also updates the Builder's debug location, but we want to
436 // keep the one the Builder was configured to use.
437 Builder.SetCurrentDebugLocation(DebugLoc);
438 return New;
439}
440
441BasicBlock *llvm::splitBB(IRBuilder<> &Builder, bool CreateBranch,
442 llvm::Twine Name) {
443 DebugLoc DebugLoc = Builder.getCurrentDebugLocation();
444 BasicBlock *New = splitBB(Builder.saveIP(), CreateBranch, DebugLoc, Name);
445 if (CreateBranch)
446 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
447 else
448 Builder.SetInsertPoint(Builder.GetInsertBlock());
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 } else {
480 FakeVal = Builder.CreateLoad(IntTy, FakeValAddr, Name + ".val");
481 ToBeDeleted.push_back(FakeVal);
482 }
483
484 // Generate a fake use of this value
485 Builder.restoreIP(InnerAllocaIP);
486 Instruction *UseFakeVal;
487 if (AsPtr) {
488 UseFakeVal = Builder.CreateLoad(IntTy, FakeVal, Name + ".use");
489 } else {
490 UseFakeVal = cast<BinaryOperator>(Builder.CreateAdd(
491 FakeVal, Is64Bit ? Builder.getInt64(10) : Builder.getInt32(10)));
492 }
493 ToBeDeleted.push_back(UseFakeVal);
494 return FakeVal;
495}
496
497//===----------------------------------------------------------------------===//
498// OpenMPIRBuilderConfig
499//===----------------------------------------------------------------------===//
500
501namespace {
503/// Values for bit flags for marking which requires clauses have been used.
504enum OpenMPOffloadingRequiresDirFlags {
505 /// flag undefined.
506 OMP_REQ_UNDEFINED = 0x000,
507 /// no requires directive present.
508 OMP_REQ_NONE = 0x001,
509 /// reverse_offload clause.
510 OMP_REQ_REVERSE_OFFLOAD = 0x002,
511 /// unified_address clause.
512 OMP_REQ_UNIFIED_ADDRESS = 0x004,
513 /// unified_shared_memory clause.
514 OMP_REQ_UNIFIED_SHARED_MEMORY = 0x008,
515 /// dynamic_allocators clause.
516 OMP_REQ_DYNAMIC_ALLOCATORS = 0x010,
517 LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
518};
519
520class OMPCodeExtractor : public CodeExtractor {
521public:
522 OMPCodeExtractor(OpenMPIRBuilder &OMPBuilder, ArrayRef<BasicBlock *> BBs,
523 DominatorTree *DT = nullptr, bool AggregateArgs = false,
524 BlockFrequencyInfo *BFI = nullptr,
525 BranchProbabilityInfo *BPI = nullptr,
526 AssumptionCache *AC = nullptr, bool AllowVarArgs = false,
527 bool AllowAlloca = false,
528 BasicBlock *AllocationBlock = nullptr,
529 ArrayRef<BasicBlock *> DeallocationBlocks = {},
530 std::string Suffix = "", bool ArgsInZeroAddressSpace = false)
531 : CodeExtractor(BBs, DT, AggregateArgs, BFI, BPI, AC, AllowVarArgs,
532 AllowAlloca, AllocationBlock, DeallocationBlocks, Suffix,
533 ArgsInZeroAddressSpace),
534 OMPBuilder(OMPBuilder) {}
535
536 virtual ~OMPCodeExtractor() = default;
537
538protected:
539 OpenMPIRBuilder &OMPBuilder;
540};
541
542class DeviceSharedMemCodeExtractor : public OMPCodeExtractor {
543public:
544 using OMPCodeExtractor::OMPCodeExtractor;
545 virtual ~DeviceSharedMemCodeExtractor() = default;
546
547protected:
548 virtual Instruction *
549 allocateVar(IRBuilder<>::InsertPoint AllocaIP, Type *VarType,
550 const Twine &Name = Twine(""),
551 AddrSpaceCastInst **CastedAlloc = nullptr) override {
552 return OMPBuilder.createOMPAllocShared(AllocaIP, VarType, Name);
553 }
554
555 virtual Instruction *deallocateVar(IRBuilder<>::InsertPoint DeallocIP,
556 Value *Var, Type *VarType) override {
557 return OMPBuilder.createOMPFreeShared(DeallocIP, Var, VarType);
558 }
559};
560
561/// Helper storing information about regions to outline using device shared
562/// memory for intermediate allocations.
563struct DeviceSharedMemOutlineInfo : public OpenMPIRBuilder::OutlineInfo {
564 OpenMPIRBuilder &OMPBuilder;
565
566 DeviceSharedMemOutlineInfo(OpenMPIRBuilder &OMPBuilder)
567 : OMPBuilder(OMPBuilder) {}
568 virtual ~DeviceSharedMemOutlineInfo() = default;
569
570 virtual std::unique_ptr<CodeExtractor>
571 createCodeExtractor(ArrayRef<BasicBlock *> Blocks,
572 bool ArgsInZeroAddressSpace,
573 Twine Suffix = Twine("")) override;
574};
575
576} // anonymous namespace
577
579 : RequiresFlags(OMP_REQ_UNDEFINED) {}
580
583 bool HasRequiresReverseOffload, bool HasRequiresUnifiedAddress,
584 bool HasRequiresUnifiedSharedMemory, bool HasRequiresDynamicAllocators)
587 RequiresFlags(OMP_REQ_UNDEFINED) {
588 if (HasRequiresReverseOffload)
589 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
590 if (HasRequiresUnifiedAddress)
591 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
592 if (HasRequiresUnifiedSharedMemory)
593 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
594 if (HasRequiresDynamicAllocators)
595 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
596}
597
599 return RequiresFlags & OMP_REQ_REVERSE_OFFLOAD;
600}
601
603 return RequiresFlags & OMP_REQ_UNIFIED_ADDRESS;
604}
605
607 return RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY;
608}
609
611 return RequiresFlags & OMP_REQ_DYNAMIC_ALLOCATORS;
612}
613
615 return hasRequiresFlags() ? RequiresFlags
616 : static_cast<int64_t>(OMP_REQ_NONE);
617}
618
620 if (Value)
621 RequiresFlags |= OMP_REQ_REVERSE_OFFLOAD;
622 else
623 RequiresFlags &= ~OMP_REQ_REVERSE_OFFLOAD;
624}
625
627 if (Value)
628 RequiresFlags |= OMP_REQ_UNIFIED_ADDRESS;
629 else
630 RequiresFlags &= ~OMP_REQ_UNIFIED_ADDRESS;
631}
632
634 if (Value)
635 RequiresFlags |= OMP_REQ_UNIFIED_SHARED_MEMORY;
636 else
637 RequiresFlags &= ~OMP_REQ_UNIFIED_SHARED_MEMORY;
638}
639
641 if (Value)
642 RequiresFlags |= OMP_REQ_DYNAMIC_ALLOCATORS;
643 else
644 RequiresFlags &= ~OMP_REQ_DYNAMIC_ALLOCATORS;
645}
646
647//===----------------------------------------------------------------------===//
648// OpenMPIRBuilder
649//===----------------------------------------------------------------------===//
650
653 SmallVector<Value *> &ArgsVector) {
655 Value *PointerNum = Builder.getInt32(KernelArgs.NumTargetItems);
656 auto Int32Ty = Type::getInt32Ty(Builder.getContext());
657 constexpr size_t MaxDim = 3;
658 Value *ZeroArray = Constant::getNullValue(ArrayType::get(Int32Ty, MaxDim));
659
660 Value *HasNoWaitFlag = Builder.getInt64(KernelArgs.HasNoWait);
661
662 Value *DynCGroupMemFallbackFlag =
663 Builder.getInt64(static_cast<uint64_t>(KernelArgs.DynCGroupMemFallback));
664 DynCGroupMemFallbackFlag = Builder.CreateShl(DynCGroupMemFallbackFlag, 2);
665
666 Value *StrictBlocksFlag = Builder.getInt64(KernelArgs.StrictBlocks);
667 Value *StrictThreadsFlag = Builder.getInt64(KernelArgs.StrictThreads);
668
669 StrictBlocksFlag = Builder.CreateShl(StrictBlocksFlag, 6);
670 StrictThreadsFlag = Builder.CreateShl(StrictThreadsFlag, 7);
671
672 Value *Flags = Builder.CreateOr(HasNoWaitFlag, DynCGroupMemFallbackFlag);
673 Flags = Builder.CreateOr(Flags, StrictBlocksFlag);
674 Flags = Builder.CreateOr(Flags, StrictThreadsFlag);
675
676 assert(!KernelArgs.NumTeams.empty() && !KernelArgs.NumThreads.empty());
677
678 Value *NumTeams3D =
679 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumTeams[0], {0});
680 Value *NumThreads3D =
681 Builder.CreateInsertValue(ZeroArray, KernelArgs.NumThreads[0], {0});
682 for (unsigned I :
683 seq<unsigned>(1, std::min(KernelArgs.NumTeams.size(), MaxDim)))
684 NumTeams3D =
685 Builder.CreateInsertValue(NumTeams3D, KernelArgs.NumTeams[I], {I});
686 for (unsigned I :
687 seq<unsigned>(1, std::min(KernelArgs.NumThreads.size(), MaxDim)))
688 NumThreads3D =
689 Builder.CreateInsertValue(NumThreads3D, KernelArgs.NumThreads[I], {I});
690
691 ArgsVector = {Version,
692 PointerNum,
693 KernelArgs.RTArgs.BasePointersArray,
694 KernelArgs.RTArgs.PointersArray,
695 KernelArgs.RTArgs.SizesArray,
696 KernelArgs.RTArgs.MapTypesArray,
697 KernelArgs.RTArgs.MapNamesArray,
698 KernelArgs.RTArgs.MappersArray,
699 KernelArgs.NumIterations,
700 Flags,
701 NumTeams3D,
702 NumThreads3D,
703 KernelArgs.DynCGroupMem};
704}
705
707 LLVMContext &Ctx = Fn.getContext();
708
709 // Get the function's current attributes.
710 auto Attrs = Fn.getAttributes();
711 auto FnAttrs = Attrs.getFnAttrs();
712 auto RetAttrs = Attrs.getRetAttrs();
714 for (size_t ArgNo = 0; ArgNo < Fn.arg_size(); ++ArgNo)
715 ArgAttrs.emplace_back(Attrs.getParamAttrs(ArgNo));
716
717 // Add AS to FnAS while taking special care with integer extensions.
718 auto addAttrSet = [&](AttributeSet &FnAS, const AttributeSet &AS,
719 bool Param = true) -> void {
720 bool HasSignExt = AS.hasAttribute(Attribute::SExt);
721 bool HasZeroExt = AS.hasAttribute(Attribute::ZExt);
722 if (HasSignExt || HasZeroExt) {
723 assert(AS.getNumAttributes() == 1 &&
724 "Currently not handling extension attr combined with others.");
725 if (Param) {
726 if (auto AK = TargetLibraryInfo::getExtAttrForI32Param(T, HasSignExt))
727 FnAS = FnAS.addAttribute(Ctx, AK);
728 } else if (auto AK =
729 TargetLibraryInfo::getExtAttrForI32Return(T, HasSignExt))
730 FnAS = FnAS.addAttribute(Ctx, AK);
731 } else {
732 FnAS = FnAS.addAttributes(Ctx, AS);
733 }
734 };
735
736#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet;
737#include "llvm/Frontend/OpenMP/OMPKinds.def"
738
739 // Add attributes to the function declaration.
740 switch (FnID) {
741#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \
742 case Enum: \
743 FnAttrs = FnAttrs.addAttributes(Ctx, FnAttrSet); \
744 addAttrSet(RetAttrs, RetAttrSet, /*Param*/ false); \
745 for (size_t ArgNo = 0; ArgNo < ArgAttrSets.size(); ++ArgNo) \
746 addAttrSet(ArgAttrs[ArgNo], ArgAttrSets[ArgNo]); \
747 Fn.setAttributes(AttributeList::get(Ctx, FnAttrs, RetAttrs, ArgAttrs)); \
748 break;
749#include "llvm/Frontend/OpenMP/OMPKinds.def"
750 default:
751 // Attributes are optional.
752 break;
753 }
754}
755
758 FunctionType *FnTy = nullptr;
759 Function *Fn = nullptr;
760
761 // Try to find the declation in the module first.
762 switch (FnID) {
763#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \
764 case Enum: \
765 FnTy = FunctionType::get(ReturnType, ArrayRef<Type *>{__VA_ARGS__}, \
766 IsVarArg); \
767 Fn = M.getFunction(Str); \
768 break;
769#include "llvm/Frontend/OpenMP/OMPKinds.def"
770 }
771
772 if (!Fn) {
773 // Create a new declaration if we need one.
774 switch (FnID) {
775#define OMP_RTL(Enum, Str, ...) \
776 case Enum: \
777 Fn = Function::Create(FnTy, GlobalValue::ExternalLinkage, Str, M); \
778 break;
779#include "llvm/Frontend/OpenMP/OMPKinds.def"
780 }
781 Fn->setCallingConv(Config.getRuntimeCC());
782 // Add information if the runtime function takes a callback function
783 if (FnID == OMPRTL___kmpc_fork_call || FnID == OMPRTL___kmpc_fork_teams) {
784 if (!Fn->hasMetadata(LLVMContext::MD_callback)) {
785 LLVMContext &Ctx = Fn->getContext();
786 MDBuilder MDB(Ctx);
787 // Annotate the callback behavior of the runtime function:
788 // - The callback callee is argument number 2 (microtask).
789 // - The first two arguments of the callback callee are unknown (-1).
790 // - All variadic arguments to the runtime function are passed to the
791 // callback callee.
792 Fn->addMetadata(
793 LLVMContext::MD_callback,
795 2, {-1, -1}, /* VarArgsArePassed */ true)}));
796 }
797 }
798
799 LLVM_DEBUG(dbgs() << "Created OpenMP runtime function " << Fn->getName()
800 << " with type " << *Fn->getFunctionType() << "\n");
801 addAttributes(FnID, *Fn);
802
803 } else {
804 LLVM_DEBUG(dbgs() << "Found OpenMP runtime function " << Fn->getName()
805 << " with type " << *Fn->getFunctionType() << "\n");
806 }
807
808 assert(Fn && "Failed to create OpenMP runtime function");
809
810 return {FnTy, Fn};
811}
812
815 if (!FiniBB) {
816 Function *ParentFunc = Builder.GetInsertBlock()->getParent();
818 FiniBB = BasicBlock::Create(Builder.getContext(), ".fini", ParentFunc);
819 Builder.SetInsertPoint(FiniBB);
820 // FiniCB adds the branch to the exit stub.
821 if (Error Err = FiniCB(Builder.saveIP()))
822 return Err;
823 }
824 return FiniBB;
825}
826
828 BasicBlock *OtherFiniBB) {
829 // Simple case: FiniBB does not exist yet: re-use OtherFiniBB.
830 if (!FiniBB) {
831 FiniBB = OtherFiniBB;
832
833 Builder.SetInsertPoint(FiniBB->getFirstNonPHIIt());
834 if (Error Err = FiniCB(Builder.saveIP()))
835 return Err;
836
837 return Error::success();
838 }
839
840 // Move instructions from FiniBB to the start of OtherFiniBB.
841 auto EndIt = FiniBB->end();
842 if (FiniBB->size() >= 1)
843 if (auto Prev = std::prev(EndIt); Prev->isTerminator())
844 EndIt = Prev;
845 OtherFiniBB->splice(OtherFiniBB->getFirstNonPHIIt(), FiniBB, FiniBB->begin(),
846 EndIt);
847
848 FiniBB->replaceAllUsesWith(OtherFiniBB);
849 FiniBB->eraseFromParent();
850 FiniBB = OtherFiniBB;
851 return Error::success();
852}
853
856 auto *Fn = dyn_cast<llvm::Function>(RTLFn.getCallee());
857 assert(Fn && "Failed to create OpenMP runtime function pointer");
858 return Fn;
859}
860
863 StringRef Name) {
864 CallInst *Call = Builder.CreateCall(Callee, Args, Name);
865 Call->setCallingConv(Config.getRuntimeCC());
866 return Call;
867}
868
869void OpenMPIRBuilder::initialize() { initializeTypes(M); }
870
873 BasicBlock &EntryBlock = Function->getEntryBlock();
874 BasicBlock::iterator MoveLocInst = EntryBlock.getFirstNonPHIIt();
875
876 // Loop over blocks looking for constant allocas, skipping the entry block
877 // as any allocas there are already in the desired location.
878 for (auto Block = std::next(Function->begin(), 1); Block != Function->end();
879 Block++) {
880 for (auto Inst = Block->getReverseIterator()->begin();
881 Inst != Block->getReverseIterator()->end();) {
883 Inst++;
885 continue;
886 AllocaInst->moveBeforePreserving(MoveLocInst);
887 } else {
888 Inst++;
889 }
890 }
891 }
892}
893
896
897 auto ShouldHoistAlloca = [](const llvm::AllocaInst &AllocaInst) {
898 // TODO: For now, we support simple static allocations, we might need to
899 // move non-static ones as well. However, this will need further analysis to
900 // move the lenght arguments as well.
902 };
903
904 for (llvm::Instruction &Inst : Block)
906 if (ShouldHoistAlloca(*AllocaInst))
907 AllocasToMove.push_back(AllocaInst);
908
909 auto InsertPoint =
910 Block.getParent()->getEntryBlock().getTerminator()->getIterator();
911
912 for (llvm::Instruction *AllocaInst : AllocasToMove)
914}
915
917 PostDominatorTree PostDomTree(*Func);
918 for (llvm::BasicBlock &BB : *Func)
919 if (PostDomTree.properlyDominates(&BB, &Func->getEntryBlock()))
921}
922
924 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
926 SmallVector<std::unique_ptr<OutlineInfo>, 16> DeferredOutlines;
927 for (std::unique_ptr<OutlineInfo> &OI : OutlineInfos) {
928 // Skip functions that have not finalized yet; may happen with nested
929 // function generation.
930 if (Fn && OI->getFunction() != Fn) {
931 DeferredOutlines.push_back(std::move(OI));
932 continue;
933 }
934
935 ParallelRegionBlockSet.clear();
936 Blocks.clear();
937 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
938
939 Function *OuterFn = OI->getFunction();
940 CodeExtractorAnalysisCache CEAC(*OuterFn);
941 // If we generate code for the target device, we need to allocate
942 // struct for aggregate params in the device default alloca address space.
943 // OpenMP runtime requires that the params of the extracted functions are
944 // passed as zero address space pointers. This flag ensures that
945 // CodeExtractor generates correct code for extracted functions
946 // which are used by OpenMP runtime.
947 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
948 std::unique_ptr<CodeExtractor> Extractor =
949 OI->createCodeExtractor(Blocks, ArgsInZeroAddressSpace, ".omp_par");
950
951 LLVM_DEBUG(dbgs() << "Before outlining: " << *OuterFn << "\n");
952 LLVM_DEBUG(dbgs() << "Entry " << OI->EntryBB->getName()
953 << " Exit: " << OI->ExitBB->getName() << "\n");
954 assert(Extractor->isEligible() &&
955 "Expected OpenMP outlining to be possible!");
956
957 for (auto *V : OI->ExcludeArgsFromAggregate)
958 Extractor->excludeArgFromAggregate(V);
959
960 Function *OutlinedFn =
961 Extractor->extractCodeRegion(CEAC, OI->Inputs, OI->Outputs);
962
963 // Forward target-cpu, target-features attributes to the outlined function.
964 auto TargetCpuAttr = OuterFn->getFnAttribute("target-cpu");
965 if (TargetCpuAttr.isStringAttribute())
966 OutlinedFn->addFnAttr(TargetCpuAttr);
967
968 auto TargetFeaturesAttr = OuterFn->getFnAttribute("target-features");
969 if (TargetFeaturesAttr.isStringAttribute())
970 OutlinedFn->addFnAttr(TargetFeaturesAttr);
971
972 LLVM_DEBUG(dbgs() << "After outlining: " << *OuterFn << "\n");
973 LLVM_DEBUG(dbgs() << " Outlined function: " << *OutlinedFn << "\n");
974 assert(OutlinedFn->getReturnType()->isVoidTy() &&
975 "OpenMP outlined functions should not return a value!");
976
977 // For compability with the clang CG we move the outlined function after the
978 // one with the parallel region.
979 OutlinedFn->removeFromParent();
980 M.getFunctionList().insertAfter(OuterFn->getIterator(), OutlinedFn);
981
982 // Remove the artificial entry introduced by the extractor right away, we
983 // made our own entry block after all.
984 {
985 BasicBlock &ArtificialEntry = OutlinedFn->getEntryBlock();
986 assert(ArtificialEntry.getUniqueSuccessor() == OI->EntryBB);
987 assert(OI->EntryBB->getUniquePredecessor() == &ArtificialEntry);
988 // Move instructions from the to-be-deleted ArtificialEntry to the entry
989 // basic block of the parallel region. CodeExtractor generates
990 // instructions to unwrap the aggregate argument and may sink
991 // allocas/bitcasts for values that are solely used in the outlined region
992 // and do not escape.
993 assert(!ArtificialEntry.empty() &&
994 "Expected instructions to add in the outlined region entry");
995 for (BasicBlock::reverse_iterator It = ArtificialEntry.rbegin(),
996 End = ArtificialEntry.rend();
997 It != End;) {
998 Instruction &I = *It;
999 It++;
1000
1001 if (I.isTerminator()) {
1002 // Absorb any debug value that terminator may have
1003 if (Instruction *TI = OI->EntryBB->getTerminatorOrNull())
1004 TI->adoptDbgRecords(&ArtificialEntry, I.getIterator(), false);
1005 continue;
1006 }
1007
1008 I.moveBeforePreserving(*OI->EntryBB,
1009 OI->EntryBB->getFirstInsertionPt());
1010 }
1011
1012 OI->EntryBB->moveBefore(&ArtificialEntry);
1013 ArtificialEntry.eraseFromParent();
1014 }
1015 assert(&OutlinedFn->getEntryBlock() == OI->EntryBB);
1016 assert(OutlinedFn && OutlinedFn->hasNUses(1));
1017
1018 // Run a user callback, e.g. to add attributes.
1019 if (OI->PostOutlineCB)
1020 OI->PostOutlineCB(*OutlinedFn);
1021
1022 if (OI->FixUpNonEntryAllocas)
1024 }
1025
1026 // Remove work items that have been completed.
1027 OutlineInfos = std::move(DeferredOutlines);
1028
1029 // The createTarget functions embeds user written code into
1030 // the target region which may inject allocas which need to
1031 // be moved to the entry block of our target or risk malformed
1032 // optimisations by later passes, this is only relevant for
1033 // the device pass which appears to be a little more delicate
1034 // when it comes to optimisations (however, we do not block on
1035 // that here, it's up to the inserter to the list to do so).
1036 // This notbaly has to occur after the OutlinedInfo candidates
1037 // have been extracted so we have an end product that will not
1038 // be implicitly adversely affected by any raises unless
1039 // intentionally appended to the list.
1040 // NOTE: This only does so for ConstantData, it could be extended
1041 // to ConstantExpr's with further effort, however, they should
1042 // largely be folded when they get here. Extending it to runtime
1043 // defined/read+writeable allocation sizes would be non-trivial
1044 // (need to factor in movement of any stores to variables the
1045 // allocation size depends on, as well as the usual loads,
1046 // otherwise it'll yield the wrong result after movement) and
1047 // likely be more suitable as an LLVM optimisation pass.
1050
1051 EmitMetadataErrorReportFunctionTy &&ErrorReportFn =
1052 [](EmitMetadataErrorKind Kind,
1053 const TargetRegionEntryInfo &EntryInfo) -> void {
1054 errs() << "Error of kind: " << Kind
1055 << " when emitting offload entries and metadata during "
1056 "OMPIRBuilder finalization \n";
1057 };
1058
1059 if (!OffloadInfoManager.empty())
1061
1062 // Rewrite uses of globals to their replacement declare target globals if
1063 // we are processing a device module.
1064 if (Config.isTargetDevice())
1065 applyDeclareTargetGlobalReplacements();
1066
1067 if (Config.EmitLLVMUsedMetaInfo.value_or(false)) {
1068 std::vector<WeakTrackingVH> LLVMCompilerUsed = {
1069 M.getGlobalVariable("__openmp_nvptx_data_transfer_temporary_storage")};
1070 emitUsed("llvm.compiler.used", LLVMCompilerUsed);
1071 }
1072
1073 IsFinalized = true;
1074}
1075
1076bool OpenMPIRBuilder::isFinalized() { return IsFinalized; }
1077
1079 GlobalValue *Original, GlobalValue *Replacement) {
1080 assert(Original && Replacement &&
1081 "Null values provided to registerDeclareTargetGlobalReplacement");
1082 DeclareTargetGlobalReplacements.push_back({Original, Replacement});
1083}
1084
1085void OpenMPIRBuilder::applyDeclareTargetGlobalReplacements() {
1086 for (DeclareTargetGlobalReplacement &R : DeclareTargetGlobalReplacements) {
1087 GlobalValue *OldGV = R.Original;
1088 GlobalValue *NewGV = R.Replacement;
1089
1090 assert(OldGV && NewGV &&
1091 "A null value was inserted into DeclareTargetGlobalReplacements");
1092
1093 // The assert above should catch this case, but this is kept to attempt
1094 // to proceed without issue when asserts are off.
1095 if (!OldGV || !NewGV)
1096 continue;
1097
1098 // The replacement global is a reference pointer that holds the
1099 // address of the device-resident storage. Every use must load the
1100 // reference pointer first and use the loaded address.
1101 //
1102 // Constant expression users (e.g. a constant GEP embedded in another
1103 // global's initializer or in an instruction) cannot have a load inserted
1104 // in place, so first expand any constant-expression users that live inside
1105 // functions into instructions. Any remaining constant users are handled
1106 // via a direct constant rewrite below as we cannot materialize a load
1107 // there.
1108 //
1109 // NOTE: We extend the constant rewrite to module scope, as we replace all
1110 // usages.
1111 if (auto *OldConst = dyn_cast<Constant>(OldGV))
1113 /*RestrictToFunc=*/nullptr,
1114 /*RemoveDeadConstants=*/false);
1115
1116 IRBuilderBase::InsertPointGuard Guard(Builder);
1118 for (User *U : Users) {
1119 auto *Insn = dyn_cast<Instruction>(U);
1120 if (!Insn)
1121 continue;
1122
1123 // A PHI node cannot have a load inserted immediately before it, as PHIs
1124 // must remain grouped at the top of their basic block. So we need to
1125 // make sure any loads we emit are generated in the preceding edge, a
1126 // PHI may reference the global on more than one edge, so every matching
1127 // slot must be handled.
1128 if (auto *PHI = dyn_cast<PHINode>(Insn)) {
1129 for (unsigned I = 0, E = PHI->getNumIncomingValues(); I < E; ++I) {
1130 if (PHI->getIncomingValue(I) != OldGV)
1131 continue;
1132
1133 BasicBlock *IncomingBB = PHI->getIncomingBlock(I);
1134 Builder.SetInsertPoint(IncomingBB->getTerminator());
1135 Builder.SetCurrentDebugLocation(PHI->getDebugLoc());
1136 LoadInst *EdgeLoad = Builder.CreateLoad(NewGV->getType(), NewGV);
1137 PHI->setIncomingValue(I, EdgeLoad);
1138 }
1139 continue;
1140 }
1141
1142 Builder.SetInsertPoint(Insn);
1143 Builder.SetCurrentDebugLocation(Insn->getDebugLoc());
1144 LoadInst *Load = Builder.CreateLoad(NewGV->getType(), NewGV);
1145
1146 // The replacement declare target global lives in the default address
1147 // space, whereas the original global may reside in a non-default
1148 // address space. In that case the initial lowering may have
1149 // emitted an addrspacecast that is no longer valid. Replace the
1150 // whole addrspacecast with the load and erase it rather than
1151 // feeding the load back into the (now pointless) cast.
1152 // NOTE: If we end up with replacement declare target globals in
1153 // non-zero AS's the below will need some minor extensions to have the
1154 // option to alter the address space cast to the new address space where
1155 // required rather than just replacing it.
1156 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Insn)) {
1157 unsigned NewGVAS = NewGV->getType()->getPointerAddressSpace();
1158 assert(NewGVAS == 0 &&
1159 "Non-default address space declare target global");
1160 unsigned OldGVAS = OldGV->getType()->getPointerAddressSpace();
1161 unsigned DestAS = ASC->getType()->getPointerAddressSpace();
1162 if (DestAS == 0 && NewGVAS != OldGVAS) {
1163 ASC->replaceAllUsesWith(Load);
1164 ASC->eraseFromParent();
1165 continue;
1166 }
1167 }
1168
1169 Insn->replaceUsesOfWith(OldGV, Load);
1170 }
1171 }
1172
1174}
1175
1177 assert(OutlineInfos.empty() && "There must be no outstanding outlinings");
1178}
1179
1181 IntegerType *I32Ty = Type::getInt32Ty(M.getContext());
1182 auto *GV =
1183 new GlobalVariable(M, I32Ty,
1184 /* isConstant = */ true, GlobalValue::WeakODRLinkage,
1185 ConstantInt::get(I32Ty, Value), Name);
1186 GV->setVisibility(GlobalValue::HiddenVisibility);
1187
1188 return GV;
1189}
1190
1192 if (List.empty())
1193 return;
1194
1195 // Convert List to what ConstantArray needs.
1197 UsedArray.resize(List.size());
1198 for (unsigned I = 0, E = List.size(); I != E; ++I)
1200 cast<Constant>(&*List[I]), Builder.getPtrTy());
1201
1202 if (UsedArray.empty())
1203 return;
1204 ArrayType *ATy = ArrayType::get(Builder.getPtrTy(), UsedArray.size());
1205
1206 auto *GV = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
1207 ConstantArray::get(ATy, UsedArray), Name);
1208
1209 GV->setSection("llvm.metadata");
1210}
1211
1214 OMPTgtExecModeFlags Mode) {
1215 auto *Int8Ty = Builder.getInt8Ty();
1216 auto *GVMode = new GlobalVariable(
1217 M, Int8Ty, /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
1218 ConstantInt::get(Int8Ty, Mode), Twine(KernelName, "_exec_mode"));
1219 GVMode->setVisibility(GlobalVariable::ProtectedVisibility);
1220 return GVMode;
1221}
1222
1224 uint32_t SrcLocStrSize,
1225 IdentFlag LocFlags,
1226 unsigned Reserve2Flags) {
1227 // Enable "C-mode".
1228 LocFlags |= OMP_IDENT_FLAG_KMPC;
1229
1230 Constant *&Ident =
1231 IdentMap[{SrcLocStr, uint64_t(LocFlags) << 31 | Reserve2Flags}];
1232 if (!Ident) {
1233 Constant *I32Null = ConstantInt::getNullValue(Int32);
1234 Constant *IdentData[] = {I32Null,
1235 ConstantInt::get(Int32, uint32_t(LocFlags)),
1236 ConstantInt::get(Int32, Reserve2Flags),
1237 ConstantInt::get(Int32, SrcLocStrSize), SrcLocStr};
1238
1239 size_t SrcLocStrArgIdx = 4;
1240 if (OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx)
1242 IdentData[SrcLocStrArgIdx]->getType()->getPointerAddressSpace())
1243 IdentData[SrcLocStrArgIdx] = ConstantExpr::getAddrSpaceCast(
1244 SrcLocStr, OpenMPIRBuilder::Ident->getElementType(SrcLocStrArgIdx));
1245 Constant *Initializer =
1246 ConstantStruct::get(OpenMPIRBuilder::Ident, IdentData);
1247
1248 // Look for existing encoding of the location + flags, not needed but
1249 // minimizes the difference to the existing solution while we transition.
1250 for (GlobalVariable &GV : M.globals())
1251 if (GV.getValueType() == OpenMPIRBuilder::Ident && GV.hasInitializer())
1252 if (GV.getInitializer() == Initializer)
1253 Ident = &GV;
1254
1255 if (!Ident) {
1256 auto *GV = new GlobalVariable(
1257 M, OpenMPIRBuilder::Ident,
1258 /* isConstant = */ true, GlobalValue::PrivateLinkage, Initializer, "",
1260 M.getDataLayout().getDefaultGlobalsAddressSpace());
1261 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
1262 GV->setAlignment(Align(8));
1263 Ident = GV;
1264 }
1265 }
1266
1267 return ConstantExpr::getPointerBitCastOrAddrSpaceCast(Ident, IdentPtr);
1268}
1269
1271 uint32_t &SrcLocStrSize) {
1272 SrcLocStrSize = LocStr.size();
1273 Constant *&SrcLocStr = SrcLocStrMap[LocStr];
1274 if (!SrcLocStr) {
1275 Constant *Initializer =
1276 ConstantDataArray::getString(M.getContext(), LocStr);
1277
1278 // Look for existing encoding of the location, not needed but minimizes the
1279 // difference to the existing solution while we transition.
1280 for (GlobalVariable &GV : M.globals())
1281 if (GV.isConstant() && GV.hasInitializer() &&
1282 GV.getInitializer() == Initializer)
1283 return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr);
1284
1285 SrcLocStr = Builder.CreateGlobalString(
1286 LocStr, /*Name=*/"", M.getDataLayout().getDefaultGlobalsAddressSpace(),
1287 &M);
1288 }
1289 return SrcLocStr;
1290}
1291
1293 StringRef FileName,
1294 unsigned Line, unsigned Column,
1295 uint32_t &SrcLocStrSize) {
1296 SmallString<128> Buffer;
1297 Buffer.push_back(';');
1298 Buffer.append(FileName);
1299 Buffer.push_back(';');
1300 Buffer.append(FunctionName);
1301 Buffer.push_back(';');
1302 Buffer.append(std::to_string(Line));
1303 Buffer.push_back(';');
1304 Buffer.append(std::to_string(Column));
1305 Buffer.push_back(';');
1306 Buffer.push_back(';');
1307 return getOrCreateSrcLocStr(Buffer.str(), SrcLocStrSize);
1308}
1309
1310Constant *
1312 StringRef UnknownLoc = ";unknown;unknown;0;0;;";
1313 return getOrCreateSrcLocStr(UnknownLoc, SrcLocStrSize);
1314}
1315
1317 uint32_t &SrcLocStrSize,
1318 Function *F) {
1319 DILocation *DIL = DL.get();
1320 if (!DIL)
1321 return getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1322 StringRef FileName =
1323 !DIL->getFilename().empty() ? DIL->getFilename() : M.getName();
1324 StringRef Function = DIL->getScope()->getSubprogram()->getName();
1325 if (Function.empty() && F)
1326 Function = F->getName();
1327 return getOrCreateSrcLocStr(Function, FileName, DIL->getLine(),
1328 DIL->getColumn(), SrcLocStrSize);
1329}
1330
1332 uint32_t &SrcLocStrSize) {
1333 return getOrCreateSrcLocStr(Loc.DL, SrcLocStrSize,
1334 Loc.IP.getBlock()->getParent());
1335}
1336
1339 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num), Ident,
1340 "omp_global_thread_num");
1341}
1342
1343OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetInReduction(
1344 const LocationDescription &Loc, ArrayRef<Value *> OrigPtrs,
1345 ArrayRef<Type *> ResultPtrTys,
1346 function_ref<void(unsigned, Value *)> MapPrivateCB) {
1347 assert(OrigPtrs.size() == ResultPtrTys.size() &&
1348 "expected one result pointer type per in_reduction item");
1349 if (!updateToLocation(Loc))
1350 return Loc.IP;
1351 if (OrigPtrs.empty())
1352 return Builder.saveIP();
1353
1354 // Compute the executing thread's gtid once for the whole target body and
1355 // reuse it for every in_reduction lookup, so a target with several
1356 // in_reduction items does not emit a redundant __kmpc_global_thread_num per
1357 // item.
1358 uint32_t SrcLocStrSize;
1359 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1360 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1361 Value *Gtid = getOrCreateThreadID(Ident);
1362
1363 // The runtime entry point takes (and returns) a generic, default-address-
1364 // space `ptr`. A NULL descriptor makes the runtime walk the enclosing
1365 // taskgroups to find the matching task_reduction registration for the item.
1366 Type *PtrTy = PointerType::getUnqual(M.getContext());
1367 Value *NullDesc = ConstantPointerNull::get(PtrTy);
1368 FunctionCallee GetThData =
1369 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_task_reduction_get_th_data);
1370
1371 for (unsigned Idx = 0; Idx < OrigPtrs.size(); ++Idx) {
1372 // Normalize a non-default-address-space original pointer to the generic
1373 // address space before the call.
1374 Value *OrigPtr = OrigPtrs[Idx];
1375 if (auto *OrigPtrTy = dyn_cast<PointerType>(OrigPtr->getType());
1376 OrigPtrTy && OrigPtrTy->getAddressSpace() != 0)
1377 OrigPtr = Builder.CreateAddrSpaceCast(OrigPtr, PtrTy);
1378
1379 Value *Priv = Builder.CreateCall(GetThData, {Gtid, NullDesc, OrigPtr},
1380 "omp.inred.priv");
1381
1382 // Cast the returned private pointer back to the requested address space
1383 // when it differs.
1384 if (auto *ResPtrTy = dyn_cast<PointerType>(ResultPtrTys[Idx]);
1385 ResPtrTy && ResPtrTy->getAddressSpace() != 0)
1386 Priv = Builder.CreateAddrSpaceCast(Priv, ResultPtrTys[Idx]);
1387
1388 MapPrivateCB(Idx, Priv);
1389 }
1390 return Builder.saveIP();
1391}
1392
1395 bool ForceSimpleCall, bool CheckCancelFlag) {
1396 if (!updateToLocation(Loc))
1397 return Loc.IP;
1398
1399 // Build call __kmpc_cancel_barrier(loc, thread_id) or
1400 // __kmpc_barrier(loc, thread_id);
1401
1402 IdentFlag BarrierLocFlags;
1403 switch (Kind) {
1404 case OMPD_for:
1405 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR;
1406 break;
1407 case OMPD_sections:
1408 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS;
1409 break;
1410 case OMPD_single:
1411 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE;
1412 break;
1413 case OMPD_barrier:
1414 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL;
1415 break;
1416 default:
1417 BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL;
1418 break;
1419 }
1420
1421 uint32_t SrcLocStrSize;
1422 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1423 Value *Args[] = {
1424 getOrCreateIdent(SrcLocStr, SrcLocStrSize, BarrierLocFlags),
1425 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize))};
1426
1427 // If we are in a cancellable parallel region, barriers are cancellation
1428 // points.
1429 // TODO: Check why we would force simple calls or to ignore the cancel flag.
1430 bool UseCancelBarrier =
1431 !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel);
1432
1434 getOrCreateRuntimeFunctionPtr(UseCancelBarrier
1435 ? OMPRTL___kmpc_cancel_barrier
1436 : OMPRTL___kmpc_barrier),
1437 Args);
1438
1439 if (UseCancelBarrier && CheckCancelFlag)
1440 if (Error Err = emitCancelationCheckImpl(Result, OMPD_parallel))
1441 return Err;
1442
1443 return Builder.saveIP();
1444}
1445
1448 Value *IfCondition,
1449 omp::Directive CanceledDirective) {
1450 if (!updateToLocation(Loc))
1451 return Loc.IP;
1452
1453 // LLVM utilities like blocks with terminators.
1454 auto *UI = Builder.CreateUnreachable();
1455
1456 Instruction *ThenTI = UI, *ElseTI = nullptr;
1457 if (IfCondition) {
1458 SplitBlockAndInsertIfThenElse(IfCondition, UI, &ThenTI, &ElseTI);
1459
1460 // Even if the if condition evaluates to false, this should count as a
1461 // cancellation point
1462 Builder.SetInsertPoint(ElseTI);
1463 auto ElseIP = Builder.saveIP();
1464
1466 LocationDescription{ElseIP, Loc.DL}, CanceledDirective);
1467 if (!IPOrErr)
1468 return IPOrErr;
1469 }
1470
1471 Builder.SetInsertPoint(ThenTI);
1472
1473 Value *CancelKind = nullptr;
1474 switch (CanceledDirective) {
1475#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1476 case DirectiveEnum: \
1477 CancelKind = Builder.getInt32(Value); \
1478 break;
1479#include "llvm/Frontend/OpenMP/OMPKinds.def"
1480 default:
1481 llvm_unreachable("Unknown cancel kind!");
1482 }
1483
1484 uint32_t SrcLocStrSize;
1485 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1486 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1487 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1489 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancel), Args);
1490
1491 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1492 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1493 return Err;
1494
1495 // Update the insertion point and remove the terminator we introduced.
1496 Builder.SetInsertPoint(UI->getParent());
1497 UI->eraseFromParent();
1498
1499 return Builder.saveIP();
1500}
1501
1504 omp::Directive CanceledDirective) {
1505 if (!updateToLocation(Loc))
1506 return Loc.IP;
1507
1508 // LLVM utilities like blocks with terminators.
1509 auto *UI = Builder.CreateUnreachable();
1510 Builder.SetInsertPoint(UI);
1511
1512 Value *CancelKind = nullptr;
1513 switch (CanceledDirective) {
1514#define OMP_CANCEL_KIND(Enum, Str, DirectiveEnum, Value) \
1515 case DirectiveEnum: \
1516 CancelKind = Builder.getInt32(Value); \
1517 break;
1518#include "llvm/Frontend/OpenMP/OMPKinds.def"
1519 default:
1520 llvm_unreachable("Unknown cancel kind!");
1521 }
1522
1523 uint32_t SrcLocStrSize;
1524 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1525 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1526 Value *Args[] = {Ident, getOrCreateThreadID(Ident), CancelKind};
1528 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_cancellationpoint), Args);
1529
1530 // The actual cancel logic is shared with others, e.g., cancel_barriers.
1531 if (Error Err = emitCancelationCheckImpl(Result, CanceledDirective))
1532 return Err;
1533
1534 // Update the insertion point and remove the terminator we introduced.
1535 Builder.SetInsertPoint(UI->getParent());
1536 UI->eraseFromParent();
1537
1538 return Builder.saveIP();
1539}
1540
1542 const LocationDescription &Loc, InsertPointTy AllocaIP, Value *&Return,
1543 Value *Ident, Value *DeviceID, Value *NumTeams, Value *NumThreads,
1544 Value *HostPtr, ArrayRef<Value *> KernelArgs) {
1545 if (!updateToLocation(Loc))
1546 return Loc.IP;
1547
1548 Builder.restoreIP(AllocaIP);
1549 auto *KernelArgsPtr =
1550 Builder.CreateAlloca(OpenMPIRBuilder::KernelArgs, nullptr, "kernel_args");
1552
1553 for (unsigned I = 0, Size = KernelArgs.size(); I != Size; ++I) {
1554 llvm::Value *Arg =
1555 Builder.CreateStructGEP(OpenMPIRBuilder::KernelArgs, KernelArgsPtr, I);
1556 Builder.CreateAlignedStore(
1557 KernelArgs[I], Arg,
1558 M.getDataLayout().getPrefTypeAlign(KernelArgs[I]->getType()));
1559 }
1560
1561 SmallVector<Value *> OffloadingArgs{Ident, DeviceID, NumTeams,
1562 NumThreads, HostPtr, KernelArgsPtr};
1563
1565 getOrCreateRuntimeFunction(M, OMPRTL___tgt_target_kernel),
1566 OffloadingArgs);
1567
1568 return Builder.saveIP();
1569}
1570
1572 const LocationDescription &Loc, Value *OutlinedFnID,
1573 EmitFallbackCallbackTy EmitTargetCallFallbackCB, TargetKernelArgs &Args,
1574 Value *DeviceID, Value *RTLoc, InsertPointTy AllocaIP) {
1575
1576 if (!updateToLocation(Loc))
1577 return Loc.IP;
1578
1579 // On top of the arrays that were filled up, the target offloading call
1580 // takes as arguments the device id as well as the host pointer. The host
1581 // pointer is used by the runtime library to identify the current target
1582 // region, so it only has to be unique and not necessarily point to
1583 // anything. It could be the pointer to the outlined function that
1584 // implements the target region, but we aren't using that so that the
1585 // compiler doesn't need to keep that, and could therefore inline the host
1586 // function if proven worthwhile during optimization.
1587
1588 // From this point on, we need to have an ID of the target region defined.
1589 assert(OutlinedFnID && "Invalid outlined function ID!");
1590 (void)OutlinedFnID;
1591
1592 // Return value of the runtime offloading call.
1593 Value *Return = nullptr;
1594
1595 // Arguments for the target kernel.
1596 SmallVector<Value *> ArgsVector;
1597 getKernelArgsVector(Args, Builder, ArgsVector);
1598
1599 // The target region is an outlined function launched by the runtime
1600 // via calls to __tgt_target_kernel().
1601 //
1602 // Note that on the host and CPU targets, the runtime implementation of
1603 // these calls simply call the outlined function without forking threads.
1604 // The outlined functions themselves have runtime calls to
1605 // __kmpc_fork_teams() and __kmpc_fork() for this purpose, codegen'd by
1606 // the compiler in emitTeamsCall() and emitParallelCall().
1607 //
1608 // In contrast, on the NVPTX target, the implementation of
1609 // __tgt_target_teams() launches a GPU kernel with the requested number
1610 // of teams and threads so no additional calls to the runtime are required.
1611 // Check the error code and execute the host version if required.
1612 Builder.restoreIP(emitTargetKernel(
1613 Builder, AllocaIP, Return, RTLoc, DeviceID, Args.NumTeams.front(),
1614 Args.NumThreads.front(), OutlinedFnID, ArgsVector));
1615
1616 BasicBlock *OffloadFailedBlock =
1617 BasicBlock::Create(Builder.getContext(), "omp_offload.failed");
1618 BasicBlock *OffloadContBlock =
1619 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
1620 Value *Failed = Builder.CreateIsNotNull(Return);
1621 Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
1622
1623 auto CurFn = Builder.GetInsertBlock()->getParent();
1624 emitBlock(OffloadFailedBlock, CurFn);
1625 InsertPointOrErrorTy AfterIP = EmitTargetCallFallbackCB(Builder.saveIP());
1626 if (!AfterIP)
1627 return AfterIP.takeError();
1628 Builder.restoreIP(*AfterIP);
1629 emitBranch(OffloadContBlock);
1630 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
1631 return Builder.saveIP();
1632}
1633
1635 Value *CancelFlag, omp::Directive CanceledDirective) {
1636 assert(isLastFinalizationInfoCancellable(CanceledDirective) &&
1637 "Unexpected cancellation!");
1638
1639 // For a cancel barrier we create two new blocks.
1640 BasicBlock *BB = Builder.GetInsertBlock();
1641 BasicBlock *NonCancellationBlock;
1642 if (Builder.GetInsertPoint() == BB->end()) {
1643 // TODO: This branch will not be needed once we moved to the
1644 // OpenMPIRBuilder codegen completely.
1645 NonCancellationBlock = BasicBlock::Create(
1646 BB->getContext(), BB->getName() + ".cont", BB->getParent());
1647 } else {
1648 NonCancellationBlock = SplitBlock(BB, &*Builder.GetInsertPoint());
1650 Builder.SetInsertPoint(BB);
1651 }
1652 BasicBlock *CancellationBlock = BasicBlock::Create(
1653 BB->getContext(), BB->getName() + ".cncl", BB->getParent());
1654
1655 // Jump to them based on the return value.
1656 Value *Cmp = Builder.CreateIsNull(CancelFlag);
1657 Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock,
1658 /* TODO weight */ nullptr, nullptr);
1659
1660 // From the cancellation block we finalize all variables and go to the
1661 // post finalization block that is known to the FiniCB callback.
1662 auto &FI = FinalizationStack.back();
1663 Expected<BasicBlock *> FiniBBOrErr = FI.getFiniBB(Builder);
1664 if (!FiniBBOrErr)
1665 return FiniBBOrErr.takeError();
1666 Builder.SetInsertPoint(CancellationBlock);
1667 Builder.CreateBr(*FiniBBOrErr);
1668
1669 // The continuation block is where code generation continues.
1670 Builder.SetInsertPoint(NonCancellationBlock, NonCancellationBlock->begin());
1671 return Error::success();
1672}
1673
1674/// Create wrapper function used to gather the outlined function's argument
1675/// structure from a shared buffer and to forward them to it when running in
1676/// Generic mode.
1677///
1678/// The outlined function is expected to receive 2 integer arguments followed by
1679/// an optional pointer argument to an argument structure holding the rest.
1681 Function &OutlinedFn) {
1682 size_t NumArgs = OutlinedFn.arg_size();
1683 assert((NumArgs == 2 || NumArgs == 3) &&
1684 "expected a 2-3 argument parallel outlined function");
1685 bool UseArgStruct = NumArgs == 3;
1686
1687 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1688 IRBuilder<>::InsertPointGuard IPG(Builder);
1689 auto *FnTy = FunctionType::get(Builder.getVoidTy(),
1690 {Builder.getInt16Ty(), Builder.getInt32Ty()},
1691 /*isVarArg=*/false);
1692 auto *WrapperFn =
1694 OutlinedFn.getName() + ".wrapper", OMPIRBuilder->M);
1695
1696 WrapperFn->addParamAttr(0, Attribute::NoUndef);
1697 WrapperFn->addParamAttr(0, Attribute::ZExt);
1698 WrapperFn->addParamAttr(1, Attribute::NoUndef);
1699
1700 BasicBlock *EntryBB =
1701 BasicBlock::Create(OMPIRBuilder->M.getContext(), "entry", WrapperFn);
1702 Builder.SetInsertPoint(EntryBB);
1703
1704 // Allocation.
1705 Value *AddrAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1706 /*ArraySize=*/nullptr, "addr");
1707 AddrAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1708 AddrAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1709 AddrAlloca->getName() + ".ascast");
1710
1711 Value *ZeroAlloca = Builder.CreateAlloca(Builder.getInt32Ty(),
1712 /*ArraySize=*/nullptr, "zero");
1713 ZeroAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1714 ZeroAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1715 ZeroAlloca->getName() + ".ascast");
1716
1717 Value *ArgsAlloca = nullptr;
1718 if (UseArgStruct) {
1719 ArgsAlloca = Builder.CreateAlloca(Builder.getPtrTy(),
1720 /*ArraySize=*/nullptr, "global_args");
1721 ArgsAlloca = Builder.CreatePointerBitCastOrAddrSpaceCast(
1722 ArgsAlloca, Builder.getPtrTy(/*AddrSpace=*/0),
1723 ArgsAlloca->getName() + ".ascast");
1724 }
1725
1726 // Initialization.
1727 Builder.CreateStore(WrapperFn->getArg(1), AddrAlloca);
1728 Builder.CreateStore(Builder.getInt32(0), ZeroAlloca);
1729 if (UseArgStruct) {
1730 Builder.CreateCall(
1731 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(
1732 llvm::omp::RuntimeFunction::OMPRTL___kmpc_get_shared_variables),
1733 {ArgsAlloca});
1734 }
1735
1736 SmallVector<Value *, 3> Args{AddrAlloca, ZeroAlloca};
1737
1738 // Load structArg from global_args.
1739 if (UseArgStruct) {
1740 Value *StructArg = Builder.CreateLoad(Builder.getPtrTy(), ArgsAlloca);
1741 StructArg = Builder.CreateInBoundsGEP(Builder.getPtrTy(), StructArg,
1742 {Builder.getInt64(0)});
1743 StructArg = Builder.CreateLoad(Builder.getPtrTy(), StructArg, "structArg");
1744 Args.push_back(StructArg);
1745 }
1746
1747 // Call the outlined function holding the parallel body.
1748 Builder.CreateCall(&OutlinedFn, Args);
1749 Builder.CreateRetVoid();
1750
1751 return WrapperFn;
1752}
1753
1754// Callback used to create OpenMP runtime calls to support
1755// omp parallel clause for the device.
1756// We need to use this callback to replace call to the OutlinedFn in OuterFn
1757// by the call to the OpenMP DeviceRTL runtime function (kmpc_parallel_60)
1759 OpenMPIRBuilder *OMPIRBuilder, Function &OutlinedFn, Function *OuterFn,
1760 BasicBlock *OuterAllocaBB, Value *Ident, Value *IfCondition,
1761 Value *NumThreads, Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1762 Value *ThreadID, const SmallVector<Instruction *, 4> &ToBeDeleted) {
1763 assert(OutlinedFn.arg_size() >= 2 &&
1764 "Expected at least tid and bounded tid as arguments");
1765 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1766
1767 // Add some known attributes.
1768 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1769 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1770 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1771 OutlinedFn.addParamAttr(0, Attribute::NoUndef);
1772 OutlinedFn.addParamAttr(1, Attribute::NoUndef);
1773 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1774
1775 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1776 assert(CI && "Expected call instruction to outlined function");
1777 CI->getParent()->setName("omp_parallel");
1778
1779 Builder.SetInsertPoint(CI);
1780 Type *PtrTy = OMPIRBuilder->VoidPtr;
1781
1782 // Add alloca for kernel args
1783 OpenMPIRBuilder ::InsertPointTy CurrentIP = Builder.saveIP();
1784 Builder.SetInsertPoint(OuterAllocaBB, OuterAllocaBB->getFirstInsertionPt());
1785 AllocaInst *ArgsAlloca =
1786 Builder.CreateAlloca(ArrayType::get(PtrTy, NumCapturedVars));
1787 Value *Args = ArgsAlloca;
1788 // Add address space cast if array for storing arguments is not allocated
1789 // in address space 0
1790 if (ArgsAlloca->getAddressSpace())
1791 Args = Builder.CreatePointerCast(ArgsAlloca, PtrTy);
1792 Builder.restoreIP(CurrentIP);
1793
1794 // Store captured vars which are used by kmpc_parallel_60
1795 for (unsigned Idx = 0; Idx < NumCapturedVars; Idx++) {
1796 Value *V = *(CI->arg_begin() + 2 + Idx);
1797 Value *StoreAddress = Builder.CreateConstInBoundsGEP2_64(
1798 ArrayType::get(PtrTy, NumCapturedVars), Args, 0, Idx);
1799 Builder.CreateStore(V, StoreAddress);
1800 }
1801
1802 Value *Cond =
1803 IfCondition ? Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32)
1804 : Builder.getInt32(1);
1805 Value *NumThreadsArg =
1806 NumThreads ? Builder.CreateZExtOrTrunc(NumThreads, OMPIRBuilder->Int32)
1807 : Builder.getInt32(-1);
1808
1809 // If this is not a Generic kernel, we can skip generating the wrapper.
1810 Value *WrapperFn;
1811 if (isGenericKernel(*OuterFn))
1812 WrapperFn = createTargetParallelWrapper(OMPIRBuilder, OutlinedFn);
1813 else
1814 WrapperFn = Constant::getNullValue(PtrTy);
1815
1816 // Build kmpc_parallel_60 call
1817 Value *Parallel60CallArgs[] = {
1818 /* identifier*/ Ident,
1819 /* global thread num*/ ThreadID,
1820 /* if expression */ Cond,
1821 /* number of threads */ NumThreadsArg,
1822 /* Proc bind */ Builder.getInt32(-1),
1823 /* outlined function */ &OutlinedFn,
1824 /* wrapper function */ WrapperFn,
1825 /* arguments of the outlined funciton*/ Args,
1826 /* number of arguments */ Builder.getInt64(NumCapturedVars),
1827 /* strict for number of threads */ Builder.getInt32(0)};
1828
1829 FunctionCallee RTLFn =
1830 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_parallel_60);
1831
1832 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, Parallel60CallArgs);
1833
1834 LLVM_DEBUG(dbgs() << "With kmpc_parallel_60 placed: "
1835 << *Builder.GetInsertBlock()->getParent() << "\n");
1836
1837 // Initialize the local TID stack location with the argument value.
1838 Builder.SetInsertPoint(PrivTID);
1839 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1840 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1841 PrivTIDAddr);
1842
1843 // Remove redundant call to the outlined function.
1844 CI->eraseFromParent();
1845
1846 for (Instruction *I : ToBeDeleted) {
1847 I->eraseFromParent();
1848 }
1849}
1850
1851// Callback used to create OpenMP runtime calls to support
1852// omp parallel clause for the host.
1853// We need to use this callback to replace call to the OutlinedFn in OuterFn
1854// by the call to the OpenMP host runtime function ( __kmpc_fork_call[_if])
1855static void
1857 Function *OuterFn, Value *Ident, Value *IfCondition,
1858 Instruction *PrivTID, AllocaInst *PrivTIDAddr,
1859 const SmallVector<Instruction *, 4> &ToBeDeleted) {
1860 IRBuilder<> &Builder = OMPIRBuilder->Builder;
1861 FunctionCallee RTLFn;
1862 if (IfCondition) {
1863 RTLFn =
1864 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call_if);
1865 } else {
1866 RTLFn =
1867 OMPIRBuilder->getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_fork_call);
1868 }
1869 if (auto *F = dyn_cast<Function>(RTLFn.getCallee())) {
1870 if (!F->hasMetadata(LLVMContext::MD_callback)) {
1871 LLVMContext &Ctx = F->getContext();
1872 MDBuilder MDB(Ctx);
1873 // Annotate the callback behavior of the __kmpc_fork_call:
1874 // - The callback callee is argument number 2 (microtask).
1875 // - The first two arguments of the callback callee are unknown (-1).
1876 // - All variadic arguments to the __kmpc_fork_call are passed to the
1877 // callback callee.
1878 F->addMetadata(LLVMContext::MD_callback,
1880 2, {-1, -1},
1881 /* VarArgsArePassed */ true)}));
1882 }
1883 }
1884 // Add some known attributes.
1885 OutlinedFn.addParamAttr(0, Attribute::NoAlias);
1886 OutlinedFn.addParamAttr(1, Attribute::NoAlias);
1887 OutlinedFn.addFnAttr(Attribute::NoUnwind);
1888
1889 assert(OutlinedFn.arg_size() >= 2 &&
1890 "Expected at least tid and bounded tid as arguments");
1891 unsigned NumCapturedVars = OutlinedFn.arg_size() - /* tid & bounded tid */ 2;
1892
1893 CallInst *CI = cast<CallInst>(OutlinedFn.user_back());
1894 CI->getParent()->setName("omp_parallel");
1895 Builder.SetInsertPoint(CI);
1896
1897 // Build call __kmpc_fork_call[_if](Ident, n, microtask, var1, .., varn);
1898 Value *ForkCallArgs[] = {Ident, Builder.getInt32(NumCapturedVars),
1899 &OutlinedFn};
1900
1901 SmallVector<Value *, 16> RealArgs;
1902 RealArgs.append(std::begin(ForkCallArgs), std::end(ForkCallArgs));
1903 if (IfCondition) {
1904 Value *Cond = Builder.CreateSExtOrTrunc(IfCondition, OMPIRBuilder->Int32);
1905 RealArgs.push_back(Cond);
1906 }
1907 RealArgs.append(CI->arg_begin() + /* tid & bound tid */ 2, CI->arg_end());
1908
1909 // __kmpc_fork_call_if always expects a void ptr as the last argument
1910 // If there are no arguments, pass a null pointer.
1911 auto PtrTy = OMPIRBuilder->VoidPtr;
1912 if (IfCondition && NumCapturedVars == 0) {
1913 Value *NullPtrValue = Constant::getNullValue(PtrTy);
1914 RealArgs.push_back(NullPtrValue);
1915 }
1916
1917 OMPIRBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
1918
1919 LLVM_DEBUG(dbgs() << "With fork_call placed: "
1920 << *Builder.GetInsertBlock()->getParent() << "\n");
1921
1922 // Initialize the local TID stack location with the argument value.
1923 Builder.SetInsertPoint(PrivTID);
1924 Function::arg_iterator OutlinedAI = OutlinedFn.arg_begin();
1925 Builder.CreateStore(Builder.CreateLoad(OMPIRBuilder->Int32, OutlinedAI),
1926 PrivTIDAddr);
1927
1928 // Remove redundant call to the outlined function.
1929 CI->eraseFromParent();
1930
1931 for (Instruction *I : ToBeDeleted) {
1932 I->eraseFromParent();
1933 }
1934}
1935
1937 const LocationDescription &Loc, InsertPointTy OuterAllocIP,
1938 ArrayRef<BasicBlock *> OuterDeallocBlocks, BodyGenCallbackTy BodyGenCB,
1939 PrivatizeCallbackTy PrivCB, FinalizeCallbackTy FiniCB, Value *IfCondition,
1940 Value *NumThreads, omp::ProcBindKind ProcBind, bool IsCancellable) {
1941 assert(!isConflictIP(Loc.IP, OuterAllocIP) && "IPs must not be ambiguous");
1942
1943 if (!updateToLocation(Loc))
1944 return Loc.IP;
1945
1946 uint32_t SrcLocStrSize;
1947 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
1948 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
1949 const bool NeedThreadID = NumThreads || Config.isTargetDevice() ||
1950 (ProcBind != OMP_PROC_BIND_default);
1951 Value *ThreadID = NeedThreadID ? getOrCreateThreadID(Ident) : nullptr;
1952 // If we generate code for the target device, we need to allocate
1953 // struct for aggregate params in the device default alloca address space.
1954 // OpenMP runtime requires that the params of the extracted functions are
1955 // passed as zero address space pointers. This flag ensures that extracted
1956 // function arguments are declared in zero address space
1957 bool ArgsInZeroAddressSpace = Config.isTargetDevice();
1958
1959 // Build call __kmpc_push_num_threads(&Ident, global_tid, num_threads)
1960 // only if we compile for host side.
1961 if (NumThreads && !Config.isTargetDevice()) {
1962 Value *Args[] = {
1963 Ident, ThreadID,
1964 Builder.CreateIntCast(NumThreads, Int32, /*isSigned*/ false)};
1966 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_num_threads), Args);
1967 }
1968
1969 if (ProcBind != OMP_PROC_BIND_default) {
1970 // Build call __kmpc_push_proc_bind(&Ident, global_tid, proc_bind)
1971 Value *Args[] = {
1972 Ident, ThreadID,
1973 ConstantInt::get(Int32, unsigned(ProcBind), /*isSigned=*/true)};
1975 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_push_proc_bind), Args);
1976 }
1977
1978 BasicBlock *InsertBB = Builder.GetInsertBlock();
1979 Function *OuterFn = InsertBB->getParent();
1980
1981 // Save the outer alloca block because the insertion iterator may get
1982 // invalidated and we still need this later.
1983 BasicBlock *OuterAllocaBlock = OuterAllocIP.getBlock();
1984
1985 // Vector to remember instructions we used only during the modeling but which
1986 // we want to delete at the end.
1988
1989 // Change the location to the outer alloca insertion point to create and
1990 // initialize the allocas we pass into the parallel region.
1991 InsertPointTy NewOuter(OuterAllocaBlock, OuterAllocaBlock->begin());
1992 Builder.restoreIP(NewOuter);
1993 AllocaInst *TIDAddrAlloca = Builder.CreateAlloca(Int32, nullptr, "tid.addr");
1994 AllocaInst *ZeroAddrAlloca =
1995 Builder.CreateAlloca(Int32, nullptr, "zero.addr");
1996 Instruction *TIDAddr = TIDAddrAlloca;
1997 Instruction *ZeroAddr = ZeroAddrAlloca;
1998 if (ArgsInZeroAddressSpace && M.getDataLayout().getAllocaAddrSpace() != 0) {
1999 // Add additional casts to enforce pointers in zero address space
2000 TIDAddr = new AddrSpaceCastInst(
2001 TIDAddrAlloca, PointerType ::get(M.getContext(), 0), "tid.addr.ascast");
2002 TIDAddr->insertAfter(TIDAddrAlloca->getIterator());
2003 ToBeDeleted.push_back(TIDAddr);
2004 ZeroAddr = new AddrSpaceCastInst(ZeroAddrAlloca,
2005 PointerType ::get(M.getContext(), 0),
2006 "zero.addr.ascast");
2007 ZeroAddr->insertAfter(ZeroAddrAlloca->getIterator());
2008 ToBeDeleted.push_back(ZeroAddr);
2009 }
2010
2011 // We only need TIDAddr and ZeroAddr for modeling purposes to get the
2012 // associated arguments in the outlined function, so we delete them later.
2013 ToBeDeleted.push_back(TIDAddrAlloca);
2014 ToBeDeleted.push_back(ZeroAddrAlloca);
2015
2016 // Create an artificial insertion point that will also ensure the blocks we
2017 // are about to split are not degenerated.
2018 auto *UI = new UnreachableInst(Builder.getContext(), InsertBB);
2019
2020 BasicBlock *EntryBB = UI->getParent();
2021 BasicBlock *PRegEntryBB = EntryBB->splitBasicBlock(UI, "omp.par.entry");
2022 BasicBlock *PRegBodyBB = PRegEntryBB->splitBasicBlock(UI, "omp.par.region");
2023 BasicBlock *PRegPreFiniBB =
2024 PRegBodyBB->splitBasicBlock(UI, "omp.par.pre_finalize");
2025 BasicBlock *PRegExitBB = PRegPreFiniBB->splitBasicBlock(UI, "omp.par.exit");
2026
2027 auto FiniCBWrapper = [&](InsertPointTy IP) {
2028 // Hide "open-ended" blocks from the given FiniCB by setting the right jump
2029 // target to the region exit block.
2030 if (IP.getBlock()->end() == IP.getPoint()) {
2032 Builder.restoreIP(IP);
2033 Instruction *I = Builder.CreateBr(PRegExitBB);
2034 IP = InsertPointTy(I->getParent(), I->getIterator());
2035 }
2036 assert(IP.getBlock()->getTerminator()->getNumSuccessors() == 1 &&
2037 IP.getBlock()->getTerminator()->getSuccessor(0) == PRegExitBB &&
2038 "Unexpected insertion point for finalization call!");
2039 return FiniCB(IP);
2040 };
2041
2042 FinalizationStack.push_back({FiniCBWrapper, OMPD_parallel, IsCancellable});
2043
2044 // Generate the privatization allocas in the block that will become the entry
2045 // of the outlined function.
2046 Builder.SetInsertPoint(PRegEntryBB->getTerminator());
2047 InsertPointTy InnerAllocaIP = Builder.saveIP();
2048
2049 AllocaInst *PrivTIDAddr =
2050 Builder.CreateAlloca(Int32, nullptr, "tid.addr.local");
2051 Instruction *PrivTID = Builder.CreateLoad(Int32, PrivTIDAddr, "tid");
2052
2053 // Add some fake uses for OpenMP provided arguments.
2054 ToBeDeleted.push_back(Builder.CreateLoad(Int32, TIDAddr, "tid.addr.use"));
2055 Instruction *ZeroAddrUse =
2056 Builder.CreateLoad(Int32, ZeroAddr, "zero.addr.use");
2057 ToBeDeleted.push_back(ZeroAddrUse);
2058
2059 // EntryBB
2060 // |
2061 // V
2062 // PRegionEntryBB <- Privatization allocas are placed here.
2063 // |
2064 // V
2065 // PRegionBodyBB <- BodeGen is invoked here.
2066 // |
2067 // V
2068 // PRegPreFiniBB <- The block we will start finalization from.
2069 // |
2070 // V
2071 // PRegionExitBB <- A common exit to simplify block collection.
2072 //
2073
2074 LLVM_DEBUG(dbgs() << "Before body codegen: " << *OuterFn << "\n");
2075
2076 // Let the caller create the body.
2077 assert(BodyGenCB && "Expected body generation callback!");
2078 InsertPointTy CodeGenIP(PRegBodyBB, PRegBodyBB->begin());
2079 if (Error Err = BodyGenCB(InnerAllocaIP, CodeGenIP, PRegExitBB))
2080 return Err;
2081
2082 LLVM_DEBUG(dbgs() << "After body codegen: " << *OuterFn << "\n");
2083
2084 // If OuterFn is a Generic kernel, we need to use device shared memory to
2085 // allocate argument structures. Otherwise, we use stack allocations as usual.
2086 bool UsesDeviceSharedMemory =
2087 Config.isTargetDevice() && isGenericKernel(*OuterFn);
2088 std::unique_ptr<OutlineInfo> OI =
2089 UsesDeviceSharedMemory
2090 ? std::make_unique<DeviceSharedMemOutlineInfo>(*this)
2091 : std::make_unique<OutlineInfo>();
2092
2093 if (Config.isTargetDevice()) {
2094 // Generate OpenMP target specific runtime call
2095 OI->PostOutlineCB = [=, ToBeDeletedVec =
2096 std::move(ToBeDeleted)](Function &OutlinedFn) {
2097 targetParallelCallback(this, OutlinedFn, OuterFn, OuterAllocaBlock, Ident,
2098 IfCondition, NumThreads, PrivTID, PrivTIDAddr,
2099 ThreadID, ToBeDeletedVec);
2100 };
2101 } else {
2102 // Generate OpenMP host runtime call
2103 OI->PostOutlineCB = [=, ToBeDeletedVec =
2104 std::move(ToBeDeleted)](Function &OutlinedFn) {
2105 hostParallelCallback(this, OutlinedFn, OuterFn, Ident, IfCondition,
2106 PrivTID, PrivTIDAddr, ToBeDeletedVec);
2107 };
2108 }
2109
2110 OI->FixUpNonEntryAllocas = true;
2111 OI->OuterAllocBB = OuterAllocaBlock;
2112 OI->EntryBB = PRegEntryBB;
2113 OI->ExitBB = PRegExitBB;
2114 OI->OuterDeallocBBs.reserve(OuterDeallocBlocks.size());
2115 copy(OuterDeallocBlocks, OI->OuterDeallocBBs.end());
2116
2117 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
2119 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
2120
2121 CodeExtractorAnalysisCache CEAC(*OuterFn);
2122 CodeExtractor Extractor(Blocks, /* DominatorTree */ nullptr,
2123 /* AggregateArgs */ false,
2124 /* BlockFrequencyInfo */ nullptr,
2125 /* BranchProbabilityInfo */ nullptr,
2126 /* AssumptionCache */ nullptr,
2127 /* AllowVarArgs */ true,
2128 /* AllowAlloca */ true,
2129 /* AllocationBlock */ OuterAllocaBlock,
2130 /* DeallocationBlocks */ {},
2131 /* Suffix */ ".omp_par", ArgsInZeroAddressSpace);
2132
2133 // Find inputs to, outputs from the code region.
2134 BasicBlock *CommonExit = nullptr;
2135 SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
2136 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
2137
2138 Extractor.findInputsOutputs(Inputs, Outputs, SinkingCands,
2139 /*CollectGlobalInputs=*/true);
2140
2141 Inputs.remove_if([&](Value *I) {
2143 return GV->getValueType() == OpenMPIRBuilder::Ident;
2144
2145 return false;
2146 });
2147
2148 LLVM_DEBUG(dbgs() << "Before privatization: " << *OuterFn << "\n");
2149
2150 FunctionCallee TIDRTLFn =
2151 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_global_thread_num);
2152
2153 auto PrivHelper = [&](Value &V) -> Error {
2154 if (&V == TIDAddr || &V == ZeroAddr) {
2155 OI->ExcludeArgsFromAggregate.push_back(&V);
2156 return Error::success();
2157 }
2158
2160 for (Use &U : V.uses())
2161 if (auto *UserI = dyn_cast<Instruction>(U.getUser()))
2162 if (ParallelRegionBlockSet.count(UserI->getParent()))
2163 Uses.insert(&U);
2164
2165 // __kmpc_fork_call expects extra arguments as pointers. If the input
2166 // already has a pointer type, everything is fine. Otherwise, store the
2167 // value onto stack and load it back inside the to-be-outlined region. This
2168 // will ensure only the pointer will be passed to the function.
2169 // FIXME: if there are more than 15 trailing arguments, they must be
2170 // additionally packed in a struct.
2171 Value *Inner = &V;
2172 if (!V.getType()->isPointerTy()) {
2174 LLVM_DEBUG(llvm::dbgs() << "Forwarding input as pointer: " << V << "\n");
2175
2176 Builder.restoreIP(OuterAllocIP);
2177 Value *Ptr;
2178 if (UsesDeviceSharedMemory) {
2179 // Use device shared memory instead, if needed.
2180 Ptr = createOMPAllocShared(OuterAllocIP, V.getType(),
2181 V.getName() + ".reloaded");
2182 for (BasicBlock *DeallocBlock : OuterDeallocBlocks)
2184 InsertPointTy(DeallocBlock, DeallocBlock->getFirstInsertionPt()),
2185 Ptr, V.getType());
2186 } else {
2187 Ptr = Builder.CreateAlloca(V.getType(), nullptr,
2188 V.getName() + ".reloaded");
2189 }
2190
2191 // Store to stack at end of the block that currently branches to the entry
2192 // block of the to-be-outlined region.
2193 Builder.SetInsertPoint(InsertBB,
2194 InsertBB->getTerminator()->getIterator());
2195 Builder.CreateStore(&V, Ptr);
2196
2197 // Load back next to allocations in the to-be-outlined region.
2198 Builder.restoreIP(InnerAllocaIP);
2199 Inner = Builder.CreateLoad(V.getType(), Ptr);
2200 }
2201
2202 Value *ReplacementValue = nullptr;
2203 CallInst *CI = dyn_cast<CallInst>(&V);
2204 if (CI && CI->getCalledFunction() == TIDRTLFn.getCallee()) {
2205 ReplacementValue = PrivTID;
2206 } else {
2207 InsertPointOrErrorTy AfterIP =
2208 PrivCB(InnerAllocaIP, Builder.saveIP(), V, *Inner, ReplacementValue);
2209 if (!AfterIP)
2210 return AfterIP.takeError();
2211 Builder.restoreIP(*AfterIP);
2212 InnerAllocaIP = {
2213 InnerAllocaIP.getBlock(),
2214 InnerAllocaIP.getBlock()->getTerminator()->getIterator()};
2215
2216 assert(ReplacementValue &&
2217 "Expected copy/create callback to set replacement value!");
2218 if (ReplacementValue == &V)
2219 return Error::success();
2220 }
2221
2222 for (Use *UPtr : Uses)
2223 UPtr->set(ReplacementValue);
2224
2225 return Error::success();
2226 };
2227
2228 // Reset the inner alloca insertion as it will be used for loading the values
2229 // wrapped into pointers before passing them into the to-be-outlined region.
2230 // Configure it to insert immediately after the fake use of zero address so
2231 // that they are available in the generated body and so that the
2232 // OpenMP-related values (thread ID and zero address pointers) remain leading
2233 // in the argument list.
2234 InnerAllocaIP = IRBuilder<>::InsertPoint(
2235 ZeroAddrUse->getParent(), ZeroAddrUse->getNextNode()->getIterator());
2236
2237 // Reset the outer alloca insertion point to the entry of the relevant block
2238 // in case it was invalidated.
2239 OuterAllocIP = IRBuilder<>::InsertPoint(
2240 OuterAllocaBlock, OuterAllocaBlock->getFirstInsertionPt());
2241
2242 for (Value *Input : Inputs) {
2243 LLVM_DEBUG(dbgs() << "Captured input: " << *Input << "\n");
2244 if (Error Err = PrivHelper(*Input))
2245 return Err;
2246 }
2247 LLVM_DEBUG({
2248 for (Value *Output : Outputs)
2249 LLVM_DEBUG(dbgs() << "Captured output: " << *Output << "\n");
2250 });
2251 assert(Outputs.empty() &&
2252 "OpenMP outlining should not produce live-out values!");
2253
2254 LLVM_DEBUG(dbgs() << "After privatization: " << *OuterFn << "\n");
2255 LLVM_DEBUG({
2256 for (auto *BB : Blocks)
2257 dbgs() << " PBR: " << BB->getName() << "\n";
2258 });
2259
2260 // Adjust the finalization stack, verify the adjustment, and call the
2261 // finalize function a last time to finalize values between the pre-fini
2262 // block and the exit block if we left the parallel "the normal way".
2263 auto FiniInfo = FinalizationStack.pop_back_val();
2264 (void)FiniInfo;
2265 assert(FiniInfo.DK == OMPD_parallel &&
2266 "Unexpected finalization stack state!");
2267
2268 Instruction *PRegPreFiniTI = PRegPreFiniBB->getTerminator();
2269
2270 InsertPointTy PreFiniIP(PRegPreFiniBB, PRegPreFiniTI->getIterator());
2271 Expected<BasicBlock *> FiniBBOrErr = FiniInfo.getFiniBB(Builder);
2272 if (!FiniBBOrErr)
2273 return FiniBBOrErr.takeError();
2274 {
2276 Builder.restoreIP(PreFiniIP);
2277 Builder.CreateBr(*FiniBBOrErr);
2278 // There's currently a branch to omp.par.exit. Delete it. We will get there
2279 // via the fini block
2280 if (Instruction *Term = Builder.GetInsertBlock()->getTerminator())
2281 Term->eraseFromParent();
2282 }
2283
2284 // Register the outlined info.
2285 addOutlineInfo(std::move(OI));
2286
2287 InsertPointTy AfterIP(UI->getParent(), UI->getParent()->end());
2288 UI->eraseFromParent();
2289
2290 return AfterIP;
2291}
2292
2294 // Build call void __kmpc_flush(ident_t *loc)
2295 uint32_t SrcLocStrSize;
2296 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2297 Value *Args[] = {getOrCreateIdent(SrcLocStr, SrcLocStrSize)};
2298
2300 Args);
2301}
2302
2304 if (!updateToLocation(Loc))
2305 return;
2306 emitFlush(Loc);
2307}
2308
2310 Value *Message) {
2311 if (!updateToLocation(Loc))
2312 return;
2313
2314 // Build call void __kmpc_error(ident_t *loc, int severity,
2315 // const char *message)
2316 uint32_t SrcLocStrSize;
2317 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2318 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2319 // Severity: 1 = warning, 2 = fatal.
2320 Value *Severity = ConstantInt::get(Int32, IsFatal ? 2 : 1);
2321 Value *MessageArg = Message ? Message : ConstantPointerNull::get(Int8Ptr);
2322 Value *Args[] = {Ident, Severity, MessageArg};
2323
2325 Args);
2326}
2327
2329 // Build call __kmpc_omp_taskyield(loc, thread_id, 0);
2330 uint32_t SrcLocStrSize;
2331 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2332 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2333 Constant *I32Null = ConstantInt::getNullValue(Int32);
2334 Value *Args[] = {Ident, getOrCreateThreadID(Ident), I32Null};
2335
2337 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskyield), Args);
2338}
2339
2345
2347 const DependData &Dep) {
2348 // Store the pointer to the variable
2349 Value *Addr = Builder.CreateStructGEP(
2350 DependInfo, Entry,
2351 static_cast<unsigned int>(RTLDependInfoFields::BaseAddr));
2352 Value *DepValPtr = Builder.CreatePtrToInt(Dep.DepVal, SizeTy);
2353 Builder.CreateStore(DepValPtr, Addr);
2354 // Store the size of the variable
2355 Value *Size = Builder.CreateStructGEP(
2356 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Len));
2357 Builder.CreateStore(
2358 ConstantInt::get(SizeTy,
2359 M.getDataLayout().getTypeStoreSize(Dep.DepValueType)),
2360 Size);
2361 // Store the dependency kind
2362 Value *Flags = Builder.CreateStructGEP(
2363 DependInfo, Entry, static_cast<unsigned int>(RTLDependInfoFields::Flags));
2364 Builder.CreateStore(ConstantInt::get(Builder.getInt8Ty(),
2365 static_cast<unsigned int>(Dep.DepKind)),
2366 Flags);
2367}
2368
2369// Processes the dependencies in Dependencies and does the following
2370// - Allocates space on the stack of an array of DependInfo objects
2371// - Populates each DependInfo object with relevant information of
2372// the corresponding dependence.
2373// - All code is inserted in the entry block of the current function.
2375 OpenMPIRBuilder &OMPBuilder,
2377 // Early return if we have no dependencies to process
2378 if (Dependencies.empty())
2379 return nullptr;
2380
2381 // Given a vector of DependData objects, in this function we create an
2382 // array on the stack that holds kmp_depend_info objects corresponding
2383 // to each dependency. This is then passed to the OpenMP runtime.
2384 // For example, if there are 'n' dependencies then the following psedo
2385 // code is generated. Assume the first dependence is on a variable 'a'
2386 //
2387 // \code{c}
2388 // DepArray = alloc(n x sizeof(kmp_depend_info);
2389 // idx = 0;
2390 // DepArray[idx].base_addr = ptrtoint(&a);
2391 // DepArray[idx].len = 8;
2392 // DepArray[idx].flags = Dep.DepKind; /*(See OMPContants.h for DepKind)*/
2393 // ++idx;
2394 // DepArray[idx].base_addr = ...;
2395 // \endcode
2396
2397 IRBuilderBase &Builder = OMPBuilder.Builder;
2398 Type *DependInfo = OMPBuilder.DependInfo;
2399
2400 Value *DepArray = nullptr;
2401 OpenMPIRBuilder::InsertPointTy OldIP = Builder.saveIP();
2402 Builder.SetInsertPoint(
2404
2405 Type *DepArrayTy = ArrayType::get(DependInfo, Dependencies.size());
2406 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2407
2408 Builder.restoreIP(OldIP);
2409
2410 for (const auto &[DepIdx, Dep] : enumerate(Dependencies)) {
2411 Value *Base =
2412 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2413 OMPBuilder.emitTaskDependency(Builder, Base, Dep);
2414 }
2415 return DepArray;
2416}
2417
2419 // Build call kmp_int32 __kmpc_omp_taskwait(ident_t *loc, kmp_int32
2420 // global_tid);
2421 uint32_t SrcLocStrSize;
2422 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2423 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2424 Value *Args[] = {Ident, getOrCreateThreadID(Ident)};
2425
2426 // Ignore return result until untied tasks are supported.
2428 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_taskwait), Args);
2429}
2430
2432 DependenciesInfo Dependencies) {
2433 if (!updateToLocation(Loc))
2434 return;
2435
2436 Value *DepArray = nullptr;
2437 Type *DepArrayTy = nullptr;
2438 Value *NumDeps = nullptr;
2439 if (Dependencies.DepArray) {
2440 DepArray = Dependencies.DepArray;
2441 NumDeps = Dependencies.NumDeps;
2442 } else if (!Dependencies.Deps.empty()) {
2443 InsertPointTy OldIP = Builder.saveIP();
2444 BasicBlock &entryBB =
2445 Builder.GetInsertBlock()->getParent()->getEntryBlock();
2446 Builder.SetInsertPoint(&entryBB, entryBB.getFirstInsertionPt());
2447
2448 DepArrayTy = ArrayType::get(DependInfo, Dependencies.Deps.size());
2449 DepArray = Builder.CreateAlloca(DepArrayTy, nullptr, ".dep.arr.addr");
2450 NumDeps = Builder.getInt32(Dependencies.Deps.size());
2451
2452 Builder.restoreIP(OldIP);
2453 for (const auto &[DepIdx, Dep] : enumerate(Dependencies.Deps)) {
2454 Value *Base =
2455 Builder.CreateConstInBoundsGEP2_64(DepArrayTy, DepArray, 0, DepIdx);
2456 this->emitTaskDependency(Builder, Base, Dep);
2457 }
2458 }
2459
2460 if (DepArray) {
2461 uint32_t SrcLocStrSize;
2462 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2463 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2464 Value *Args[] = {
2465 Ident,
2466 getOrCreateThreadID(Ident),
2467 NumDeps,
2468 DepArray,
2469 ConstantInt::get(Builder.getInt32Ty(), 0),
2471 ConstantInt::get(Builder.getInt32Ty(), false)};
2474 omp::RuntimeFunction::OMPRTL___kmpc_omp_taskwait_deps_51),
2475 Args);
2476 } else {
2478 }
2479}
2480
2481/// Create the task duplication function passed to kmpc_taskloop.
2482Expected<Value *> OpenMPIRBuilder::createTaskDuplicationFunction(
2483 Type *PrivatesTy, int32_t PrivatesIndex, TaskDupCallbackTy DupCB) {
2484 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2485 if (!DupCB)
2487 PointerType::get(Builder.getContext(), ProgramAddressSpace));
2488
2489 // From OpenMP Runtime p_task_dup_t:
2490 // Routine optionally generated by the compiler for setting the lastprivate
2491 // flag and calling needed constructors for private/firstprivate objects (used
2492 // to form taskloop tasks from pattern task) Parameters: dest task, src task,
2493 // lastprivate flag.
2494 // typedef void (*p_task_dup_t)(kmp_task_t *, kmp_task_t *, kmp_int32);
2495
2496 auto *VoidPtrTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2497
2498 FunctionType *DupFuncTy = FunctionType::get(
2499 Builder.getVoidTy(), {VoidPtrTy, VoidPtrTy, Builder.getInt32Ty()},
2500 /*isVarArg=*/false);
2501
2502 Function *DupFunction = Function::Create(DupFuncTy, Function::InternalLinkage,
2503 "omp_taskloop_dup", M);
2504 Value *DestTaskArg = DupFunction->getArg(0);
2505 Value *SrcTaskArg = DupFunction->getArg(1);
2506 Value *LastprivateFlagArg = DupFunction->getArg(2);
2507 DestTaskArg->setName("dest_task");
2508 SrcTaskArg->setName("src_task");
2509 LastprivateFlagArg->setName("lastprivate_flag");
2510
2511 IRBuilderBase::InsertPointGuard Guard(Builder);
2512 Builder.SetInsertPoint(
2513 BasicBlock::Create(Builder.getContext(), "entry", DupFunction));
2514
2515 auto GetTaskContextPtrFromArg = [&](Value *Arg) -> Value * {
2516 Type *TaskWithPrivatesTy =
2517 StructType::get(Builder.getContext(), {Task, PrivatesTy});
2518 Value *TaskPrivates = Builder.CreateGEP(
2519 TaskWithPrivatesTy, Arg, {Builder.getInt32(0), Builder.getInt32(1)});
2520 Value *ContextPtr = Builder.CreateGEP(
2521 PrivatesTy, TaskPrivates,
2522 {Builder.getInt32(0), Builder.getInt32(PrivatesIndex)});
2523 return ContextPtr;
2524 };
2525
2526 Value *DestTaskContextPtr = GetTaskContextPtrFromArg(DestTaskArg);
2527 Value *SrcTaskContextPtr = GetTaskContextPtrFromArg(SrcTaskArg);
2528
2529 DestTaskContextPtr->setName("destPtr");
2530 SrcTaskContextPtr->setName("srcPtr");
2531
2532 InsertPointTy AllocaIP(&DupFunction->getEntryBlock(),
2533 DupFunction->getEntryBlock().begin());
2534 InsertPointTy CodeGenIP = Builder.saveIP();
2535 Expected<IRBuilderBase::InsertPoint> AfterIPOrError =
2536 DupCB(AllocaIP, CodeGenIP, DestTaskContextPtr, SrcTaskContextPtr);
2537 if (!AfterIPOrError)
2538 return AfterIPOrError.takeError();
2539 Builder.restoreIP(*AfterIPOrError);
2540
2541 Builder.CreateRetVoid();
2542
2543 return DupFunction;
2544}
2545
2546OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
2547 const LocationDescription &Loc, InsertPointTy AllocaIP,
2548 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2549 llvm::function_ref<llvm::Expected<llvm::CanonicalLoopInfo *>()> LoopInfo,
2550 Value *LBVal, Value *UBVal, Value *StepVal, bool Untied, Value *IfCond,
2551 Value *GrainSize, bool NoGroup, int Sched, Value *Final, bool Mergeable,
2552 Value *Priority, uint64_t NumOfCollapseLoops, TaskDupCallbackTy DupCB,
2553 Value *TaskContextStructPtrVal) {
2554
2555 if (!updateToLocation(Loc))
2556 return InsertPointTy();
2557
2558 uint32_t SrcLocStrSize;
2559 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2560 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2561
2562 BasicBlock *TaskloopExitBB =
2563 splitBB(Builder, /*CreateBranch=*/true, "taskloop.exit");
2564 BasicBlock *TaskloopBodyBB =
2565 splitBB(Builder, /*CreateBranch=*/true, "taskloop.body");
2566 BasicBlock *TaskloopAllocaBB =
2567 splitBB(Builder, /*CreateBranch=*/true, "taskloop.alloca");
2568
2569 InsertPointTy TaskloopAllocaIP =
2570 InsertPointTy(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2571 InsertPointTy TaskloopBodyIP =
2572 InsertPointTy(TaskloopBodyBB, TaskloopBodyBB->begin());
2573
2574 if (Error Err = BodyGenCB(TaskloopAllocaIP, TaskloopBodyIP, TaskloopExitBB))
2575 return Err;
2576
2577 llvm::Expected<llvm::CanonicalLoopInfo *> result = LoopInfo();
2578 if (!result) {
2579 return result.takeError();
2580 }
2581
2582 llvm::CanonicalLoopInfo *CLI = result.get();
2583 auto OI = std::make_unique<OutlineInfo>();
2584 OI->EntryBB = TaskloopAllocaBB;
2585 OI->OuterAllocBB = AllocaIP.getBlock();
2586 OI->ExitBB = TaskloopExitBB;
2587 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2588 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2589
2590 // Add the thread ID argument.
2591 SmallVector<Instruction *> ToBeDeleted;
2592 // dummy instruction to be used as a fake argument
2593 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2594 Builder, AllocaIP, ToBeDeleted, TaskloopAllocaIP, "global.tid", false));
2595 Value *FakeLB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2596 TaskloopAllocaIP, "lb", false, true);
2597 Value *FakeUB = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2598 TaskloopAllocaIP, "ub", false, true);
2599 Value *FakeStep = createFakeIntVal(Builder, AllocaIP, ToBeDeleted,
2600 TaskloopAllocaIP, "step", false, true);
2601 // For Taskloop, we want to force the bounds being the first 3 inputs in the
2602 // aggregate struct
2603 OI->Inputs.insert(FakeLB);
2604 OI->Inputs.insert(FakeUB);
2605 OI->Inputs.insert(FakeStep);
2606 if (TaskContextStructPtrVal)
2607 OI->Inputs.insert(TaskContextStructPtrVal);
2608 assert(((TaskContextStructPtrVal && DupCB) ||
2609 (!TaskContextStructPtrVal && !DupCB)) &&
2610 "Task context struct ptr and duplication callback must be both set "
2611 "or both null");
2612
2613 // It isn't safe to run the duplication bodygen callback inside the post
2614 // outlining callback so this has to be run now before we know the real task
2615 // shareds structure type.
2616 unsigned ProgramAddressSpace = M.getDataLayout().getProgramAddressSpace();
2617 Type *PointerTy = PointerType::get(Builder.getContext(), ProgramAddressSpace);
2618 Type *FakeSharedsTy = StructType::get(
2619 Builder.getContext(),
2620 {FakeLB->getType(), FakeUB->getType(), FakeStep->getType(), PointerTy});
2621 Expected<Value *> TaskDupFnOrErr = createTaskDuplicationFunction(
2622 FakeSharedsTy,
2623 /*PrivatesIndex: the pointer after the three indices above*/ 3, DupCB);
2624 if (!TaskDupFnOrErr) {
2625 return TaskDupFnOrErr.takeError();
2626 }
2627 Value *TaskDupFn = *TaskDupFnOrErr;
2628
2629 OI->PostOutlineCB = [this, Ident, LBVal, UBVal, StepVal, Untied,
2630 TaskloopAllocaBB, CLI, TaskDupFn, ToBeDeleted, IfCond,
2631 GrainSize, NoGroup, Sched, FakeLB, FakeUB, FakeStep,
2632 FakeSharedsTy, Final, Mergeable, Priority,
2633 NumOfCollapseLoops](Function &OutlinedFn) mutable {
2634 // Replace the Stale CI by appropriate RTL function call.
2635 assert(OutlinedFn.hasOneUse() &&
2636 "there must be a single user for the outlined function");
2637 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2638
2639 /* Create the casting for the Bounds Values that can be used when outlining
2640 * to replace the uses of the fakes with real values */
2641 BasicBlock *CodeReplBB = StaleCI->getParent();
2642 Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
2643 Value *CastedLBVal =
2644 Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
2645 Value *CastedUBVal =
2646 Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
2647 Value *CastedStepVal =
2648 Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
2649
2650 Builder.SetInsertPoint(StaleCI);
2651
2652 // Gather the arguments for emitting the runtime call for
2653 // @__kmpc_omp_task_alloc
2654 Function *TaskAllocFn =
2655 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2656
2657 Value *ThreadID = getOrCreateThreadID(Ident);
2658
2659 if (!NoGroup) {
2660 // Emit runtime call for @__kmpc_taskgroup
2661 Function *TaskgroupFn =
2662 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
2663 Builder.CreateCall(TaskgroupFn, {Ident, ThreadID});
2664 }
2665
2666 // `flags` Argument Configuration
2667 // Task is tied if (Flags & 1) == 1.
2668 // Task is untied if (Flags & 1) == 0.
2669 // Task is final if (Flags & 2) == 2.
2670 // Task is not final if (Flags & 2) == 0.
2671 // Task is mergeable if (Flags & 4) == 4.
2672 // Task is not mergeable if (Flags & 4) == 0.
2673 // Task is priority if (Flags & 32) == 32.
2674 // Task is not priority if (Flags & 32) == 0.
2675 Value *Flags = Builder.getInt32(Untied ? 0 : 1);
2676 if (Final)
2677 Flags = Builder.CreateOr(Builder.getInt32(2), Flags);
2678 if (Mergeable)
2679 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2680 if (Priority)
2681 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2682
2683 Value *TaskSize = Builder.getInt64(
2684 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
2685
2686 AllocaInst *ArgStructAlloca =
2688 assert(ArgStructAlloca &&
2689 "Unable to find the alloca instruction corresponding to arguments "
2690 "for extracted function");
2691 std::optional<TypeSize> ArgAllocSize =
2692 ArgStructAlloca->getAllocationSize(M.getDataLayout());
2693 assert(ArgAllocSize &&
2694 "Unable to determine size of arguments for extracted function");
2695 Value *SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
2696
2697 // Emit the @__kmpc_omp_task_alloc runtime call
2698 // The runtime call returns a pointer to an area where the task captured
2699 // variables must be copied before the task is run (TaskData)
2700 CallInst *TaskData = Builder.CreateCall(
2701 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
2702 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
2703 /*task_func=*/&OutlinedFn});
2704
2705 Value *Shareds = StaleCI->getArgOperand(1);
2706 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
2707 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
2708 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
2709 SharedsSize);
2710 // Get the pointer to loop lb, ub, step from task ptr
2711 // and set up the lowerbound,upperbound and step values
2712 llvm::Value *Lb = Builder.CreateGEP(
2713 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(0)});
2714
2715 llvm::Value *Ub = Builder.CreateGEP(
2716 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(1)});
2717
2718 llvm::Value *Step = Builder.CreateGEP(
2719 FakeSharedsTy, TaskShareds, {Builder.getInt32(0), Builder.getInt32(2)});
2720 llvm::Value *Loadstep = Builder.CreateLoad(Builder.getInt64Ty(), Step);
2721
2722 // set up the arguments for emitting kmpc_taskloop runtime call
2723 // setting values for ifval, nogroup, sched, grainsize, task_dup
2724 Value *IfCondVal =
2725 IfCond ? Builder.CreateIntCast(IfCond, Builder.getInt32Ty(), true)
2726 : Builder.getInt32(1);
2727 // As __kmpc_taskgroup is called manually in OMPIRBuilder, NoGroupVal should
2728 // always be 1 when calling __kmpc_taskloop to ensure it is not called again
2729 Value *NoGroupVal = Builder.getInt32(1);
2730 Value *SchedVal = Builder.getInt32(Sched);
2731 Value *GrainSizeVal =
2732 GrainSize ? Builder.CreateIntCast(GrainSize, Builder.getInt64Ty(), true)
2733 : Builder.getInt64(0);
2734 Value *TaskDup = TaskDupFn;
2735
2736 Value *Args[] = {Ident, ThreadID, TaskData, IfCondVal, Lb, Ub,
2737 Loadstep, NoGroupVal, SchedVal, GrainSizeVal, TaskDup};
2738
2739 // taskloop runtime call
2740 Function *TaskloopFn =
2741 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskloop);
2742 Builder.CreateCall(TaskloopFn, Args);
2743
2744 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup if
2745 // nogroup is not defined
2746 if (!NoGroup) {
2747 Function *EndTaskgroupFn =
2748 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
2749 Builder.CreateCall(EndTaskgroupFn, {Ident, ThreadID});
2750 }
2751
2752 StaleCI->eraseFromParent();
2753
2754 Builder.SetInsertPoint(TaskloopAllocaBB, TaskloopAllocaBB->begin());
2755
2756 LoadInst *SharedsOutlined =
2757 Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
2758 OutlinedFn.getArg(1)->replaceUsesWithIf(
2759 SharedsOutlined,
2760 [SharedsOutlined](Use &U) { return U.getUser() != SharedsOutlined; });
2761
2762 Value *IV = CLI->getIndVar();
2763 Type *IVTy = IV->getType();
2764 Constant *One = ConstantInt::get(Builder.getInt64Ty(), 1);
2765
2766 // When outlining, CodeExtractor will create GEP's to the LowerBound and
2767 // UpperBound. These GEP's can be reused for loading the tasks respective
2768 // bounds.
2769 Value *TaskLB = nullptr;
2770 Value *TaskUB = nullptr;
2771 Value *TaskStep = nullptr;
2772 Value *LoadTaskLB = nullptr;
2773 Value *LoadTaskUB = nullptr;
2774 Value *LoadTaskStep = nullptr;
2775 for (Instruction &I : *TaskloopAllocaBB) {
2776 if (I.getOpcode() == Instruction::GetElementPtr) {
2777 GetElementPtrInst &Gep = cast<GetElementPtrInst>(I);
2778 if (ConstantInt *CI = dyn_cast<ConstantInt>(Gep.getOperand(2))) {
2779 switch (CI->getZExtValue()) {
2780 case 0:
2781 TaskLB = &I;
2782 break;
2783 case 1:
2784 TaskUB = &I;
2785 break;
2786 case 2:
2787 TaskStep = &I;
2788 break;
2789 }
2790 }
2791 } else if (I.getOpcode() == Instruction::Load) {
2792 LoadInst &Load = cast<LoadInst>(I);
2793 if (Load.getPointerOperand() == TaskLB) {
2794 assert(TaskLB != nullptr && "Expected value for TaskLB");
2795 LoadTaskLB = &I;
2796 } else if (Load.getPointerOperand() == TaskUB) {
2797 assert(TaskUB != nullptr && "Expected value for TaskUB");
2798 LoadTaskUB = &I;
2799 } else if (Load.getPointerOperand() == TaskStep) {
2800 assert(TaskStep != nullptr && "Expected value for TaskStep");
2801 LoadTaskStep = &I;
2802 }
2803 }
2804 }
2805
2806 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
2807
2808 assert(LoadTaskLB != nullptr && "Expected value for LoadTaskLB");
2809 assert(LoadTaskUB != nullptr && "Expected value for LoadTaskUB");
2810 assert(LoadTaskStep != nullptr && "Expected value for LoadTaskStep");
2811 Value *TripCountMinusOne = Builder.CreateSDiv(
2812 Builder.CreateSub(LoadTaskUB, LoadTaskLB), LoadTaskStep);
2813 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One, "trip_cnt");
2814 Value *CastedTripCount = Builder.CreateIntCast(TripCount, IVTy, true);
2815 Value *CastedTaskLB = Builder.CreateIntCast(LoadTaskLB, IVTy, true);
2816 // set the trip count in the CLI
2817 CLI->setTripCount(CastedTripCount);
2818
2819 Builder.SetInsertPoint(CLI->getBody(),
2820 CLI->getBody()->getFirstInsertionPt());
2821
2822 if (NumOfCollapseLoops > 1) {
2823 llvm::SmallVector<User *> UsersToReplace;
2824 // When using the collapse clause, the bounds of the loop have to be
2825 // adjusted to properly represent the iterator of the outer loop.
2826 Value *IVPlusTaskLB = Builder.CreateAdd(
2827 CLI->getIndVar(),
2828 Builder.CreateSub(CastedTaskLB, ConstantInt::get(IVTy, 1)));
2829 // To ensure every Use is correctly captured, we first want to record
2830 // which users to replace the value in, and then replace the value.
2831 for (auto IVUse = CLI->getIndVar()->uses().begin();
2832 IVUse != CLI->getIndVar()->uses().end(); IVUse++) {
2833 User *IVUser = IVUse->getUser();
2834 if (auto *Op = dyn_cast<BinaryOperator>(IVUser)) {
2835 if (Op->getOpcode() == Instruction::URem ||
2836 Op->getOpcode() == Instruction::UDiv) {
2837 UsersToReplace.push_back(IVUser);
2838 }
2839 }
2840 }
2841 for (User *User : UsersToReplace) {
2842 User->replaceUsesOfWith(CLI->getIndVar(), IVPlusTaskLB);
2843 }
2844 } else {
2845 // The canonical loop is generated with a fixed lower bound. We need to
2846 // update the index calculation code to use the task's lower bound. The
2847 // generated code looks like this:
2848 // %omp_loop.iv = phi ...
2849 // ...
2850 // %tmp = mul [type] %omp_loop.iv, step
2851 // %user_index = add [type] tmp, lb
2852 // OpenMPIRBuilder constructs canonical loops to have exactly three uses
2853 // of the normalised induction variable:
2854 // 1. This one: converting the normalised IV to the user IV
2855 // 2. The increment (add)
2856 // 3. The comparison against the trip count (icmp)
2857 // (1) is the only use that is a mul followed by an add so this cannot
2858 // match other IR.
2859 assert(CLI->getIndVar()->getNumUses() == 3 &&
2860 "Canonical loop should have exactly three uses of the ind var");
2861 for (User *IVUser : CLI->getIndVar()->users()) {
2862 if (auto *Mul = dyn_cast<BinaryOperator>(IVUser)) {
2863 if (Mul->getOpcode() == Instruction::Mul) {
2864 for (User *MulUser : Mul->users()) {
2865 if (auto *Add = dyn_cast<BinaryOperator>(MulUser)) {
2866 if (Add->getOpcode() == Instruction::Add) {
2867 Add->setOperand(1, CastedTaskLB);
2868 }
2869 }
2870 }
2871 }
2872 }
2873 }
2874 }
2875
2876 FakeLB->replaceAllUsesWith(CastedLBVal);
2877 FakeUB->replaceAllUsesWith(CastedUBVal);
2878 FakeStep->replaceAllUsesWith(CastedStepVal);
2879 for (Instruction *I : llvm::reverse(ToBeDeleted)) {
2880 I->eraseFromParent();
2881 }
2882 };
2883
2884 addOutlineInfo(std::move(OI));
2885 Builder.SetInsertPoint(TaskloopExitBB, TaskloopExitBB->begin());
2886 return Builder.saveIP();
2887}
2888
2891 M.getContext(), M.getDataLayout().getPointerSizeInBits());
2893 llvm::Type::getInt32Ty(M.getContext()));
2894}
2895
2897 const LocationDescription &Loc, InsertPointTy AllocaIP,
2898 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB,
2899 bool Tied, Value *Final, Value *IfCondition,
2900 const DependenciesInfo &Dependencies, const AffinityData &Affinities,
2901 bool Mergeable, Value *EventHandle, Value *Priority) {
2902
2903 if (!updateToLocation(Loc))
2904 return InsertPointTy();
2905
2906 uint32_t SrcLocStrSize;
2907 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
2908 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
2909 // The current basic block is split into four basic blocks. After outlining,
2910 // they will be mapped as follows:
2911 // ```
2912 // def current_fn() {
2913 // current_basic_block:
2914 // br label %task.exit
2915 // task.exit:
2916 // ; instructions after task
2917 // }
2918 // def outlined_fn() {
2919 // task.alloca:
2920 // br label %task.body
2921 // task.body:
2922 // ret void
2923 // }
2924 // ```
2925 BasicBlock *TaskExitBB = splitBB(Builder, /*CreateBranch=*/true, "task.exit");
2926 BasicBlock *TaskBodyBB = splitBB(Builder, /*CreateBranch=*/true, "task.body");
2927 BasicBlock *TaskAllocaBB =
2928 splitBB(Builder, /*CreateBranch=*/true, "task.alloca");
2929
2930 InsertPointTy TaskAllocaIP =
2931 InsertPointTy(TaskAllocaBB, TaskAllocaBB->begin());
2932 InsertPointTy TaskBodyIP = InsertPointTy(TaskBodyBB, TaskBodyBB->begin());
2933 if (Error Err = BodyGenCB(TaskAllocaIP, TaskBodyIP, TaskExitBB))
2934 return Err;
2935
2936 auto OI = std::make_unique<OutlineInfo>();
2937 OI->EntryBB = TaskAllocaBB;
2938 OI->OuterAllocBB = AllocaIP.getBlock();
2939 OI->ExitBB = TaskExitBB;
2940 OI->OuterDeallocBBs.reserve(DeallocBlocks.size());
2941 copy(DeallocBlocks, OI->OuterDeallocBBs.end());
2942
2943 // Add the thread ID argument.
2945 OI->ExcludeArgsFromAggregate.push_back(createFakeIntVal(
2946 Builder, AllocaIP, ToBeDeleted, TaskAllocaIP, "global.tid", false));
2947
2948 OI->PostOutlineCB = [this, Ident, Tied, Final, IfCondition, Dependencies,
2949 Affinities, Mergeable, Priority, EventHandle,
2950 TaskAllocaBB,
2951 ToBeDeleted](Function &OutlinedFn) mutable {
2952 // Replace the Stale CI by appropriate RTL function call.
2953 assert(OutlinedFn.hasOneUse() &&
2954 "there must be a single user for the outlined function");
2955 CallInst *StaleCI = cast<CallInst>(OutlinedFn.user_back());
2956
2957 // HasShareds is true if any variables are captured in the outlined region,
2958 // false otherwise.
2959 bool HasShareds = StaleCI->arg_size() > 1;
2960 Builder.SetInsertPoint(StaleCI);
2961
2962 // Gather the arguments for emitting the runtime call for
2963 // @__kmpc_omp_task_alloc
2964 Function *TaskAllocFn =
2965 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_alloc);
2966
2967 // Arguments - `loc_ref` (Ident) and `gtid` (ThreadID)
2968 // call.
2969 Value *ThreadID = getOrCreateThreadID(Ident);
2970
2971 // Argument - `flags`
2972 // Task is tied iff (Flags & 1) == 1.
2973 // Task is untied iff (Flags & 1) == 0.
2974 // Task is final iff (Flags & 2) == 2.
2975 // Task is not final iff (Flags & 2) == 0.
2976 // Task is mergeable or merged-if0 iff (Flags & 4) == 4.
2977 // Task is neither mergeable nor merged-if0 iff (Flags & 4) == 0.
2978 // Task is detachable iff (Flags & 64) == 64.
2979 // Task is not detachable iff (Flags & 64) == 0.
2980 // Task is priority iff (Flags & 32) == 32.
2981 // Task is not priority iff (Flags & 32) == 0.
2982 // TODO: Handle the other flags.
2983 Value *Flags = Builder.getInt32(Tied);
2984 auto *ConstIfCondition = dyn_cast_or_null<ConstantInt>(IfCondition);
2985 bool UseMergedIf0Path = ConstIfCondition && ConstIfCondition->isZero();
2986 if (Final) {
2987 Value *FinalFlag =
2988 Builder.CreateSelect(Final, Builder.getInt32(2), Builder.getInt32(0));
2989 Flags = Builder.CreateOr(FinalFlag, Flags);
2990 }
2991
2992 if (Mergeable || UseMergedIf0Path)
2993 Flags = Builder.CreateOr(Builder.getInt32(4), Flags);
2994 if (EventHandle)
2995 Flags = Builder.CreateOr(Builder.getInt32(64), Flags);
2996 if (Priority)
2997 Flags = Builder.CreateOr(Builder.getInt32(32), Flags);
2998
2999 // Argument - `sizeof_kmp_task_t` (TaskSize)
3000 // Tasksize refers to the size in bytes of kmp_task_t data structure
3001 // including private vars accessed in task.
3002 // TODO: add kmp_task_t_with_privates (privates)
3003 Value *TaskSize = Builder.getInt64(
3004 divideCeil(M.getDataLayout().getTypeSizeInBits(Task), 8));
3005
3006 // Argument - `sizeof_shareds` (SharedsSize)
3007 // SharedsSize refers to the shareds array size in the kmp_task_t data
3008 // structure.
3009 Value *SharedsSize = Builder.getInt64(0);
3010 if (HasShareds) {
3011 AllocaInst *ArgStructAlloca =
3013 assert(ArgStructAlloca &&
3014 "Unable to find the alloca instruction corresponding to arguments "
3015 "for extracted function");
3016 std::optional<TypeSize> ArgAllocSize =
3017 ArgStructAlloca->getAllocationSize(M.getDataLayout());
3018 assert(ArgAllocSize &&
3019 "Unable to determine size of arguments for extracted function");
3020 SharedsSize = Builder.getInt64(ArgAllocSize->getFixedValue());
3021 }
3022 // Emit the @__kmpc_omp_task_alloc runtime call
3023 // The runtime call returns a pointer to an area where the task captured
3024 // variables must be copied before the task is run (TaskData)
3026 TaskAllocFn, {/*loc_ref=*/Ident, /*gtid=*/ThreadID, /*flags=*/Flags,
3027 /*sizeof_task=*/TaskSize, /*sizeof_shared=*/SharedsSize,
3028 /*task_func=*/&OutlinedFn});
3029
3030 if (Affinities.Count && Affinities.Info) {
3032 OMPRTL___kmpc_omp_reg_task_with_affinity);
3033
3034 createRuntimeFunctionCall(RegAffFn, {Ident, ThreadID, TaskData,
3035 Affinities.Count, Affinities.Info});
3036 }
3037
3038 // Emit detach clause initialization.
3039 // evt = (typeof(evt))__kmpc_task_allow_completion_event(loc, tid,
3040 // task_descriptor);
3041 if (EventHandle) {
3043 OMPRTL___kmpc_task_allow_completion_event);
3044 llvm::Value *EventVal =
3045 createRuntimeFunctionCall(TaskDetachFn, {Ident, ThreadID, TaskData});
3046 llvm::Value *EventHandleAddr =
3047 Builder.CreatePointerBitCastOrAddrSpaceCast(EventHandle,
3048 Builder.getPtrTy(0));
3049 EventVal = Builder.CreatePtrToInt(EventVal, Builder.getInt64Ty());
3050 Builder.CreateStore(EventVal, EventHandleAddr);
3051 }
3052 // Copy the arguments for outlined function
3053 if (HasShareds) {
3054 Value *Shareds = StaleCI->getArgOperand(1);
3055 Align Alignment = TaskData->getPointerAlignment(M.getDataLayout());
3056 Value *TaskShareds = Builder.CreateLoad(VoidPtr, TaskData);
3057 Builder.CreateMemCpy(TaskShareds, Alignment, Shareds, Alignment,
3058 SharedsSize);
3059 }
3060
3061 if (Priority) {
3062 //
3063 // The return type of "__kmpc_omp_task_alloc" is "kmp_task_t *",
3064 // we populate the priority information into the "kmp_task_t" here
3065 //
3066 // The struct "kmp_task_t" definition is available in kmp.h
3067 // kmp_task_t = { shareds, routine, part_id, data1, data2 }
3068 // data2 is used for priority
3069 //
3070 Type *Int32Ty = Builder.getInt32Ty();
3071 Constant *Zero = ConstantInt::get(Int32Ty, 0);
3072 // kmp_task_t* => { ptr }
3073 Type *TaskPtr = StructType::get(VoidPtr);
3074 Value *TaskGEP =
3075 Builder.CreateInBoundsGEP(TaskPtr, TaskData, {Zero, Zero});
3076 // kmp_task_t => { ptr, ptr, i32, ptr, ptr }
3077 Type *TaskStructType = StructType::get(
3078 VoidPtr, VoidPtr, Builder.getInt32Ty(), VoidPtr, VoidPtr);
3079 Value *PriorityData = Builder.CreateInBoundsGEP(
3080 TaskStructType, TaskGEP, {Zero, ConstantInt::get(Int32Ty, 4)});
3081 // kmp_cmplrdata_t => { ptr, ptr }
3082 Type *CmplrStructType = StructType::get(VoidPtr, VoidPtr);
3083 Value *CmplrData = Builder.CreateInBoundsGEP(CmplrStructType,
3084 PriorityData, {Zero, Zero});
3085 Builder.CreateStore(Priority, CmplrData);
3086 }
3087
3088 Value *DepArray = nullptr;
3089 Value *NumDeps = nullptr;
3090 if (Dependencies.DepArray) {
3091 DepArray = Dependencies.DepArray;
3092 NumDeps = Dependencies.NumDeps;
3093 } else if (!Dependencies.Deps.empty()) {
3094 DepArray = emitTaskDependencies(*this, Dependencies.Deps);
3095 NumDeps = Builder.getInt32(Dependencies.Deps.size());
3096 }
3097
3098 // In the presence of the `if` clause, the following IR is generated:
3099 // ...
3100 // %data = call @__kmpc_omp_task_alloc(...)
3101 // br i1 %if_condition, label %then, label %else
3102 // then:
3103 // call @__kmpc_omp_task(...)
3104 // br label %exit
3105 // else:
3106 // ;; Wait for resolution of dependencies, if any, before
3107 // ;; beginning the task
3108 // call @__kmpc_omp_wait_deps(...)
3109 // call @__kmpc_omp_task_begin_if0(...)
3110 // call @outlined_fn(...)
3111 // call @__kmpc_omp_task_complete_if0(...)
3112 // br label %exit
3113 // exit:
3114 // ...
3115 if (IfCondition && !UseMergedIf0Path) {
3116 // `SplitBlockAndInsertIfThenElse` requires the block to have a
3117 // terminator.
3118 splitBB(Builder, /*CreateBranch=*/true, "if.end");
3119 Instruction *IfTerminator =
3120 Builder.GetInsertPoint()->getParent()->getTerminator();
3121 Instruction *ThenTI = IfTerminator, *ElseTI = nullptr;
3122 Builder.SetInsertPoint(IfTerminator);
3123 SplitBlockAndInsertIfThenElse(IfCondition, IfTerminator, &ThenTI,
3124 &ElseTI);
3125 Builder.SetInsertPoint(ElseTI);
3126
3127 if (DepArray) {
3128 Function *TaskWaitFn =
3129 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_wait_deps);
3131 TaskWaitFn,
3132 {Ident, ThreadID, NumDeps, DepArray,
3133 ConstantInt::get(Builder.getInt32Ty(), 0),
3135 }
3136 Function *TaskBeginFn =
3137 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_begin_if0);
3138 Function *TaskCompleteFn =
3139 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_complete_if0);
3140 createRuntimeFunctionCall(TaskBeginFn, {Ident, ThreadID, TaskData});
3141 CallInst *CI = nullptr;
3142 if (HasShareds)
3143 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID, TaskData});
3144 else
3145 CI = createRuntimeFunctionCall(&OutlinedFn, {ThreadID});
3146 CI->setDebugLoc(StaleCI->getDebugLoc());
3147 createRuntimeFunctionCall(TaskCompleteFn, {Ident, ThreadID, TaskData});
3148 Builder.SetInsertPoint(ThenTI);
3149 }
3150
3151 if (DepArray) {
3152 Function *TaskFn =
3153 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task_with_deps);
3155 TaskFn,
3156 {Ident, ThreadID, TaskData, NumDeps, DepArray,
3157 ConstantInt::get(Builder.getInt32Ty(), 0),
3159
3160 } else {
3161 // Emit the @__kmpc_omp_task runtime call to spawn the task
3162 Function *TaskFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_omp_task);
3163 createRuntimeFunctionCall(TaskFn, {Ident, ThreadID, TaskData});
3164 }
3165
3166 StaleCI->eraseFromParent();
3167
3168 Builder.SetInsertPoint(TaskAllocaBB, TaskAllocaBB->begin());
3169 if (HasShareds) {
3170 LoadInst *Shareds = Builder.CreateLoad(VoidPtr, OutlinedFn.getArg(1));
3171 OutlinedFn.getArg(1)->replaceUsesWithIf(
3172 Shareds, [Shareds](Use &U) { return U.getUser() != Shareds; });
3173 }
3174
3175 // The insert point may refer to one of the instructions about to be
3176 // deleted. It is not needed anymore so clear it instead of leaving it
3177 // dangling.
3178 Builder.ClearInsertionPoint();
3179 for (Instruction *I : llvm::reverse(ToBeDeleted))
3180 I->eraseFromParent();
3181 };
3182
3183 addOutlineInfo(std::move(OI));
3184 Builder.SetInsertPoint(TaskExitBB, TaskExitBB->begin());
3185
3186 return Builder.saveIP();
3187}
3188
3190 const LocationDescription &Loc, InsertPointTy AllocaIP,
3191 ArrayRef<BasicBlock *> DeallocBlocks, BodyGenCallbackTy BodyGenCB) {
3192 if (!updateToLocation(Loc))
3193 return InsertPointTy();
3194
3195 uint32_t SrcLocStrSize;
3196 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
3197 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
3198 Value *ThreadID = getOrCreateThreadID(Ident);
3199
3200 // Emit the @__kmpc_taskgroup runtime call to start the taskgroup
3201 Function *TaskgroupFn =
3202 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_taskgroup);
3203 createRuntimeFunctionCall(TaskgroupFn, {Ident, ThreadID});
3204
3205 BasicBlock *TaskgroupExitBB = splitBB(Builder, true, "taskgroup.exit");
3206 if (Error Err = BodyGenCB(AllocaIP, Builder.saveIP(), DeallocBlocks))
3207 return Err;
3208
3209 Builder.SetInsertPoint(TaskgroupExitBB);
3210 // Emit the @__kmpc_end_taskgroup runtime call to end the taskgroup
3211 Function *EndTaskgroupFn =
3212 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_taskgroup);
3213 createRuntimeFunctionCall(EndTaskgroupFn, {Ident, ThreadID});
3214
3215 return Builder.saveIP();
3216}
3217
3219 const LocationDescription &Loc, InsertPointTy AllocaIP,
3221 FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) {
3222 assert(!isConflictIP(AllocaIP, Loc.IP) && "Dedicated IP allocas required");
3223
3224 if (!updateToLocation(Loc))
3225 return Loc.IP;
3226
3227 FinalizationStack.push_back({FiniCB, OMPD_sections, IsCancellable});
3228
3229 // Each section is emitted as a switch case
3230 // Each finalization callback is handled from clang.EmitOMPSectionDirective()
3231 // -> OMP.createSection() which generates the IR for each section
3232 // Iterate through all sections and emit a switch construct:
3233 // switch (IV) {
3234 // case 0:
3235 // <SectionStmt[0]>;
3236 // break;
3237 // ...
3238 // case <NumSection> - 1:
3239 // <SectionStmt[<NumSection> - 1]>;
3240 // break;
3241 // }
3242 // ...
3243 // section_loop.after:
3244 // <FiniCB>;
3245 auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, Value *IndVar) -> Error {
3246 Builder.restoreIP(CodeGenIP);
3248 splitBBWithSuffix(Builder, /*CreateBranch=*/false, ".sections.after");
3249 Function *CurFn = Continue->getParent();
3250 SwitchInst *SwitchStmt = Builder.CreateSwitch(IndVar, Continue);
3251
3252 unsigned CaseNumber = 0;
3253 for (auto SectionCB : SectionCBs) {
3255 M.getContext(), "omp_section_loop.body.case", CurFn, Continue);
3256 SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB);
3257 Builder.SetInsertPoint(CaseBB);
3258 UncondBrInst *CaseEndBr = Builder.CreateBr(Continue);
3259 if (Error Err =
3260 SectionCB(InsertPointTy(),
3261 {CaseEndBr->getParent(), CaseEndBr->getIterator()}, {}))
3262 return Err;
3263 CaseNumber++;
3264 }
3265 // remove the existing terminator from body BB since there can be no
3266 // terminators after switch/case
3267 return Error::success();
3268 };
3269 // Loop body ends here
3270 // LowerBound, UpperBound, and STride for createCanonicalLoop
3271 Type *I32Ty = Type::getInt32Ty(M.getContext());
3272 Value *LB = ConstantInt::get(I32Ty, 0);
3273 Value *UB = ConstantInt::get(I32Ty, SectionCBs.size());
3274 Value *ST = ConstantInt::get(I32Ty, 1);
3276 Loc, LoopBodyGenCB, LB, UB, ST, true, false, AllocaIP, "section_loop");
3277 if (!LoopInfo)
3278 return LoopInfo.takeError();
3279
3280 InsertPointOrErrorTy WsloopIP =
3281 applyStaticWorkshareLoop(Loc.DL, *LoopInfo, AllocaIP,
3282 WorksharingLoopType::ForStaticLoop, !IsNowait);
3283 if (!WsloopIP)
3284 return WsloopIP.takeError();
3285 InsertPointTy AfterIP = *WsloopIP;
3286
3287 BasicBlock *LoopFini = AfterIP.getBlock()->getSinglePredecessor();
3288 assert(LoopFini && "Bad structure of static workshare loop finalization");
3289
3290 // Apply the finalization callback in LoopAfterBB
3291 auto FiniInfo = FinalizationStack.pop_back_val();
3292 assert(FiniInfo.DK == OMPD_sections &&
3293 "Unexpected finalization stack state!");
3294 if (Error Err = FiniInfo.mergeFiniBB(Builder, LoopFini))
3295 return Err;
3296
3297 return AfterIP;
3298}
3299
3302 BodyGenCallbackTy BodyGenCB,
3303 FinalizeCallbackTy FiniCB) {
3304 if (!updateToLocation(Loc))
3305 return Loc.IP;
3306
3307 auto FiniCBWrapper = [&](InsertPointTy IP) {
3308 if (IP.getBlock()->end() != IP.getPoint())
3309 return FiniCB(IP);
3310 // This must be done otherwise any nested constructs using FinalizeOMPRegion
3311 // will fail because that function requires the Finalization Basic Block to
3312 // have a terminator, which is already removed by EmitOMPRegionBody.
3313 // IP is currently at cancelation block.
3314 // We need to backtrack to the condition block to fetch
3315 // the exit block and create a branch from cancelation
3316 // to exit block.
3318 Builder.restoreIP(IP);
3319 auto *CaseBB = Loc.IP.getBlock();
3320 auto *CondBB = CaseBB->getSinglePredecessor()->getSinglePredecessor();
3321 auto *ExitBB = CondBB->getTerminator()->getSuccessor(1);
3322 Instruction *I = Builder.CreateBr(ExitBB);
3323 IP = InsertPointTy(I->getParent(), I->getIterator());
3324 return FiniCB(IP);
3325 };
3326
3327 Directive OMPD = Directive::OMPD_sections;
3328 // Since we are using Finalization Callback here, HasFinalize
3329 // and IsCancellable have to be true
3330 return EmitOMPInlinedRegion(OMPD, nullptr, nullptr, BodyGenCB, FiniCBWrapper,
3331 /*Conditional*/ false, /*hasFinalize*/ true,
3332 /*IsCancellable*/ true);
3333}
3334
3340
3341Value *OpenMPIRBuilder::getGPUThreadID() {
3344 OMPRTL___kmpc_get_hardware_thread_id_in_block),
3345 {});
3346}
3347
3348Value *OpenMPIRBuilder::getGPUWarpSize() {
3350 getOrCreateRuntimeFunction(M, OMPRTL___kmpc_get_warp_size), {});
3351}
3352
3353Value *OpenMPIRBuilder::getNVPTXWarpID() {
3354 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3355 return Builder.CreateAShr(getGPUThreadID(), LaneIDBits, "nvptx_warp_id");
3356}
3357
3358Value *OpenMPIRBuilder::getNVPTXLaneID() {
3359 unsigned LaneIDBits = Log2_32(Config.getGridValue().GV_Warp_Size);
3360 assert(LaneIDBits < 32 && "Invalid LaneIDBits size in NVPTX device.");
3361 unsigned LaneIDMask = ~0u >> (32u - LaneIDBits);
3362 return Builder.CreateAnd(getGPUThreadID(), Builder.getInt32(LaneIDMask),
3363 "nvptx_lane_id");
3364}
3365
3366Value *OpenMPIRBuilder::castValueToType(InsertPointTy AllocaIP, Value *From,
3367 Type *ToType) {
3368 Type *FromType = From->getType();
3369 uint64_t FromSize = M.getDataLayout().getTypeStoreSize(FromType);
3370 uint64_t ToSize = M.getDataLayout().getTypeStoreSize(ToType);
3371 assert(FromSize > 0 && "From size must be greater than zero");
3372 assert(ToSize > 0 && "To size must be greater than zero");
3373 if (FromType == ToType)
3374 return From;
3375 if (FromSize == ToSize)
3376 return Builder.CreateBitCast(From, ToType);
3377 if (ToType->isIntegerTy() && FromType->isIntegerTy())
3378 return Builder.CreateIntCast(From, ToType, /*isSigned*/ true);
3379 InsertPointTy SaveIP = Builder.saveIP();
3380 Builder.restoreIP(AllocaIP);
3381 Value *CastItem = Builder.CreateAlloca(ToType);
3382 Builder.restoreIP(SaveIP);
3383
3384 Value *ValCastItem = Builder.CreatePointerBitCastOrAddrSpaceCast(
3385 CastItem, Builder.getPtrTy(0));
3386 Builder.CreateStore(From, ValCastItem);
3387 return Builder.CreateLoad(ToType, CastItem);
3388}
3389
3390Value *OpenMPIRBuilder::createRuntimeShuffleFunction(InsertPointTy AllocaIP,
3391 Value *Element,
3392 Type *ElementType,
3393 Value *Offset) {
3394 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElementType);
3395 assert(Size <= 8 && "Unsupported bitwidth in shuffle instruction");
3396
3397 // Cast all types to 32- or 64-bit values before calling shuffle routines.
3398 Type *CastTy = Builder.getIntNTy(Size <= 4 ? 32 : 64);
3399 Value *ElemCast = castValueToType(AllocaIP, Element, CastTy);
3400 Value *WarpSize =
3401 Builder.CreateIntCast(getGPUWarpSize(), Builder.getInt16Ty(), true);
3403 Size <= 4 ? RuntimeFunction::OMPRTL___kmpc_shuffle_int32
3404 : RuntimeFunction::OMPRTL___kmpc_shuffle_int64);
3405 Value *WarpSizeCast =
3406 Builder.CreateIntCast(WarpSize, Builder.getInt16Ty(), /*isSigned=*/true);
3407 Value *ShuffleCall =
3408 createRuntimeFunctionCall(ShuffleFunc, {ElemCast, Offset, WarpSizeCast});
3409 // The shuffle runtime functions return a 32- or 64-bit value. Cast it back
3410 // down to the requested element type, otherwise storing the result would
3411 // write past the end of an element narrower than the shuffle width.
3412 return castValueToType(AllocaIP, ShuffleCall, ElementType);
3413}
3414
3415void OpenMPIRBuilder::shuffleAndStore(InsertPointTy AllocaIP, Value *SrcAddr,
3416 Value *DstAddr, Type *ElemType,
3417 Value *Offset, Type *ReductionArrayTy,
3418 bool IsByRefElem) {
3419 uint64_t Size = M.getDataLayout().getTypeStoreSize(ElemType);
3420 // Create the loop over the big sized data.
3421 // ptr = (void*)Elem;
3422 // ptrEnd = (void*) Elem + 1;
3423 // Step = 8;
3424 // while (ptr + Step < ptrEnd)
3425 // shuffle((int64_t)*ptr);
3426 // Step = 4;
3427 // while (ptr + Step < ptrEnd)
3428 // shuffle((int32_t)*ptr);
3429 // ...
3430 Type *IndexTy = Builder.getIndexTy(
3431 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3432 Value *ElemPtr = DstAddr;
3433 Value *Ptr = SrcAddr;
3434 for (unsigned IntSize = 8; IntSize >= 1; IntSize /= 2) {
3435 if (Size < IntSize)
3436 continue;
3437 Type *IntType = Builder.getIntNTy(IntSize * 8);
3438 Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3439 Ptr, Builder.getPtrTy(0), Ptr->getName() + ".ascast");
3440 Value *SrcAddrGEP =
3441 Builder.CreateGEP(ElemType, SrcAddr, {ConstantInt::get(IndexTy, 1)});
3442 ElemPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3443 ElemPtr, Builder.getPtrTy(0), ElemPtr->getName() + ".ascast");
3444
3445 Function *CurFunc = Builder.GetInsertBlock()->getParent();
3446 if ((Size / IntSize) > 1) {
3447 Value *PtrEnd = Builder.CreatePointerBitCastOrAddrSpaceCast(
3448 SrcAddrGEP, Builder.getPtrTy());
3449 BasicBlock *PreCondBB =
3450 BasicBlock::Create(M.getContext(), ".shuffle.pre_cond");
3451 BasicBlock *ThenBB = BasicBlock::Create(M.getContext(), ".shuffle.then");
3452 BasicBlock *ExitBB = BasicBlock::Create(M.getContext(), ".shuffle.exit");
3453 BasicBlock *CurrentBB = Builder.GetInsertBlock();
3454 emitBlock(PreCondBB, CurFunc);
3455 PHINode *PhiSrc =
3456 Builder.CreatePHI(Ptr->getType(), /*NumReservedValues=*/2);
3457 PhiSrc->addIncoming(Ptr, CurrentBB);
3458 PHINode *PhiDest =
3459 Builder.CreatePHI(ElemPtr->getType(), /*NumReservedValues=*/2);
3460 PhiDest->addIncoming(ElemPtr, CurrentBB);
3461 Ptr = PhiSrc;
3462 ElemPtr = PhiDest;
3463 Value *PtrDiff = Builder.CreatePtrDiff(
3464 Builder.getInt8Ty(), PtrEnd,
3465 Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Builder.getPtrTy()));
3466 Builder.CreateCondBr(
3467 Builder.CreateICmpSGT(PtrDiff, Builder.getInt64(IntSize - 1)), ThenBB,
3468 ExitBB);
3469 emitBlock(ThenBB, CurFunc);
3470 Value *Res = createRuntimeShuffleFunction(
3471 AllocaIP,
3472 Builder.CreateAlignedLoad(
3473 IntType, Ptr, M.getDataLayout().getPrefTypeAlign(ElemType)),
3474 IntType, Offset);
3475 Builder.CreateAlignedStore(Res, ElemPtr,
3476 M.getDataLayout().getPrefTypeAlign(ElemType));
3477 Value *LocalPtr =
3478 Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3479 Value *LocalElemPtr =
3480 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3481 PhiSrc->addIncoming(LocalPtr, ThenBB);
3482 PhiDest->addIncoming(LocalElemPtr, ThenBB);
3483 emitBranch(PreCondBB);
3484 emitBlock(ExitBB, CurFunc);
3485 } else {
3486 // The shuffled value comes back as the chunk's integer type, so the
3487 // store covers exactly this chunk regardless of what ElemType is.
3488 Value *Res = createRuntimeShuffleFunction(
3489 AllocaIP, Builder.CreateLoad(IntType, Ptr), IntType, Offset);
3490 Builder.CreateStore(Res, ElemPtr);
3491 Ptr = Builder.CreateGEP(IntType, Ptr, {ConstantInt::get(IndexTy, 1)});
3492 ElemPtr =
3493 Builder.CreateGEP(IntType, ElemPtr, {ConstantInt::get(IndexTy, 1)});
3494 }
3495 Size = Size % IntSize;
3496 }
3497}
3498
3499Error OpenMPIRBuilder::emitReductionListCopy(
3500 InsertPointTy AllocaIP, CopyAction Action, Type *ReductionArrayTy,
3501 ArrayRef<ReductionInfo> ReductionInfos, Value *SrcBase, Value *DestBase,
3502 ArrayRef<bool> IsByRef, CopyOptionsTy CopyOptions) {
3503 Type *IndexTy = Builder.getIndexTy(
3504 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3505 Value *RemoteLaneOffset = CopyOptions.RemoteLaneOffset;
3506
3507 // Iterates, element-by-element, through the source Reduce list and
3508 // make a copy.
3509 for (auto En : enumerate(ReductionInfos)) {
3510 const ReductionInfo &RI = En.value();
3511 Value *SrcElementAddr = nullptr;
3512 AllocaInst *DestAlloca = nullptr;
3513 Value *DestElementAddr = nullptr;
3514 Value *DestElementPtrAddr = nullptr;
3515 // Should we shuffle in an element from a remote lane?
3516 bool ShuffleInElement = false;
3517 // Set to true to update the pointer in the dest Reduce list to a
3518 // newly created element.
3519 bool UpdateDestListPtr = false;
3520
3521 // Step 1.1: Get the address for the src element in the Reduce list.
3522 Value *SrcElementPtrAddr = Builder.CreateInBoundsGEP(
3523 ReductionArrayTy, SrcBase,
3524 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3525 SrcElementAddr = Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrAddr);
3526
3527 // Step 1.2: Create a temporary to store the element in the destination
3528 // Reduce list.
3529 DestElementPtrAddr = Builder.CreateInBoundsGEP(
3530 ReductionArrayTy, DestBase,
3531 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
3532 bool IsByRefElem = (!IsByRef.empty() && IsByRef[En.index()]);
3533 switch (Action) {
3535 InsertPointTy CurIP = Builder.saveIP();
3536 Builder.restoreIP(AllocaIP);
3537
3538 Type *DestAllocaType =
3539 IsByRefElem ? RI.ByRefAllocatedType : RI.ElementType;
3540 DestAlloca = Builder.CreateAlloca(DestAllocaType, nullptr,
3541 ".omp.reduction.element");
3542 DestAlloca->setAlignment(
3543 M.getDataLayout().getPrefTypeAlign(DestAllocaType));
3544 DestElementAddr = DestAlloca;
3545 DestElementAddr =
3546 Builder.CreateAddrSpaceCast(DestElementAddr, Builder.getPtrTy(),
3547 DestElementAddr->getName() + ".ascast");
3548 Builder.restoreIP(CurIP);
3549 ShuffleInElement = true;
3550 UpdateDestListPtr = true;
3551 break;
3552 }
3554 DestElementAddr =
3555 Builder.CreateLoad(Builder.getPtrTy(), DestElementPtrAddr);
3556 break;
3557 }
3558 }
3559
3560 // Now that all active lanes have read the element in the
3561 // Reduce list, shuffle over the value from the remote lane.
3562 if (ShuffleInElement) {
3563 Type *ShuffleType = RI.ElementType;
3564 Value *ShuffleSrcAddr = SrcElementAddr;
3565 Value *ShuffleDestAddr = DestElementAddr;
3566 AllocaInst *LocalStorage = nullptr;
3567
3568 if (IsByRefElem) {
3569 assert(RI.ByRefElementType && "Expected by-ref element type to be set");
3570 assert(RI.ByRefAllocatedType &&
3571 "Expected by-ref allocated type to be set");
3572 // For by-ref reductions, we need to copy from the remote lane the
3573 // actual value of the partial reduction computed by that remote lane;
3574 // rather than, for example, a pointer to that data or, even worse, a
3575 // pointer to the descriptor of the by-ref reduction element.
3576 ShuffleType = RI.ByRefElementType;
3577
3578 if (RI.DataPtrPtrGen) {
3579 // Descriptor-based by-ref: extract data pointer from descriptor.
3580 InsertPointOrErrorTy GenResult = RI.DataPtrPtrGen(
3581 Builder.saveIP(), ShuffleSrcAddr, ShuffleSrcAddr);
3582
3583 if (!GenResult)
3584 return GenResult.takeError();
3585
3586 ShuffleSrcAddr =
3587 Builder.CreateLoad(Builder.getPtrTy(), ShuffleSrcAddr);
3588
3589 {
3590 InsertPointTy OldIP = Builder.saveIP();
3591 Builder.restoreIP(AllocaIP);
3592
3593 LocalStorage = Builder.CreateAlloca(ShuffleType);
3594 Builder.restoreIP(OldIP);
3595 ShuffleDestAddr = LocalStorage;
3596 }
3597 } else {
3598 // Non-descriptor by-ref: the pointer already references data
3599 // directly. Shuffle into the destination alloca.
3600 ShuffleDestAddr = DestElementAddr;
3601 }
3602 }
3603
3604 shuffleAndStore(AllocaIP, ShuffleSrcAddr, ShuffleDestAddr, ShuffleType,
3605 RemoteLaneOffset, ReductionArrayTy, IsByRefElem);
3606
3607 if (IsByRefElem && RI.DataPtrPtrGen) {
3608 // Copy descriptor from source and update base_ptr to shuffled data
3609 Value *DestDescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3610 DestAlloca, Builder.getPtrTy(), ".ascast");
3611
3612 InsertPointOrErrorTy GenResult = generateReductionDescriptor(
3613 DestDescriptorAddr, LocalStorage, SrcElementAddr,
3614 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
3615
3616 if (!GenResult)
3617 return GenResult.takeError();
3618 }
3619 } else {
3620 switch (RI.EvaluationKind) {
3621 case EvalKind::Scalar: {
3622 Value *Elem = Builder.CreateLoad(RI.ElementType, SrcElementAddr);
3623 // Store the source element value to the dest element address.
3624 Builder.CreateStore(Elem, DestElementAddr);
3625 break;
3626 }
3627 case EvalKind::Complex: {
3628 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
3629 RI.ElementType, SrcElementAddr, 0, 0, ".realp");
3630 Value *SrcReal = Builder.CreateLoad(
3631 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
3632 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
3633 RI.ElementType, SrcElementAddr, 0, 1, ".imagp");
3634 Value *SrcImg = Builder.CreateLoad(
3635 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
3636
3637 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
3638 RI.ElementType, DestElementAddr, 0, 0, ".realp");
3639 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
3640 RI.ElementType, DestElementAddr, 0, 1, ".imagp");
3641 Builder.CreateStore(SrcReal, DestRealPtr);
3642 Builder.CreateStore(SrcImg, DestImgPtr);
3643 break;
3644 }
3645 case EvalKind::Aggregate: {
3646 Value *SizeVal = Builder.getInt64(
3647 M.getDataLayout().getTypeStoreSize(RI.ElementType));
3648 Builder.CreateMemCpy(
3649 DestElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3650 SrcElementAddr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
3651 SizeVal, false);
3652 break;
3653 }
3654 };
3655 }
3656
3657 // Step 3.1: Modify reference in dest Reduce list as needed.
3658 // Modifying the reference in Reduce list to point to the newly
3659 // created element. The element is live in the current function
3660 // scope and that of functions it invokes (i.e., reduce_function).
3661 // RemoteReduceData[i] = (void*)&RemoteElem
3662 if (UpdateDestListPtr) {
3663 Value *CastDestAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
3664 DestElementAddr, Builder.getPtrTy(),
3665 DestElementAddr->getName() + ".ascast");
3666 Builder.CreateStore(CastDestAddr, DestElementPtrAddr);
3667 }
3668 }
3669
3670 return Error::success();
3671}
3672
3673Expected<Function *> OpenMPIRBuilder::emitInterWarpCopyFunction(
3674 const LocationDescription &Loc, ArrayRef<ReductionInfo> ReductionInfos,
3675 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3676 IRBuilder<>::InsertPointGuard IPG(Builder);
3677 LLVMContext &Ctx = M.getContext();
3678 FunctionType *FuncTy = FunctionType::get(
3679 Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getInt32Ty()},
3680 /* IsVarArg */ false);
3681 Function *WcFunc =
3683 "_omp_reduction_inter_warp_copy_func", &M);
3684 WcFunc->setCallingConv(Config.getRuntimeCC());
3685 WcFunc->setAttributes(FuncAttrs);
3686 WcFunc->addParamAttr(0, Attribute::NoUndef);
3687 WcFunc->addParamAttr(1, Attribute::NoUndef);
3688 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", WcFunc);
3689 Builder.SetInsertPoint(EntryBB);
3690 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3691
3692 // ReduceList: thread local Reduce list.
3693 // At the stage of the computation when this function is called, partially
3694 // aggregated values reside in the first lane of every active warp.
3695 Argument *ReduceListArg = WcFunc->getArg(0);
3696 // NumWarps: number of warps active in the parallel region. This could
3697 // be smaller than 32 (max warps in a CTA) for partial block reduction.
3698 Argument *NumWarpsArg = WcFunc->getArg(1);
3699
3700 // This array is used as a medium to transfer, one reduce element at a time,
3701 // the data from the first lane of every warp to lanes in the first warp
3702 // in order to perform the final step of a reduction in a parallel region
3703 // (reduction across warps). The array is placed in NVPTX __shared__ memory
3704 // for reduced latency, as well as to have a distinct copy for concurrently
3705 // executing target regions. The array is declared with common linkage so
3706 // as to be shared across compilation units.
3707 StringRef TransferMediumName =
3708 "__openmp_nvptx_data_transfer_temporary_storage";
3709 GlobalVariable *TransferMedium = M.getGlobalVariable(TransferMediumName);
3710 unsigned WarpSize = Config.getGridValue().GV_Warp_Size;
3711 ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), WarpSize);
3712 if (!TransferMedium) {
3713 TransferMedium = new GlobalVariable(
3714 M, ArrayTy, /*isConstant=*/false, GlobalVariable::WeakAnyLinkage,
3715 UndefValue::get(ArrayTy), TransferMediumName,
3716 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal,
3717 /*AddressSpace=*/3);
3718 }
3719
3720 // Get the CUDA thread id of the current OpenMP thread on the GPU.
3721 Value *GPUThreadID = getGPUThreadID();
3722 // nvptx_lane_id = nvptx_id % warpsize
3723 Value *LaneID = getNVPTXLaneID();
3724 // nvptx_warp_id = nvptx_id / warpsize
3725 Value *WarpID = getNVPTXWarpID();
3726
3727 InsertPointTy AllocaIP =
3728 InsertPointTy(Builder.GetInsertBlock(),
3729 Builder.GetInsertBlock()->getFirstInsertionPt());
3730 Type *Arg0Type = ReduceListArg->getType();
3731 Type *Arg1Type = NumWarpsArg->getType();
3732 Builder.restoreIP(AllocaIP);
3733 AllocaInst *ReduceListAlloca = Builder.CreateAlloca(
3734 Arg0Type, nullptr, ReduceListArg->getName() + ".addr");
3735 AllocaInst *NumWarpsAlloca =
3736 Builder.CreateAlloca(Arg1Type, nullptr, NumWarpsArg->getName() + ".addr");
3737 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3738 ReduceListAlloca, Arg0Type, ReduceListAlloca->getName() + ".ascast");
3739 Value *NumWarpsAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3740 NumWarpsAlloca, Builder.getPtrTy(0),
3741 NumWarpsAlloca->getName() + ".ascast");
3742 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
3743 Builder.CreateStore(NumWarpsArg, NumWarpsAddrCast);
3744 AllocaIP = getInsertPointAfterInstr(NumWarpsAlloca);
3745 InsertPointTy CodeGenIP =
3746 getInsertPointAfterInstr(&Builder.GetInsertBlock()->back());
3747 Builder.restoreIP(CodeGenIP);
3748
3749 Value *ReduceList =
3750 Builder.CreateLoad(Builder.getPtrTy(), ReduceListAddrCast);
3751
3752 for (auto En : enumerate(ReductionInfos)) {
3753 //
3754 // Warp master copies reduce element to transfer medium in __shared__
3755 // memory.
3756 //
3757 const ReductionInfo &RI = En.value();
3758 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
3759 unsigned RealTySize = M.getDataLayout().getTypeAllocSize(
3760 IsByRefElem ? RI.ByRefElementType : RI.ElementType);
3761 for (unsigned TySize = 4; TySize > 0 && RealTySize > 0; TySize /= 2) {
3762 Type *CType = Builder.getIntNTy(TySize * 8);
3763
3764 unsigned NumIters = RealTySize / TySize;
3765 if (NumIters == 0)
3766 continue;
3767 Value *Cnt = nullptr;
3768 Value *CntAddr = nullptr;
3769 BasicBlock *PrecondBB = nullptr;
3770 BasicBlock *ExitBB = nullptr;
3771 if (NumIters > 1) {
3772 CodeGenIP = Builder.saveIP();
3773 Builder.restoreIP(AllocaIP);
3774 CntAddr =
3775 Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, ".cnt.addr");
3776
3777 CntAddr = Builder.CreateAddrSpaceCast(CntAddr, Builder.getPtrTy(),
3778 CntAddr->getName() + ".ascast");
3779 Builder.restoreIP(CodeGenIP);
3780 Builder.CreateStore(Constant::getNullValue(Builder.getInt32Ty()),
3781 CntAddr,
3782 /*Volatile=*/false);
3783 PrecondBB = BasicBlock::Create(Ctx, "precond");
3784 ExitBB = BasicBlock::Create(Ctx, "exit");
3785 BasicBlock *BodyBB = BasicBlock::Create(Ctx, "body");
3786 emitBlock(PrecondBB, Builder.GetInsertBlock()->getParent());
3787 Cnt = Builder.CreateLoad(Builder.getInt32Ty(), CntAddr,
3788 /*Volatile=*/false);
3789 Value *Cmp = Builder.CreateICmpULT(
3790 Cnt, ConstantInt::get(Builder.getInt32Ty(), NumIters));
3791 Builder.CreateCondBr(Cmp, BodyBB, ExitBB);
3792 emitBlock(BodyBB, Builder.GetInsertBlock()->getParent());
3793 }
3794
3795 // kmpc_barrier.
3796 InsertPointOrErrorTy BarrierIP1 =
3798 omp::Directive::OMPD_unknown,
3799 /* ForceSimpleCall */ false,
3800 /* CheckCancelFlag */ true);
3801 if (!BarrierIP1)
3802 return BarrierIP1.takeError();
3803 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
3804 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
3805 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
3806
3807 // if (lane_id == 0)
3808 Value *IsWarpMaster = Builder.CreateIsNull(LaneID, "warp_master");
3809 Builder.CreateCondBr(IsWarpMaster, ThenBB, ElseBB);
3810 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
3811
3812 // Reduce element = LocalReduceList[i]
3813 auto *RedListArrayTy =
3814 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3815 Type *IndexTy = Builder.getIndexTy(
3816 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
3817 Value *ElemPtrPtr =
3818 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3819 {ConstantInt::get(IndexTy, 0),
3820 ConstantInt::get(IndexTy, En.index())});
3821 // elemptr = ((CopyType*)(elemptrptr)) + I
3822 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
3823
3824 if (IsByRefElem && RI.DataPtrPtrGen) {
3825 InsertPointOrErrorTy GenRes =
3826 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
3827
3828 if (!GenRes)
3829 return GenRes.takeError();
3830
3831 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
3832 }
3833
3834 if (NumIters > 1)
3835 ElemPtr = Builder.CreateGEP(Builder.getInt32Ty(), ElemPtr, Cnt);
3836
3837 // Get pointer to location in transfer medium.
3838 // MediumPtr = &medium[warp_id]
3839 Value *MediumPtr = Builder.CreateInBoundsGEP(
3840 ArrayTy, TransferMedium, {Builder.getInt64(0), WarpID});
3841 // elem = *elemptr
3842 //*MediumPtr = elem
3843 Value *Elem = Builder.CreateLoad(CType, ElemPtr);
3844 // Store the source element value to the dest element address.
3845 Builder.CreateStore(Elem, MediumPtr,
3846 /*IsVolatile*/ true);
3847 Builder.CreateBr(MergeBB);
3848
3849 // else
3850 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
3851 Builder.CreateBr(MergeBB);
3852
3853 // endif
3854 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
3855 InsertPointOrErrorTy BarrierIP2 =
3857 omp::Directive::OMPD_unknown,
3858 /* ForceSimpleCall */ false,
3859 /* CheckCancelFlag */ true);
3860 if (!BarrierIP2)
3861 return BarrierIP2.takeError();
3862
3863 // Warp 0 copies reduce element from transfer medium
3864 BasicBlock *W0ThenBB = BasicBlock::Create(Ctx, "then");
3865 BasicBlock *W0ElseBB = BasicBlock::Create(Ctx, "else");
3866 BasicBlock *W0MergeBB = BasicBlock::Create(Ctx, "ifcont");
3867
3868 Value *NumWarpsVal =
3869 Builder.CreateLoad(Builder.getInt32Ty(), NumWarpsAddrCast);
3870 // Up to 32 threads in warp 0 are active.
3871 Value *IsActiveThread =
3872 Builder.CreateICmpULT(GPUThreadID, NumWarpsVal, "is_active_thread");
3873 Builder.CreateCondBr(IsActiveThread, W0ThenBB, W0ElseBB);
3874
3875 emitBlock(W0ThenBB, Builder.GetInsertBlock()->getParent());
3876
3877 // SecMediumPtr = &medium[tid]
3878 // SrcMediumVal = *SrcMediumPtr
3879 Value *SrcMediumPtrVal = Builder.CreateInBoundsGEP(
3880 ArrayTy, TransferMedium, {Builder.getInt64(0), GPUThreadID});
3881 // TargetElemPtr = (CopyType*)(SrcDataAddr[i]) + I
3882 Value *TargetElemPtrPtr =
3883 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
3884 {ConstantInt::get(IndexTy, 0),
3885 ConstantInt::get(IndexTy, En.index())});
3886 Value *TargetElemPtrVal =
3887 Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtrPtr);
3888 Value *TargetElemPtr = TargetElemPtrVal;
3889
3890 if (IsByRefElem && RI.DataPtrPtrGen) {
3891 InsertPointOrErrorTy GenRes =
3892 RI.DataPtrPtrGen(Builder.saveIP(), TargetElemPtr, TargetElemPtr);
3893
3894 if (!GenRes)
3895 return GenRes.takeError();
3896
3897 TargetElemPtr = Builder.CreateLoad(Builder.getPtrTy(), TargetElemPtr);
3898 }
3899
3900 if (NumIters > 1)
3901 TargetElemPtr =
3902 Builder.CreateGEP(Builder.getInt32Ty(), TargetElemPtr, Cnt);
3903
3904 // *TargetElemPtr = SrcMediumVal;
3905 Value *SrcMediumValue =
3906 Builder.CreateLoad(CType, SrcMediumPtrVal, /*IsVolatile*/ true);
3907 Builder.CreateStore(SrcMediumValue, TargetElemPtr);
3908 Builder.CreateBr(W0MergeBB);
3909
3910 emitBlock(W0ElseBB, Builder.GetInsertBlock()->getParent());
3911 Builder.CreateBr(W0MergeBB);
3912
3913 emitBlock(W0MergeBB, Builder.GetInsertBlock()->getParent());
3914
3915 if (NumIters > 1) {
3916 Cnt = Builder.CreateNSWAdd(
3917 Cnt, ConstantInt::get(Builder.getInt32Ty(), /*V=*/1));
3918 Builder.CreateStore(Cnt, CntAddr, /*Volatile=*/false);
3919
3920 auto *CurFn = Builder.GetInsertBlock()->getParent();
3921 emitBranch(PrecondBB);
3922 emitBlock(ExitBB, CurFn);
3923 }
3924 RealTySize %= TySize;
3925 }
3926 }
3927
3928 Builder.CreateRetVoid();
3929
3930 return WcFunc;
3931}
3932
3933Expected<Function *> OpenMPIRBuilder::emitShuffleAndReduceFunction(
3934 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
3935 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
3936 LLVMContext &Ctx = M.getContext();
3937 IRBuilder<>::InsertPointGuard IPG(Builder);
3938 FunctionType *FuncTy =
3939 FunctionType::get(Builder.getVoidTy(),
3940 {Builder.getPtrTy(), Builder.getInt16Ty(),
3941 Builder.getInt16Ty(), Builder.getInt16Ty()},
3942 /* IsVarArg */ false);
3943 Function *SarFunc =
3945 "_omp_reduction_shuffle_and_reduce_func", &M);
3946 SarFunc->setCallingConv(Config.getRuntimeCC());
3947 SarFunc->setAttributes(FuncAttrs);
3948 SarFunc->addParamAttr(0, Attribute::NoUndef);
3949 SarFunc->addParamAttr(1, Attribute::NoUndef);
3950 SarFunc->addParamAttr(2, Attribute::NoUndef);
3951 SarFunc->addParamAttr(3, Attribute::NoUndef);
3952 SarFunc->addParamAttr(1, Attribute::SExt);
3953 SarFunc->addParamAttr(2, Attribute::SExt);
3954 SarFunc->addParamAttr(3, Attribute::SExt);
3955 BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "entry", SarFunc);
3956 Builder.SetInsertPoint(EntryBB);
3957 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
3958
3959 // Thread local Reduce list used to host the values of data to be reduced.
3960 Argument *ReduceListArg = SarFunc->getArg(0);
3961 // Current lane id; could be logical.
3962 Argument *LaneIDArg = SarFunc->getArg(1);
3963 // Offset of the remote source lane relative to the current lane.
3964 Argument *RemoteLaneOffsetArg = SarFunc->getArg(2);
3965 // Algorithm version. This is expected to be known at compile time.
3966 Argument *AlgoVerArg = SarFunc->getArg(3);
3967
3968 Type *ReduceListArgType = ReduceListArg->getType();
3969 Type *LaneIDArgType = LaneIDArg->getType();
3970 Type *LaneIDArgPtrType = Builder.getPtrTy(0);
3971 Value *ReduceListAlloca = Builder.CreateAlloca(
3972 ReduceListArgType, nullptr, ReduceListArg->getName() + ".addr");
3973 Value *LaneIdAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3974 LaneIDArg->getName() + ".addr");
3975 Value *RemoteLaneOffsetAlloca = Builder.CreateAlloca(
3976 LaneIDArgType, nullptr, RemoteLaneOffsetArg->getName() + ".addr");
3977 Value *AlgoVerAlloca = Builder.CreateAlloca(LaneIDArgType, nullptr,
3978 AlgoVerArg->getName() + ".addr");
3979 ArrayType *RedListArrayTy =
3980 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
3981
3982 // Create a local thread-private variable to host the Reduce list
3983 // from a remote lane.
3984 Instruction *RemoteReductionListAlloca = Builder.CreateAlloca(
3985 RedListArrayTy, nullptr, ".omp.reduction.remote_reduce_list");
3986
3987 Value *ReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3988 ReduceListAlloca, ReduceListArgType,
3989 ReduceListAlloca->getName() + ".ascast");
3990 Value *LaneIdAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3991 LaneIdAlloca, LaneIDArgPtrType, LaneIdAlloca->getName() + ".ascast");
3992 Value *RemoteLaneOffsetAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3993 RemoteLaneOffsetAlloca, LaneIDArgPtrType,
3994 RemoteLaneOffsetAlloca->getName() + ".ascast");
3995 Value *AlgoVerAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3996 AlgoVerAlloca, LaneIDArgPtrType, AlgoVerAlloca->getName() + ".ascast");
3997 Value *RemoteListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
3998 RemoteReductionListAlloca, Builder.getPtrTy(),
3999 RemoteReductionListAlloca->getName() + ".ascast");
4000
4001 Builder.CreateStore(ReduceListArg, ReduceListAddrCast);
4002 Builder.CreateStore(LaneIDArg, LaneIdAddrCast);
4003 Builder.CreateStore(RemoteLaneOffsetArg, RemoteLaneOffsetAddrCast);
4004 Builder.CreateStore(AlgoVerArg, AlgoVerAddrCast);
4005
4006 Value *ReduceList = Builder.CreateLoad(ReduceListArgType, ReduceListAddrCast);
4007 Value *LaneId = Builder.CreateLoad(LaneIDArgType, LaneIdAddrCast);
4008 Value *RemoteLaneOffset =
4009 Builder.CreateLoad(LaneIDArgType, RemoteLaneOffsetAddrCast);
4010 Value *AlgoVer = Builder.CreateLoad(LaneIDArgType, AlgoVerAddrCast);
4011
4012 InsertPointTy AllocaIP = getInsertPointAfterInstr(RemoteReductionListAlloca);
4013
4014 // This loop iterates through the list of reduce elements and copies,
4015 // element by element, from a remote lane in the warp to RemoteReduceList,
4016 // hosted on the thread's stack.
4017 Error EmitRedLsCpRes = emitReductionListCopy(
4018 AllocaIP, CopyAction::RemoteLaneToThread, RedListArrayTy, ReductionInfos,
4019 ReduceList, RemoteListAddrCast, IsByRef,
4020 {RemoteLaneOffset, nullptr, nullptr});
4021
4022 if (EmitRedLsCpRes)
4023 return EmitRedLsCpRes;
4024
4025 // The actions to be performed on the Remote Reduce list is dependent
4026 // on the algorithm version.
4027 //
4028 // if (AlgoVer==0) || (AlgoVer==1 && (LaneId < Offset)) || (AlgoVer==2 &&
4029 // LaneId % 2 == 0 && Offset > 0):
4030 // do the reduction value aggregation
4031 //
4032 // The thread local variable Reduce list is mutated in place to host the
4033 // reduced data, which is the aggregated value produced from local and
4034 // remote lanes.
4035 //
4036 // Note that AlgoVer is expected to be a constant integer known at compile
4037 // time.
4038 // When AlgoVer==0, the first conjunction evaluates to true, making
4039 // the entire predicate true during compile time.
4040 // When AlgoVer==1, the second conjunction has only the second part to be
4041 // evaluated during runtime. Other conjunctions evaluates to false
4042 // during compile time.
4043 // When AlgoVer==2, the third conjunction has only the second part to be
4044 // evaluated during runtime. Other conjunctions evaluates to false
4045 // during compile time.
4046 Value *CondAlgo0 = Builder.CreateIsNull(AlgoVer);
4047 Value *Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4048 Value *LaneComp = Builder.CreateICmpULT(LaneId, RemoteLaneOffset);
4049 Value *CondAlgo1 = Builder.CreateAnd(Algo1, LaneComp);
4050 Value *Algo2 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(2));
4051 Value *LaneIdAnd1 = Builder.CreateAnd(LaneId, Builder.getInt16(1));
4052 Value *LaneIdComp = Builder.CreateIsNull(LaneIdAnd1);
4053 Value *Algo2AndLaneIdComp = Builder.CreateAnd(Algo2, LaneIdComp);
4054 Value *RemoteOffsetComp =
4055 Builder.CreateICmpSGT(RemoteLaneOffset, Builder.getInt16(0));
4056 Value *CondAlgo2 = Builder.CreateAnd(Algo2AndLaneIdComp, RemoteOffsetComp);
4057 Value *CA0OrCA1 = Builder.CreateOr(CondAlgo0, CondAlgo1);
4058 Value *CondReduce = Builder.CreateOr(CA0OrCA1, CondAlgo2);
4059
4060 BasicBlock *ThenBB = BasicBlock::Create(Ctx, "then");
4061 BasicBlock *ElseBB = BasicBlock::Create(Ctx, "else");
4062 BasicBlock *MergeBB = BasicBlock::Create(Ctx, "ifcont");
4063
4064 Builder.CreateCondBr(CondReduce, ThenBB, ElseBB);
4065 emitBlock(ThenBB, Builder.GetInsertBlock()->getParent());
4066 Value *LocalReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4067 ReduceList, Builder.getPtrTy());
4068 Value *RemoteReduceListPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4069 RemoteListAddrCast, Builder.getPtrTy());
4070 createRuntimeFunctionCall(ReduceFn, {LocalReduceListPtr, RemoteReduceListPtr})
4071 ->addFnAttr(Attribute::NoUnwind);
4072 Builder.CreateBr(MergeBB);
4073
4074 emitBlock(ElseBB, Builder.GetInsertBlock()->getParent());
4075 Builder.CreateBr(MergeBB);
4076
4077 emitBlock(MergeBB, Builder.GetInsertBlock()->getParent());
4078
4079 // if (AlgoVer==1 && (LaneId >= Offset)) copy Remote Reduce list to local
4080 // Reduce list.
4081 Algo1 = Builder.CreateICmpEQ(AlgoVer, Builder.getInt16(1));
4082 Value *LaneIdGtOffset = Builder.CreateICmpUGE(LaneId, RemoteLaneOffset);
4083 Value *CondCopy = Builder.CreateAnd(Algo1, LaneIdGtOffset);
4084
4085 BasicBlock *CpyThenBB = BasicBlock::Create(Ctx, "then");
4086 BasicBlock *CpyElseBB = BasicBlock::Create(Ctx, "else");
4087 BasicBlock *CpyMergeBB = BasicBlock::Create(Ctx, "ifcont");
4088 Builder.CreateCondBr(CondCopy, CpyThenBB, CpyElseBB);
4089
4090 emitBlock(CpyThenBB, Builder.GetInsertBlock()->getParent());
4091
4092 EmitRedLsCpRes = emitReductionListCopy(
4093 AllocaIP, CopyAction::ThreadCopy, RedListArrayTy, ReductionInfos,
4094 RemoteListAddrCast, ReduceList, IsByRef);
4095
4096 if (EmitRedLsCpRes)
4097 return EmitRedLsCpRes;
4098
4099 Builder.CreateBr(CpyMergeBB);
4100
4101 emitBlock(CpyElseBB, Builder.GetInsertBlock()->getParent());
4102 Builder.CreateBr(CpyMergeBB);
4103
4104 emitBlock(CpyMergeBB, Builder.GetInsertBlock()->getParent());
4105
4106 Builder.CreateRetVoid();
4107
4108 return SarFunc;
4109}
4110
4112OpenMPIRBuilder::generateReductionDescriptor(
4113 Value *DescriptorAddr, Value *DataPtr, Value *SrcDescriptorAddr,
4114 Type *DescriptorType,
4115 function_ref<InsertPointOrErrorTy(InsertPointTy, Value *, Value *&)>
4116 DataPtrPtrGen) {
4117
4118 // Copy the source descriptor to preserve all metadata (rank, extents,
4119 // strides, etc.)
4120 Value *DescriptorSize =
4121 Builder.getInt64(M.getDataLayout().getTypeStoreSize(DescriptorType));
4122 Builder.CreateMemCpy(
4123 DescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4124 SrcDescriptorAddr, M.getDataLayout().getPrefTypeAlign(DescriptorType),
4125 DescriptorSize);
4126
4127 // Update the base pointer field to point to the local shuffled data
4128 Value *DataPtrField;
4129 InsertPointOrErrorTy GenResult =
4130 DataPtrPtrGen(Builder.saveIP(), DescriptorAddr, DataPtrField);
4131
4132 if (!GenResult)
4133 return GenResult.takeError();
4134
4135 Builder.CreateStore(Builder.CreatePointerBitCastOrAddrSpaceCast(
4136 DataPtr, Builder.getPtrTy(), ".ascast"),
4137 DataPtrField);
4138
4139 return Builder.saveIP();
4140}
4141
4142Expected<Value *> OpenMPIRBuilder::createReductionDescriptorCopy(
4143 InsertPointTy AllocaIP, const ReductionInfo &RI, Value *DataPtr,
4144 Value *SrcDescriptorAddr, Type *DescriptorPtrTy, const Twine &Name) {
4145 InsertPointTy OldIP = Builder.saveIP();
4146 Builder.restoreIP(AllocaIP);
4147
4148 AllocaInst *DescriptorAlloca =
4149 Builder.CreateAlloca(RI.ByRefAllocatedType, nullptr, Name);
4150 DescriptorAlloca->setAlignment(
4151 M.getDataLayout().getPrefTypeAlign(RI.ByRefAllocatedType));
4152 Value *DescriptorAddr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4153 DescriptorAlloca, DescriptorPtrTy,
4154 DescriptorAlloca->getName() + ".ascast");
4155
4156 Builder.restoreIP(OldIP);
4157
4158 InsertPointOrErrorTy GenResult =
4159 generateReductionDescriptor(DescriptorAddr, DataPtr, SrcDescriptorAddr,
4160 RI.ByRefAllocatedType, RI.DataPtrPtrGen);
4161 if (!GenResult)
4162 return GenResult.takeError();
4163
4164 return DescriptorAddr;
4165}
4166
4167Expected<Function *> OpenMPIRBuilder::emitListToGlobalCopyFunction(
4168 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4169 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4170 IRBuilder<>::InsertPointGuard IPG(Builder);
4171 LLVMContext &Ctx = M.getContext();
4172 FunctionType *FuncTy = FunctionType::get(
4173 Builder.getVoidTy(),
4174 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4175 /* IsVarArg */ false);
4176 Function *LtGCFunc =
4178 "_omp_reduction_list_to_global_copy_func", &M);
4179 LtGCFunc->setAttributes(FuncAttrs);
4180 LtGCFunc->addParamAttr(0, Attribute::NoUndef);
4181 LtGCFunc->addParamAttr(1, Attribute::NoUndef);
4182 LtGCFunc->addParamAttr(2, Attribute::NoUndef);
4183
4184 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGCFunc);
4185 Builder.SetInsertPoint(EntryBlock);
4186 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4187
4188 // Buffer: global reduction buffer.
4189 Argument *BufferArg = LtGCFunc->getArg(0);
4190 // Idx: index of the buffer.
4191 Argument *IdxArg = LtGCFunc->getArg(1);
4192 // ReduceList: thread local Reduce list.
4193 Argument *ReduceListArg = LtGCFunc->getArg(2);
4194
4195 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4196 BufferArg->getName() + ".addr");
4197 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4198 IdxArg->getName() + ".addr");
4199 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4200 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4201 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4202 BufferArgAlloca, Builder.getPtrTy(),
4203 BufferArgAlloca->getName() + ".ascast");
4204 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4205 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4206 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4207 ReduceListArgAlloca, Builder.getPtrTy(),
4208 ReduceListArgAlloca->getName() + ".ascast");
4209
4210 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4211 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4212 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4213
4214 Value *LocalReduceList =
4215 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4216 Value *BufferArgVal =
4217 Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4218 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4219 Type *IndexTy = Builder.getIndexTy(
4220 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4221 for (auto En : enumerate(ReductionInfos)) {
4222 const ReductionInfo &RI = En.value();
4223 auto *RedListArrayTy =
4224 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4225 // Reduce element = LocalReduceList[i]
4226 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4227 RedListArrayTy, LocalReduceList,
4228 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4229 // elemptr = ((CopyType*)(elemptrptr)) + I
4230 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4231
4232 // Global = Buffer.VD[Idx];
4233 Value *BufferVD =
4234 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferArgVal, Idxs);
4235 Value *GlobVal = Builder.CreateConstInBoundsGEP2_32(
4236 ReductionsBufferTy, BufferVD, 0, En.index());
4237
4238 switch (RI.EvaluationKind) {
4239 case EvalKind::Scalar: {
4240 Value *TargetElement;
4241
4242 if (IsByRef.empty() || !IsByRef[En.index()]) {
4243 TargetElement = Builder.CreateLoad(RI.ElementType, ElemPtr);
4244 } else {
4245 if (RI.DataPtrPtrGen) {
4246 InsertPointOrErrorTy GenResult =
4247 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4248
4249 if (!GenResult)
4250 return GenResult.takeError();
4251
4252 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4253 }
4254 TargetElement = Builder.CreateLoad(RI.ByRefElementType, ElemPtr);
4255 }
4256
4257 Builder.CreateStore(TargetElement, GlobVal);
4258 break;
4259 }
4260 case EvalKind::Complex: {
4261 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4262 RI.ElementType, ElemPtr, 0, 0, ".realp");
4263 Value *SrcReal = Builder.CreateLoad(
4264 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4265 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4266 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4267 Value *SrcImg = Builder.CreateLoad(
4268 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4269
4270 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4271 RI.ElementType, GlobVal, 0, 0, ".realp");
4272 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4273 RI.ElementType, GlobVal, 0, 1, ".imagp");
4274 Builder.CreateStore(SrcReal, DestRealPtr);
4275 Builder.CreateStore(SrcImg, DestImgPtr);
4276 break;
4277 }
4278 case EvalKind::Aggregate: {
4279 Value *SizeVal =
4280 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4281 Builder.CreateMemCpy(
4282 GlobVal, M.getDataLayout().getPrefTypeAlign(RI.ElementType), ElemPtr,
4283 M.getDataLayout().getPrefTypeAlign(RI.ElementType), SizeVal, false);
4284 break;
4285 }
4286 }
4287 }
4288
4289 Builder.CreateRetVoid();
4290 return LtGCFunc;
4291}
4292
4293Expected<Function *> OpenMPIRBuilder::emitListToGlobalReduceFunction(
4294 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4295 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4296 IRBuilder<>::InsertPointGuard IPG(Builder);
4297 LLVMContext &Ctx = M.getContext();
4298 FunctionType *FuncTy = FunctionType::get(
4299 Builder.getVoidTy(),
4300 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4301 /* IsVarArg */ false);
4302 Function *LtGRFunc =
4304 "_omp_reduction_list_to_global_reduce_func", &M);
4305 LtGRFunc->setAttributes(FuncAttrs);
4306 LtGRFunc->addParamAttr(0, Attribute::NoUndef);
4307 LtGRFunc->addParamAttr(1, Attribute::NoUndef);
4308 LtGRFunc->addParamAttr(2, Attribute::NoUndef);
4309
4310 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", LtGRFunc);
4311 Builder.SetInsertPoint(EntryBlock);
4312 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4313
4314 // Buffer: global reduction buffer.
4315 Argument *BufferArg = LtGRFunc->getArg(0);
4316 // Idx: index of the buffer.
4317 Argument *IdxArg = LtGRFunc->getArg(1);
4318 // ReduceList: thread local Reduce list.
4319 Argument *ReduceListArg = LtGRFunc->getArg(2);
4320
4321 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4322 BufferArg->getName() + ".addr");
4323 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4324 IdxArg->getName() + ".addr");
4325 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4326 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4327 auto *RedListArrayTy =
4328 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4329
4330 // 1. Build a list of reduction variables.
4331 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4332 Value *LocalReduceList =
4333 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4334
4335 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4336
4337 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4338 BufferArgAlloca, Builder.getPtrTy(),
4339 BufferArgAlloca->getName() + ".ascast");
4340 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4341 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4342 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4343 ReduceListArgAlloca, Builder.getPtrTy(),
4344 ReduceListArgAlloca->getName() + ".ascast");
4345 Value *LocalReduceListAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4346 LocalReduceList, Builder.getPtrTy(),
4347 LocalReduceList->getName() + ".ascast");
4348
4349 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4350 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4351 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4352
4353 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4354 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4355 Type *IndexTy = Builder.getIndexTy(
4356 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4357 for (auto En : enumerate(ReductionInfos)) {
4358 const ReductionInfo &RI = En.value();
4359
4360 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4361 RedListArrayTy, LocalReduceListAddrCast,
4362 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4363 Value *BufferVD =
4364 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4365 // Global = Buffer.VD[Idx];
4366 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4367 ReductionsBufferTy, BufferVD, 0, En.index());
4368
4369 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4370 // Get source descriptor from the reduce list argument
4371 Value *ReduceList =
4372 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4373 Value *SrcElementPtrPtr =
4374 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceList,
4375 {ConstantInt::get(IndexTy, 0),
4376 ConstantInt::get(IndexTy, En.index())});
4377 Value *SrcDescriptorAddr =
4378 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4379
4380 // Copy descriptor from source and update base_ptr to global buffer data
4381 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4382 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4383 if (!ByRefAlloc)
4384 return ByRefAlloc.takeError();
4385
4386 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4387 } else {
4388 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4389 }
4390 }
4391
4392 // Call reduce_function(GlobalReduceList, ReduceList)
4393 Value *ReduceList =
4394 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4395 createRuntimeFunctionCall(ReduceFn, {LocalReduceListAddrCast, ReduceList})
4396 ->addFnAttr(Attribute::NoUnwind);
4397 Builder.CreateRetVoid();
4398 return LtGRFunc;
4399}
4400
4401Expected<Function *> OpenMPIRBuilder::emitGlobalToListCopyFunction(
4402 ArrayRef<ReductionInfo> ReductionInfos, Type *ReductionsBufferTy,
4403 AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4404 IRBuilder<>::InsertPointGuard IPG(Builder);
4405 LLVMContext &Ctx = M.getContext();
4406 FunctionType *FuncTy = FunctionType::get(
4407 Builder.getVoidTy(),
4408 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4409 /* IsVarArg */ false);
4410 Function *GtLCFunc =
4412 "_omp_reduction_global_to_list_copy_func", &M);
4413 GtLCFunc->setAttributes(FuncAttrs);
4414 GtLCFunc->addParamAttr(0, Attribute::NoUndef);
4415 GtLCFunc->addParamAttr(1, Attribute::NoUndef);
4416 GtLCFunc->addParamAttr(2, Attribute::NoUndef);
4417
4418 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLCFunc);
4419 Builder.SetInsertPoint(EntryBlock);
4420 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4421
4422 // Buffer: global reduction buffer.
4423 Argument *BufferArg = GtLCFunc->getArg(0);
4424 // Idx: index of the buffer.
4425 Argument *IdxArg = GtLCFunc->getArg(1);
4426 // ReduceList: thread local Reduce list.
4427 Argument *ReduceListArg = GtLCFunc->getArg(2);
4428
4429 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4430 BufferArg->getName() + ".addr");
4431 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4432 IdxArg->getName() + ".addr");
4433 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4434 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4435 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4436 BufferArgAlloca, Builder.getPtrTy(),
4437 BufferArgAlloca->getName() + ".ascast");
4438 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4439 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4440 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4441 ReduceListArgAlloca, Builder.getPtrTy(),
4442 ReduceListArgAlloca->getName() + ".ascast");
4443 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4444 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4445 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4446
4447 Value *LocalReduceList =
4448 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4449 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4450 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4451 Type *IndexTy = Builder.getIndexTy(
4452 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4453 for (auto En : enumerate(ReductionInfos)) {
4454 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
4455 auto *RedListArrayTy =
4456 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4457 // Reduce element = LocalReduceList[i]
4458 Value *ElemPtrPtr = Builder.CreateInBoundsGEP(
4459 RedListArrayTy, LocalReduceList,
4460 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4461 // elemptr = ((CopyType*)(elemptrptr)) + I
4462 Value *ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtrPtr);
4463 // Global = Buffer.VD[Idx];
4464 Value *BufferVD =
4465 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4466 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4467 ReductionsBufferTy, BufferVD, 0, En.index());
4468
4469 switch (RI.EvaluationKind) {
4470 case EvalKind::Scalar: {
4471 Type *ElemType = RI.ElementType;
4472
4473 if (!IsByRef.empty() && IsByRef[En.index()]) {
4474 ElemType = RI.ByRefElementType;
4475 if (RI.DataPtrPtrGen) {
4476 InsertPointOrErrorTy GenResult =
4477 RI.DataPtrPtrGen(Builder.saveIP(), ElemPtr, ElemPtr);
4478
4479 if (!GenResult)
4480 return GenResult.takeError();
4481
4482 ElemPtr = Builder.CreateLoad(Builder.getPtrTy(), ElemPtr);
4483 }
4484 }
4485
4486 Value *TargetElement = Builder.CreateLoad(ElemType, GlobValPtr);
4487 Builder.CreateStore(TargetElement, ElemPtr);
4488 break;
4489 }
4490 case EvalKind::Complex: {
4491 Value *SrcRealPtr = Builder.CreateConstInBoundsGEP2_32(
4492 RI.ElementType, GlobValPtr, 0, 0, ".realp");
4493 Value *SrcReal = Builder.CreateLoad(
4494 RI.ElementType->getStructElementType(0), SrcRealPtr, ".real");
4495 Value *SrcImgPtr = Builder.CreateConstInBoundsGEP2_32(
4496 RI.ElementType, GlobValPtr, 0, 1, ".imagp");
4497 Value *SrcImg = Builder.CreateLoad(
4498 RI.ElementType->getStructElementType(1), SrcImgPtr, ".imag");
4499
4500 Value *DestRealPtr = Builder.CreateConstInBoundsGEP2_32(
4501 RI.ElementType, ElemPtr, 0, 0, ".realp");
4502 Value *DestImgPtr = Builder.CreateConstInBoundsGEP2_32(
4503 RI.ElementType, ElemPtr, 0, 1, ".imagp");
4504 Builder.CreateStore(SrcReal, DestRealPtr);
4505 Builder.CreateStore(SrcImg, DestImgPtr);
4506 break;
4507 }
4508 case EvalKind::Aggregate: {
4509 Value *SizeVal =
4510 Builder.getInt64(M.getDataLayout().getTypeStoreSize(RI.ElementType));
4511 Builder.CreateMemCpy(
4512 ElemPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4513 GlobValPtr, M.getDataLayout().getPrefTypeAlign(RI.ElementType),
4514 SizeVal, false);
4515 break;
4516 }
4517 }
4518 }
4519
4520 Builder.CreateRetVoid();
4521 return GtLCFunc;
4522}
4523
4524Expected<Function *> OpenMPIRBuilder::emitGlobalToListReduceFunction(
4525 ArrayRef<ReductionInfo> ReductionInfos, Function *ReduceFn,
4526 Type *ReductionsBufferTy, AttributeList FuncAttrs, ArrayRef<bool> IsByRef) {
4527 IRBuilder<>::InsertPointGuard IPG(Builder);
4528 LLVMContext &Ctx = M.getContext();
4529 auto *FuncTy = FunctionType::get(
4530 Builder.getVoidTy(),
4531 {Builder.getPtrTy(), Builder.getInt32Ty(), Builder.getPtrTy()},
4532 /* IsVarArg */ false);
4533 Function *GtLRFunc =
4535 "_omp_reduction_global_to_list_reduce_func", &M);
4536 GtLRFunc->setAttributes(FuncAttrs);
4537 GtLRFunc->addParamAttr(0, Attribute::NoUndef);
4538 GtLRFunc->addParamAttr(1, Attribute::NoUndef);
4539 GtLRFunc->addParamAttr(2, Attribute::NoUndef);
4540
4541 BasicBlock *EntryBlock = BasicBlock::Create(Ctx, "entry", GtLRFunc);
4542 Builder.SetInsertPoint(EntryBlock);
4543 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4544
4545 // Buffer: global reduction buffer.
4546 Argument *BufferArg = GtLRFunc->getArg(0);
4547 // Idx: index of the buffer.
4548 Argument *IdxArg = GtLRFunc->getArg(1);
4549 // ReduceList: thread local Reduce list.
4550 Argument *ReduceListArg = GtLRFunc->getArg(2);
4551
4552 Value *BufferArgAlloca = Builder.CreateAlloca(Builder.getPtrTy(), nullptr,
4553 BufferArg->getName() + ".addr");
4554 Value *IdxArgAlloca = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
4555 IdxArg->getName() + ".addr");
4556 Value *ReduceListArgAlloca = Builder.CreateAlloca(
4557 Builder.getPtrTy(), nullptr, ReduceListArg->getName() + ".addr");
4558 ArrayType *RedListArrayTy =
4559 ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4560
4561 // 1. Build a list of reduction variables.
4562 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4563 Value *LocalReduceList =
4564 Builder.CreateAlloca(RedListArrayTy, nullptr, ".omp.reduction.red_list");
4565
4566 InsertPointTy AllocaIP{EntryBlock, EntryBlock->begin()};
4567
4568 Value *BufferArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4569 BufferArgAlloca, Builder.getPtrTy(),
4570 BufferArgAlloca->getName() + ".ascast");
4571 Value *IdxArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4572 IdxArgAlloca, Builder.getPtrTy(), IdxArgAlloca->getName() + ".ascast");
4573 Value *ReduceListArgAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4574 ReduceListArgAlloca, Builder.getPtrTy(),
4575 ReduceListArgAlloca->getName() + ".ascast");
4576 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4577 LocalReduceList, Builder.getPtrTy(),
4578 LocalReduceList->getName() + ".ascast");
4579
4580 Builder.CreateStore(BufferArg, BufferArgAddrCast);
4581 Builder.CreateStore(IdxArg, IdxArgAddrCast);
4582 Builder.CreateStore(ReduceListArg, ReduceListArgAddrCast);
4583
4584 Value *BufferVal = Builder.CreateLoad(Builder.getPtrTy(), BufferArgAddrCast);
4585 Value *Idxs[] = {Builder.CreateLoad(Builder.getInt32Ty(), IdxArgAddrCast)};
4586 Type *IndexTy = Builder.getIndexTy(
4587 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4588 for (auto En : enumerate(ReductionInfos)) {
4589 const ReductionInfo &RI = En.value();
4590
4591 Value *TargetElementPtrPtr = Builder.CreateInBoundsGEP(
4592 RedListArrayTy, ReductionList,
4593 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4594 // Global = Buffer.VD[Idx];
4595 Value *BufferVD =
4596 Builder.CreateInBoundsGEP(ReductionsBufferTy, BufferVal, Idxs);
4597 Value *GlobValPtr = Builder.CreateConstInBoundsGEP2_32(
4598 ReductionsBufferTy, BufferVD, 0, En.index());
4599
4600 if (!IsByRef.empty() && IsByRef[En.index()] && RI.DataPtrPtrGen) {
4601 // Get source descriptor from the reduce list
4602 Value *ReduceListVal =
4603 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4604 Value *SrcElementPtrPtr =
4605 Builder.CreateInBoundsGEP(RedListArrayTy, ReduceListVal,
4606 {ConstantInt::get(IndexTy, 0),
4607 ConstantInt::get(IndexTy, En.index())});
4608 Value *SrcDescriptorAddr =
4609 Builder.CreateLoad(Builder.getPtrTy(), SrcElementPtrPtr);
4610
4611 // Copy descriptor from source and update base_ptr to global buffer data
4612 Expected<Value *> ByRefAlloc = createReductionDescriptorCopy(
4613 AllocaIP, RI, GlobValPtr, SrcDescriptorAddr, Builder.getPtrTy());
4614 if (!ByRefAlloc)
4615 return ByRefAlloc.takeError();
4616
4617 Builder.CreateStore(*ByRefAlloc, TargetElementPtrPtr);
4618 } else {
4619 Builder.CreateStore(GlobValPtr, TargetElementPtrPtr);
4620 }
4621 }
4622
4623 // Call reduce_function(ReduceList, GlobalReduceList)
4624 Value *ReduceList =
4625 Builder.CreateLoad(Builder.getPtrTy(), ReduceListArgAddrCast);
4626 createRuntimeFunctionCall(ReduceFn, {ReduceList, ReductionList})
4627 ->addFnAttr(Attribute::NoUnwind);
4628 Builder.CreateRetVoid();
4629 return GtLRFunc;
4630}
4631
4632std::string OpenMPIRBuilder::getReductionFuncName(StringRef Name) const {
4633 std::string Suffix =
4634 createPlatformSpecificName({"omp", "reduction", "reduction_func"});
4635 return (Name + Suffix).str();
4636}
4637
4638Expected<Function *> OpenMPIRBuilder::createReductionFunction(
4639 StringRef ReducerName, ArrayRef<ReductionInfo> ReductionInfos,
4641 AttributeList FuncAttrs) {
4642 IRBuilder<>::InsertPointGuard IPG(Builder);
4643 auto *FuncTy = FunctionType::get(Builder.getVoidTy(),
4644 {Builder.getPtrTy(), Builder.getPtrTy()},
4645 /* IsVarArg */ false);
4646 std::string Name = getReductionFuncName(ReducerName);
4647 Function *ReductionFunc =
4649 ReductionFunc->setCallingConv(Config.getRuntimeCC());
4650 ReductionFunc->setAttributes(FuncAttrs);
4651 ReductionFunc->addParamAttr(0, Attribute::NoUndef);
4652 ReductionFunc->addParamAttr(1, Attribute::NoUndef);
4653 BasicBlock *EntryBB =
4654 BasicBlock::Create(M.getContext(), "entry", ReductionFunc);
4655 Builder.SetInsertPoint(EntryBB);
4656 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
4657
4658 // Need to alloca memory here and deal with the pointers before getting
4659 // LHS/RHS pointers out
4660 Value *LHSArrayPtr = nullptr;
4661 Value *RHSArrayPtr = nullptr;
4662 Argument *Arg0 = ReductionFunc->getArg(0);
4663 Argument *Arg1 = ReductionFunc->getArg(1);
4664 Type *Arg0Type = Arg0->getType();
4665 Type *Arg1Type = Arg1->getType();
4666
4667 Value *LHSAlloca =
4668 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
4669 Value *RHSAlloca =
4670 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
4671 Value *LHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4672 LHSAlloca, Arg0Type, LHSAlloca->getName() + ".ascast");
4673 Value *RHSAddrCast = Builder.CreatePointerBitCastOrAddrSpaceCast(
4674 RHSAlloca, Arg1Type, RHSAlloca->getName() + ".ascast");
4675 Builder.CreateStore(Arg0, LHSAddrCast);
4676 Builder.CreateStore(Arg1, RHSAddrCast);
4677 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
4678 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
4679
4680 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), ReductionInfos.size());
4681 Type *IndexTy = Builder.getIndexTy(
4682 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4683 SmallVector<Value *> LHSPtrs, RHSPtrs;
4684 for (auto En : enumerate(ReductionInfos)) {
4685 const ReductionInfo &RI = En.value();
4686 Value *RHSI8PtrPtr = Builder.CreateInBoundsGEP(
4687 RedArrayTy, RHSArrayPtr,
4688 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4689 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
4690 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4691 RHSI8Ptr, RI.PrivateVariable->getType(),
4692 RHSI8Ptr->getName() + ".ascast");
4693
4694 Value *LHSI8PtrPtr = Builder.CreateInBoundsGEP(
4695 RedArrayTy, LHSArrayPtr,
4696 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4697 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
4698 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
4699 LHSI8Ptr, RI.Variable->getType(), LHSI8Ptr->getName() + ".ascast");
4700
4702 LHSPtrs.emplace_back(LHSPtr);
4703 RHSPtrs.emplace_back(RHSPtr);
4704 } else {
4705 Value *LHS = LHSPtr;
4706 Value *RHS = RHSPtr;
4707
4708 if (!IsByRef.empty() && !IsByRef[En.index()]) {
4709 LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
4710 RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
4711 }
4712
4713 Value *Reduced;
4714 InsertPointOrErrorTy AfterIP =
4715 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
4716 if (!AfterIP)
4717 return AfterIP.takeError();
4718 if (!Builder.GetInsertBlock())
4719 return ReductionFunc;
4720
4721 Builder.restoreIP(*AfterIP);
4722
4723 if (!IsByRef.empty() && !IsByRef[En.index()])
4724 Builder.CreateStore(Reduced, LHSPtr);
4725 }
4726 }
4727
4729 for (auto En : enumerate(ReductionInfos)) {
4730 unsigned Index = En.index();
4731 const ReductionInfo &RI = En.value();
4732 Value *LHSFixupPtr, *RHSFixupPtr;
4733 Builder.restoreIP(RI.ReductionGenClang(
4734 Builder.saveIP(), Index, &LHSFixupPtr, &RHSFixupPtr, ReductionFunc));
4735
4736 // Fix the CallBack code genereated to use the correct Values for the LHS
4737 // and RHS
4738 LHSFixupPtr->replaceUsesWithIf(
4739 LHSPtrs[Index], [ReductionFunc](const Use &U) {
4740 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4741 ReductionFunc;
4742 });
4743 RHSFixupPtr->replaceUsesWithIf(
4744 RHSPtrs[Index], [ReductionFunc](const Use &U) {
4745 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
4746 ReductionFunc;
4747 });
4748 }
4749
4750 Builder.CreateRetVoid();
4751 // Compiling with `-O0`, `alloca`s emitted in non-entry blocks are not hoisted
4752 // to the entry block (this is dones for higher opt levels by later passes in
4753 // the pipeline). This has caused issues because non-entry `alloca`s force the
4754 // function to use dynamic stack allocations and we might run out of scratch
4755 // memory.
4756 hoistNonEntryAllocasToEntryBlock(ReductionFunc);
4757
4758 return ReductionFunc;
4759}
4760
4761static void
4763 bool IsGPU) {
4764 for (const OpenMPIRBuilder::ReductionInfo &RI : ReductionInfos) {
4765 (void)RI;
4766 assert(RI.Variable && "expected non-null variable");
4767 assert(RI.PrivateVariable && "expected non-null private variable");
4768 assert((RI.ReductionGen || RI.ReductionGenClang) &&
4769 "expected non-null reduction generator callback");
4770 if (!IsGPU) {
4771 assert(
4772 RI.Variable->getType() == RI.PrivateVariable->getType() &&
4773 "expected variables and their private equivalents to have the same "
4774 "type");
4775 }
4776 assert(RI.Variable->getType()->isPointerTy() &&
4777 "expected variables to be pointers");
4778 }
4779}
4780
4781// The atomic cross-team reduction fast path applies when every reduction in the
4782// set can be represented by an atomicrmw. Clang only populates it for scalar
4783// reductions with a supported atomic operator.
4786 return all_of(ReductionInfos, [](const OpenMPIRBuilder::ReductionInfo &RI) {
4787 return static_cast<bool>(RI.AtomicReductionGen);
4788 });
4789}
4790
4792 const LocationDescription &Loc, InsertPointTy AllocaIP,
4793 InsertPointTy CodeGenIP, ArrayRef<ReductionInfo> ReductionInfos,
4794 ArrayRef<bool> IsByRef, bool IsNoWait, bool IsTeamsReduction, bool IsSPMD,
4795 ReductionGenCBKind ReductionGenCBKind, std::optional<omp::GV> GridValue,
4796 Value *SrcLocInfo) {
4797 if (!updateToLocation(Loc))
4798 return InsertPointTy();
4799 Builder.restoreIP(CodeGenIP);
4800 checkReductionInfos(ReductionInfos, /*IsGPU*/ true);
4801 LLVMContext &Ctx = M.getContext();
4802
4803 // Source location for the ident struct
4804 if (!SrcLocInfo) {
4805 uint32_t SrcLocStrSize;
4806 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
4807 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
4808 }
4809
4810 if (ReductionInfos.size() == 0)
4811 return Builder.saveIP();
4812
4813 BasicBlock *ContinuationBlock = nullptr;
4815 // Copied code from createReductions
4816 BasicBlock *InsertBlock = Loc.IP.getBlock();
4817 ContinuationBlock =
4818 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
4819 InsertBlock->getTerminator()->eraseFromParent();
4820 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
4821 }
4822
4823 Function *CurFunc = Builder.GetInsertBlock()->getParent();
4824 AttributeList FuncAttrs;
4825 AttrBuilder AttrBldr(Ctx);
4826 for (auto Attr : CurFunc->getAttributes().getFnAttrs())
4827 AttrBldr.addAttribute(Attr);
4828 AttrBldr.removeAttribute(Attribute::OptimizeNone);
4829 FuncAttrs = FuncAttrs.addFnAttributes(Ctx, AttrBldr);
4830
4831 CodeGenIP = Builder.saveIP();
4832 Expected<Function *> ReductionResult = createReductionFunction(
4833 Builder.GetInsertBlock()->getParent()->getName(), ReductionInfos, IsByRef,
4834 ReductionGenCBKind, FuncAttrs);
4835 if (!ReductionResult)
4836 return ReductionResult.takeError();
4837 Function *ReductionFunc = *ReductionResult;
4838 Builder.restoreIP(CodeGenIP);
4839
4840 // Set the grid value in the config needed for lowering later on
4841 if (GridValue.has_value())
4842 Config.setGridValue(GridValue.value());
4843 else
4844 Config.setGridValue(getGridValue(T, ReductionFunc));
4845
4846 // Build res = __kmpc_reduce{_nowait}(<gtid>, <n>, sizeof(RedList),
4847 // RedList, shuffle_reduce_func, interwarp_copy_func);
4848 // or
4849 // Build res = __kmpc_reduce_teams_nowait_simple(<loc>, <gtid>, <lck>);
4850 Value *Res;
4851
4852 // 1. Build a list of reduction variables.
4853 // void *RedList[<n>] = {<ReductionVars>[0], ..., <ReductionVars>[<n>-1]};
4854 auto Size = ReductionInfos.size();
4855 Type *PtrTy = PointerType::get(Ctx, Config.getDefaultTargetAS());
4856 Type *FuncPtrTy =
4857 Builder.getPtrTy(M.getDataLayout().getProgramAddressSpace());
4858 Type *RedArrayTy = ArrayType::get(PtrTy, Size);
4859 CodeGenIP = Builder.saveIP();
4860 Builder.restoreIP(AllocaIP);
4861 Value *ReductionListAlloca =
4862 Builder.CreateAlloca(RedArrayTy, nullptr, ".omp.reduction.red_list");
4863 Value *ReductionList = Builder.CreatePointerBitCastOrAddrSpaceCast(
4864 ReductionListAlloca, PtrTy, ReductionListAlloca->getName() + ".ascast");
4865 Builder.restoreIP(CodeGenIP);
4866 Type *IndexTy = Builder.getIndexTy(
4867 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
4868 for (auto En : enumerate(ReductionInfos)) {
4869 const ReductionInfo &RI = En.value();
4870 Value *ElemPtr = Builder.CreateInBoundsGEP(
4871 RedArrayTy, ReductionList,
4872 {ConstantInt::get(IndexTy, 0), ConstantInt::get(IndexTy, En.index())});
4873
4874 Value *PrivateVar = RI.PrivateVariable;
4875 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
4876 if (IsByRefElem)
4877 PrivateVar = Builder.CreateLoad(RI.ElementType, PrivateVar);
4878
4879 Value *CastElem =
4880 Builder.CreatePointerBitCastOrAddrSpaceCast(PrivateVar, PtrTy);
4881 Builder.CreateStore(CastElem, ElemPtr);
4882 }
4883 CodeGenIP = Builder.saveIP();
4884 Expected<Function *> SarFunc = emitShuffleAndReduceFunction(
4885 ReductionInfos, ReductionFunc, FuncAttrs, IsByRef);
4886
4887 if (!SarFunc)
4888 return SarFunc.takeError();
4889
4890 Expected<Function *> CopyResult =
4891 emitInterWarpCopyFunction(Loc, ReductionInfos, FuncAttrs, IsByRef);
4892 if (!CopyResult)
4893 return CopyResult.takeError();
4894 Function *WcFunc = *CopyResult;
4895 Builder.restoreIP(CodeGenIP);
4896
4897 Value *RL = Builder.CreatePointerBitCastOrAddrSpaceCast(ReductionList, PtrTy);
4898
4899 // NOTE: ReductionDataSize is passed as the reduce_data_size argument to
4900 // __kmpc_nvptx_parallel_reduce_nowait_v2, but the runtime implementations do
4901 // not currently use it. It is computed here conservatively as max(element
4902 // sizes) * N rather than the exact sum, which over-calculates the size for
4903 // mixed reduction types but is harmless given the argument is unused.
4904 // TODO: Consider dropping this computation if the runtime API is ever revised
4905 // to remove the unused parameter.
4906 unsigned MaxDataSize = 0;
4907 SmallVector<Type *> ReductionTypeArgs;
4908 for (auto En : enumerate(ReductionInfos)) {
4909 // Use ByRefElementType for by-ref reductions so that MaxDataSize matches
4910 // the actual data size stored in the global reduction buffer, consistent
4911 // with the ReductionsBufferTy struct used for GEP offsets below.
4912 Type *RedTypeArg = (!IsByRef.empty() && IsByRef[En.index()])
4913 ? En.value().ByRefElementType
4914 : En.value().ElementType;
4915 auto Size = M.getDataLayout().getTypeStoreSize(RedTypeArg);
4916 if (Size > MaxDataSize)
4917 MaxDataSize = Size;
4918 ReductionTypeArgs.emplace_back(RedTypeArg);
4919 }
4920 Value *ReductionDataSize =
4921 Builder.getInt64(MaxDataSize * ReductionInfos.size());
4922
4923 // Helper function to copy thread-local data back to the original reduction
4924 // list.
4925 Function *CopyScratchToListFunc = nullptr;
4926 // Thread-local storage for the reduction variables.
4927 Value *ScratchForCopyBack = nullptr;
4928 // RL pointer to which the final value from the per-thread scratch should be
4929 // copied back. (Basically RL, appropriately casted if necessary.)
4930 Value *RLForCopyBack = RL;
4931
4932 bool IsAtomicReduction =
4933 IsTeamsReduction && isAtomicableReductionSet(ReductionInfos);
4934
4935 if (!IsTeamsReduction) {
4936 Value *SarFuncCast =
4937 Builder.CreatePointerBitCastOrAddrSpaceCast(*SarFunc, FuncPtrTy);
4938 Value *WcFuncCast =
4939 Builder.CreatePointerBitCastOrAddrSpaceCast(WcFunc, FuncPtrTy);
4940 Value *Args[] = {SrcLocInfo, ReductionDataSize, RL, SarFuncCast,
4941 WcFuncCast};
4943 RuntimeFunction::OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2);
4944 Res = createRuntimeFunctionCall(Pv2Ptr, Args);
4945 } else if (IsAtomicReduction) {
4946 // Atomic cross-team reduction fast path: determine the team's main thread
4947 // that is later to fold its value atomically into the mapped variable.
4948 Function *IsMainThreadFn = getOrCreateRuntimeFunctionPtr(
4949 RuntimeFunction::OMPRTL___kmpc_is_team_main_thread);
4950 Res = createRuntimeFunctionCall(IsMainThreadFn, {});
4951 } else {
4952 CodeGenIP = Builder.saveIP();
4953 StructType *ReductionsBufferTy = StructType::create(
4954 Ctx, ReductionTypeArgs, "struct._globalized_locals_ty");
4955
4956 Expected<Function *> LtGCFunc = emitListToGlobalCopyFunction(
4957 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4958 if (!LtGCFunc)
4959 return LtGCFunc.takeError();
4960
4961 Expected<Function *> GtLCFunc = emitGlobalToListCopyFunction(
4962 ReductionInfos, ReductionsBufferTy, FuncAttrs, IsByRef);
4963 if (!GtLCFunc)
4964 return GtLCFunc.takeError();
4965
4966 Expected<Function *> GtLRFunc = emitGlobalToListReduceFunction(
4967 ReductionInfos, ReductionFunc, ReductionsBufferTy, FuncAttrs, IsByRef);
4968 if (!GtLRFunc)
4969 return GtLRFunc.takeError();
4970
4971 Builder.restoreIP(CodeGenIP);
4972
4973 // The runtime's cross-team final aggregate uses the storage pointed at by
4974 // its reduce-list argument as per-thread scratch. When the surrounding
4975 // kernel is already in SPMD execution mode, clang emitted each reduction
4976 // private as a per-thread `alloca addrspace(5)`, so the original red_list
4977 // (RL) is already per-thread and nothing else is needed.
4978 //
4979 // When the kernel is in Non-SPMD execution mode at codegen time, clang's
4980 // Generic-mode globalization put the reduction private into team-shared
4981 // LDS. OpenMPOpt may later upgrade the kernel to Generic-SPMD, at which
4982 // point all threads of the last team would race on the shared LDS slot.
4983 // Emit a per-thread scratch buffer and a per-thread RL, copy the team-local
4984 // value in, and hand the per-thread RL to the runtime instead. The writer
4985 // thread copies the final value from that per-thread scratch back to RL
4986 // before running the existing combine path below.
4987
4988 // Thread-local RL (might need localization below before being passed to the
4989 // runtime).
4990 Value *RuntimeRL = RL;
4991
4992 if (!IsSPMD) {
4993 CodeGenIP = Builder.saveIP();
4994 Builder.restoreIP(AllocaIP);
4995 // Allocate thread-local buffer for the reduction variables.
4996 Value *PerThreadScratchAlloca = Builder.CreateAlloca(
4997 ReductionsBufferTy, /*ArraySize=*/nullptr, ".omp.reduction.scratch");
4998 Value *PerThreadScratch = Builder.CreatePointerBitCastOrAddrSpaceCast(
4999 PerThreadScratchAlloca, PtrTy,
5000 PerThreadScratchAlloca->getName() + ".ascast");
5001 // Allocate thread-local buffer for the pointers to the reduction
5002 // variables.
5003 Value *PerThreadRedListAlloca =
5004 Builder.CreateAlloca(RedArrayTy, /*ArraySize=*/nullptr,
5005 ".omp.reduction.per_thread_red_list");
5006 RuntimeRL = Builder.CreatePointerBitCastOrAddrSpaceCast(
5007 PerThreadRedListAlloca, PtrTy,
5008 PerThreadRedListAlloca->getName() + ".ascast");
5009 Builder.restoreIP(CodeGenIP);
5010
5011 // Iterate over the reduction variables and copy the team-local value to
5012 // the thread-local buffer.
5013 for (auto En : enumerate(ReductionInfos)) {
5014 const ReductionInfo &RI = En.value();
5015 bool IsByRefElem = !IsByRef.empty() && IsByRef[En.index()];
5016
5017 Value *FieldPtr = Builder.CreateConstInBoundsGEP2_32(
5018 ReductionsBufferTy, PerThreadScratch, 0, En.index());
5019 Value *Slot = Builder.CreateConstInBoundsGEP2_32(RedArrayTy, RuntimeRL,
5020 0, En.index());
5021
5022 Value *RuntimeListEntry = FieldPtr;
5023 if (IsByRefElem && RI.DataPtrPtrGen) {
5024 Value *SrcDescriptor =
5025 Builder.CreateLoad(RI.ElementType, RI.PrivateVariable);
5026 Expected<Value *> Descriptor = createReductionDescriptorCopy(
5027 AllocaIP, RI, FieldPtr, SrcDescriptor, PtrTy);
5028 if (!Descriptor)
5029 return Descriptor.takeError();
5030 RuntimeListEntry = *Descriptor;
5031 }
5032 Builder.CreateStore(RuntimeListEntry, Slot);
5033 }
5034 // The copy helpers were emitted with default-AS (AS 0) pointer params
5035 // (see emitListToGlobalCopyFunction / emitGlobalToListCopyFunction),
5036 // but PerThreadScratch and RL live in the target's default AS, which
5037 // is non-zero on e.g. SPIRV. (See Config.getDefaultTargetAS().)
5038 Type *CopyArg0Ty = (*LtGCFunc)->getFunctionType()->getParamType(0);
5039 Type *CopyArg2Ty = (*LtGCFunc)->getFunctionType()->getParamType(2);
5040 ScratchForCopyBack = Builder.CreatePointerBitCastOrAddrSpaceCast(
5041 PerThreadScratch, CopyArg0Ty);
5042 RLForCopyBack =
5043 Builder.CreatePointerBitCastOrAddrSpaceCast(RL, CopyArg2Ty);
5044 // Use index 0 because there is no array of target values to index into,
5045 // there is only one thread-local memory slot.
5046 // restoreIP above left a stale/empty debug location; this inlinable call
5047 // to a debug-info-bearing helper needs one or the verifier rejects the
5048 // module ("!dbg attachment points at wrong subprogram") after inlining.
5049 Builder.SetCurrentDebugLocation(Loc.DL);
5050 Builder.CreateCall(
5051 *LtGCFunc, {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5052 CopyScratchToListFunc = *GtLCFunc;
5053 }
5054
5055 Value *Args3[] = {SrcLocInfo, RuntimeRL, *SarFunc, WcFunc,
5056 *LtGCFunc, *GtLCFunc, *GtLRFunc};
5057
5058 Function *TeamsReduceFn = getOrCreateRuntimeFunctionPtr(
5059 RuntimeFunction::OMPRTL___kmpc_gpu_xteam_reduce_nowait);
5060 Res = createRuntimeFunctionCall(TeamsReduceFn, Args3);
5061 }
5062
5063 // 5. Build if (res == 1)
5064 BasicBlock *ExitBB = BasicBlock::Create(Ctx, ".omp.reduction.done");
5065 BasicBlock *ThenBB = BasicBlock::Create(Ctx, ".omp.reduction.then");
5066 Value *Cond = Builder.CreateICmpEQ(Res, Builder.getInt32(1));
5067 Builder.CreateCondBr(Cond, ThenBB, ExitBB);
5068
5069 // 6. Build then branch: where we have reduced values in the master
5070 // thread in each team.
5071 // __kmpc_end_reduce{_nowait}(<gtid>);
5072 // break;
5073 emitBlock(ThenBB, CurFunc);
5074
5075 // Copy the writer thread's per-thread scratch result back into the original
5076 // red-list storage before the existing combine path reads RI.PrivateVariable.
5077 // Set a debug location: this inlinable call to a debug-info-bearing helper
5078 // needs one or the verifier rejects the module after inlining.
5079 if (ScratchForCopyBack) {
5080 Builder.SetCurrentDebugLocation(Loc.DL);
5081 Builder.CreateCall(
5082 CopyScratchToListFunc,
5083 {ScratchForCopyBack, Builder.getInt32(0), RLForCopyBack});
5084 }
5085
5086 // Add emission of __kmpc_end_reduce{_nowait}(<gtid>);
5087 for (auto En : enumerate(ReductionInfos)) {
5088 const ReductionInfo &RI = En.value();
5089
5090 // Atomic cross-team fast path: each team's main thread folds its
5091 // team-reduced value directly into the mapped reduction variable with a
5092 // single atomicrmw.
5093 if (IsAtomicReduction) {
5095 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5096 if (!AfterIP)
5097 return AfterIP.takeError();
5098 Builder.restoreIP(*AfterIP);
5099 continue;
5100 }
5101
5103 Value *RedValue = RI.Variable;
5104
5105 Value *RHS =
5106 Builder.CreatePointerBitCastOrAddrSpaceCast(RI.PrivateVariable, PtrTy);
5107
5109 Value *LHSPtr, *RHSPtr;
5110 Builder.restoreIP(RI.ReductionGenClang(Builder.saveIP(), En.index(),
5111 &LHSPtr, &RHSPtr, CurFunc));
5112
5113 // Fix the CallBack code genereated to use the correct Values for the LHS
5114 // and RHS. Cast to match types before replacing (necessary to handle
5115 // different address spaces).
5116 if (LHSPtr->getType() != RedValue->getType())
5117 RedValue = Builder.CreatePointerBitCastOrAddrSpaceCast(
5118 RedValue, LHSPtr->getType());
5119 if (RHSPtr->getType() != RHS->getType())
5120 RHS =
5121 Builder.CreatePointerBitCastOrAddrSpaceCast(RHS, RHSPtr->getType());
5122
5123 LHSPtr->replaceUsesWithIf(RedValue, [ReductionFunc](const Use &U) {
5124 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5125 ReductionFunc;
5126 });
5127 RHSPtr->replaceUsesWithIf(RHS, [ReductionFunc](const Use &U) {
5128 return cast<Instruction>(U.getUser())->getParent()->getParent() ==
5129 ReductionFunc;
5130 });
5131 } else {
5132 if (IsByRef.empty() || !IsByRef[En.index()]) {
5133 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5134 "red.value." + Twine(En.index()));
5135 }
5136 Value *PrivateRedValue = Builder.CreateLoad(
5137 ValueType, RHS, "red.private.value" + Twine(En.index()));
5138 Value *Reduced;
5139 InsertPointOrErrorTy AfterIP =
5140 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5141 if (!AfterIP)
5142 return AfterIP.takeError();
5143 Builder.restoreIP(*AfterIP);
5144
5145 if (!IsByRef.empty() && !IsByRef[En.index()])
5146 Builder.CreateStore(Reduced, RI.Variable);
5147 }
5148 }
5149 emitBlock(ExitBB, CurFunc);
5150 if (ContinuationBlock) {
5151 Builder.CreateBr(ContinuationBlock);
5152 Builder.SetInsertPoint(ContinuationBlock);
5153 }
5154 Config.setEmitLLVMUsed();
5155
5156 return Builder.saveIP();
5157}
5158
5160 Type *VoidTy = Type::getVoidTy(M.getContext());
5161 Type *Int8PtrTy = PointerType::getUnqual(M.getContext());
5162 auto *FuncTy =
5163 FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy}, /* IsVarArg */ false);
5165 ".omp.reduction.func", &M);
5166}
5167
5169 Function *ReductionFunc,
5171 IRBuilder<> &Builder, ArrayRef<bool> IsByRef, bool IsGPU) {
5172 IRBuilder<>::InsertPointGuard IPG(Builder);
5173 Module *Module = ReductionFunc->getParent();
5174 BasicBlock *ReductionFuncBlock =
5175 BasicBlock::Create(Module->getContext(), "", ReductionFunc);
5176 Builder.SetInsertPoint(ReductionFuncBlock);
5177 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
5178 Value *LHSArrayPtr = nullptr;
5179 Value *RHSArrayPtr = nullptr;
5180 if (IsGPU) {
5181 // Need to alloca memory here and deal with the pointers before getting
5182 // LHS/RHS pointers out
5183 //
5184 Argument *Arg0 = ReductionFunc->getArg(0);
5185 Argument *Arg1 = ReductionFunc->getArg(1);
5186 Type *Arg0Type = Arg0->getType();
5187 Type *Arg1Type = Arg1->getType();
5188
5189 Value *LHSAlloca =
5190 Builder.CreateAlloca(Arg0Type, nullptr, Arg0->getName() + ".addr");
5191 Value *RHSAlloca =
5192 Builder.CreateAlloca(Arg1Type, nullptr, Arg1->getName() + ".addr");
5193 Value *LHSAddrCast =
5194 Builder.CreatePointerBitCastOrAddrSpaceCast(LHSAlloca, Arg0Type);
5195 Value *RHSAddrCast =
5196 Builder.CreatePointerBitCastOrAddrSpaceCast(RHSAlloca, Arg1Type);
5197 Builder.CreateStore(Arg0, LHSAddrCast);
5198 Builder.CreateStore(Arg1, RHSAddrCast);
5199 LHSArrayPtr = Builder.CreateLoad(Arg0Type, LHSAddrCast);
5200 RHSArrayPtr = Builder.CreateLoad(Arg1Type, RHSAddrCast);
5201 } else {
5202 LHSArrayPtr = ReductionFunc->getArg(0);
5203 RHSArrayPtr = ReductionFunc->getArg(1);
5204 }
5205
5206 unsigned NumReductions = ReductionInfos.size();
5207 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5208
5209 for (auto En : enumerate(ReductionInfos)) {
5210 const OpenMPIRBuilder::ReductionInfo &RI = En.value();
5211 Value *LHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5212 RedArrayTy, LHSArrayPtr, 0, En.index());
5213 Value *LHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), LHSI8PtrPtr);
5214 Value *LHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5215 LHSI8Ptr, RI.Variable->getType());
5216 Value *LHS = Builder.CreateLoad(RI.ElementType, LHSPtr);
5217 Value *RHSI8PtrPtr = Builder.CreateConstInBoundsGEP2_64(
5218 RedArrayTy, RHSArrayPtr, 0, En.index());
5219 Value *RHSI8Ptr = Builder.CreateLoad(Builder.getPtrTy(), RHSI8PtrPtr);
5220 Value *RHSPtr = Builder.CreatePointerBitCastOrAddrSpaceCast(
5221 RHSI8Ptr, RI.PrivateVariable->getType());
5222 Value *RHS = Builder.CreateLoad(RI.ElementType, RHSPtr);
5223 Value *Reduced;
5225 RI.ReductionGen(Builder.saveIP(), LHS, RHS, Reduced);
5226 if (!AfterIP)
5227 return AfterIP.takeError();
5228
5229 Builder.restoreIP(*AfterIP);
5230 // TODO: Consider flagging an error.
5231 if (!Builder.GetInsertBlock())
5232 return Error::success();
5233
5234 // store is inside of the reduction region when using by-ref
5235 if (!IsByRef[En.index()])
5236 Builder.CreateStore(Reduced, LHSPtr);
5237 }
5238 Builder.CreateRetVoid();
5239 return Error::success();
5240}
5241
5243 const LocationDescription &Loc, InsertPointTy AllocaIP,
5244 ArrayRef<ReductionInfo> ReductionInfos, ArrayRef<bool> IsByRef,
5245 bool IsNoWait, bool IsTeamsReduction) {
5246 assert(ReductionInfos.size() == IsByRef.size());
5247 if (Config.isGPU())
5248 return createReductionsGPU(Loc, AllocaIP, Builder.saveIP(), ReductionInfos,
5249 IsByRef, IsNoWait, IsTeamsReduction);
5250
5251 checkReductionInfos(ReductionInfos, /*IsGPU*/ false);
5252
5253 if (!updateToLocation(Loc))
5254 return InsertPointTy();
5255
5256 if (ReductionInfos.size() == 0)
5257 return Builder.saveIP();
5258
5259 BasicBlock *InsertBlock = Loc.IP.getBlock();
5260 BasicBlock *ContinuationBlock =
5261 InsertBlock->splitBasicBlock(Loc.IP.getPoint(), "reduce.finalize");
5262 InsertBlock->getTerminator()->eraseFromParent();
5263
5264 // Create and populate array of type-erased pointers to private reduction
5265 // values.
5266 unsigned NumReductions = ReductionInfos.size();
5267 Type *RedArrayTy = ArrayType::get(Builder.getPtrTy(), NumReductions);
5268 Builder.SetInsertPoint(AllocaIP.getBlock()->getTerminator());
5269 Value *RedArray = Builder.CreateAlloca(RedArrayTy, nullptr, "red.array");
5270
5271 Builder.SetInsertPoint(InsertBlock, InsertBlock->end());
5272
5273 for (auto En : enumerate(ReductionInfos)) {
5274 unsigned Index = En.index();
5275 const ReductionInfo &RI = En.value();
5276 Value *RedArrayElemPtr = Builder.CreateConstInBoundsGEP2_64(
5277 RedArrayTy, RedArray, 0, Index, "red.array.elem." + Twine(Index));
5278 Builder.CreateStore(RI.PrivateVariable, RedArrayElemPtr);
5279 }
5280
5281 // Emit a call to the runtime function that orchestrates the reduction.
5282 // Declare the reduction function in the process.
5283 Type *IndexTy = Builder.getIndexTy(
5284 M.getDataLayout(), M.getDataLayout().getDefaultGlobalsAddressSpace());
5285 Function *Func = Builder.GetInsertBlock()->getParent();
5286 Module *Module = Func->getParent();
5287 uint32_t SrcLocStrSize;
5288 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5289 bool CanGenerateAtomic = all_of(ReductionInfos, [](const ReductionInfo &RI) {
5290 return RI.AtomicReductionGen;
5291 });
5292 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize,
5293 CanGenerateAtomic
5294 ? IdentFlag::OMP_IDENT_FLAG_ATOMIC_REDUCE
5295 : IdentFlag(0));
5296 Value *ThreadId = getOrCreateThreadID(Ident);
5297 Constant *NumVariables = Builder.getInt32(NumReductions);
5298 const DataLayout &DL = Module->getDataLayout();
5299 unsigned RedArrayByteSize = DL.getTypeStoreSize(RedArrayTy);
5300 Constant *RedArraySize = ConstantInt::get(IndexTy, RedArrayByteSize);
5301 Function *ReductionFunc = getFreshReductionFunc(*Module);
5302 Value *Lock = getOMPCriticalRegionLock(".reduction");
5304 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_reduce_nowait
5305 : RuntimeFunction::OMPRTL___kmpc_reduce);
5306 CallInst *ReduceCall =
5307 createRuntimeFunctionCall(ReduceFunc,
5308 {Ident, ThreadId, NumVariables, RedArraySize,
5309 RedArray, ReductionFunc, Lock},
5310 "reduce");
5311
5312 // Create final reduction entry blocks for the atomic and non-atomic case.
5313 // Emit IR that dispatches control flow to one of the blocks based on the
5314 // reduction supporting the atomic mode.
5315 BasicBlock *NonAtomicRedBlock =
5316 BasicBlock::Create(Module->getContext(), "reduce.switch.nonatomic", Func);
5317 BasicBlock *AtomicRedBlock =
5318 BasicBlock::Create(Module->getContext(), "reduce.switch.atomic", Func);
5319 SwitchInst *Switch =
5320 Builder.CreateSwitch(ReduceCall, ContinuationBlock, /* NumCases */ 2);
5321 Switch->addCase(Builder.getInt32(1), NonAtomicRedBlock);
5322 Switch->addCase(Builder.getInt32(2), AtomicRedBlock);
5323
5324 // Populate the non-atomic reduction using the elementwise reduction function.
5325 // This loads the elements from the global and private variables and reduces
5326 // them before storing back the result to the global variable.
5327 Builder.SetInsertPoint(NonAtomicRedBlock);
5328 for (auto En : enumerate(ReductionInfos)) {
5329 const ReductionInfo &RI = En.value();
5331 // We have one less load for by-ref case because that load is now inside of
5332 // the reduction region
5333 Value *RedValue = RI.Variable;
5334 if (!IsByRef[En.index()]) {
5335 RedValue = Builder.CreateLoad(ValueType, RI.Variable,
5336 "red.value." + Twine(En.index()));
5337 }
5338 Value *PrivateRedValue =
5339 Builder.CreateLoad(ValueType, RI.PrivateVariable,
5340 "red.private.value." + Twine(En.index()));
5341 Value *Reduced;
5342 InsertPointOrErrorTy AfterIP =
5343 RI.ReductionGen(Builder.saveIP(), RedValue, PrivateRedValue, Reduced);
5344 if (!AfterIP)
5345 return AfterIP.takeError();
5346 Builder.restoreIP(*AfterIP);
5347
5348 if (!Builder.GetInsertBlock())
5349 return InsertPointTy();
5350 // for by-ref case, the load is inside of the reduction region
5351 if (!IsByRef[En.index()])
5352 Builder.CreateStore(Reduced, RI.Variable);
5353 }
5354 Function *EndReduceFunc = getOrCreateRuntimeFunctionPtr(
5355 IsNoWait ? RuntimeFunction::OMPRTL___kmpc_end_reduce_nowait
5356 : RuntimeFunction::OMPRTL___kmpc_end_reduce);
5357 createRuntimeFunctionCall(EndReduceFunc, {Ident, ThreadId, Lock});
5358 Builder.CreateBr(ContinuationBlock);
5359
5360 // Populate the atomic reduction using the atomic elementwise reduction
5361 // function. There are no loads/stores here because they will be happening
5362 // inside the atomic elementwise reduction.
5363 Builder.SetInsertPoint(AtomicRedBlock);
5364 if (CanGenerateAtomic && llvm::none_of(IsByRef, [](bool P) { return P; })) {
5365 for (const ReductionInfo &RI : ReductionInfos) {
5367 Builder.saveIP(), RI.ElementType, RI.Variable, RI.PrivateVariable);
5368 if (!AfterIP)
5369 return AfterIP.takeError();
5370 Builder.restoreIP(*AfterIP);
5371 if (!Builder.GetInsertBlock())
5372 return InsertPointTy();
5373 }
5374 Builder.CreateBr(ContinuationBlock);
5375 } else {
5376 Builder.CreateUnreachable();
5377 }
5378
5379 // Populate the outlined reduction function using the elementwise reduction
5380 // function. Partial values are extracted from the type-erased array of
5381 // pointers to private variables.
5382 Error Err = populateReductionFunction(ReductionFunc, ReductionInfos, Builder,
5383 IsByRef, /*isGPU=*/false);
5384 if (Err)
5385 return Err;
5386
5387 if (!Builder.GetInsertBlock())
5388 return InsertPointTy();
5389
5390 Builder.SetInsertPoint(ContinuationBlock);
5391 return Builder.saveIP();
5392}
5393
5396 BodyGenCallbackTy BodyGenCB,
5397 FinalizeCallbackTy FiniCB) {
5398 if (!updateToLocation(Loc))
5399 return Loc.IP;
5400
5401 Directive OMPD = Directive::OMPD_master;
5402 uint32_t SrcLocStrSize;
5403 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5404 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5405 Value *ThreadId = getOrCreateThreadID(Ident);
5406 Value *Args[] = {Ident, ThreadId};
5407
5408 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_master);
5409 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5410
5411 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_master);
5412 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
5413
5414 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5415 /*Conditional*/ true, /*hasFinalize*/ true);
5416}
5417
5420 BodyGenCallbackTy BodyGenCB,
5421 FinalizeCallbackTy FiniCB, Value *Filter) {
5422 if (!updateToLocation(Loc))
5423 return Loc.IP;
5424
5425 Directive OMPD = Directive::OMPD_masked;
5426 uint32_t SrcLocStrSize;
5427 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
5428 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
5429 Value *ThreadId = getOrCreateThreadID(Ident);
5430 Value *Args[] = {Ident, ThreadId, Filter};
5431 Value *ArgsEnd[] = {Ident, ThreadId};
5432
5433 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_masked);
5434 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
5435
5436 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_masked);
5437 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, ArgsEnd);
5438
5439 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
5440 /*Conditional*/ true, /*hasFinalize*/ true);
5441}
5442
5444 llvm::FunctionCallee Callee,
5446 const llvm::Twine &Name) {
5447 llvm::CallInst *Call = Builder.CreateCall(
5448 Callee, Args, SmallVector<llvm::OperandBundleDef, 1>(), Name);
5449 Call->setDoesNotThrow();
5450 return Call;
5451}
5452
5453// Expects input basic block is dominated by BeforeScanBB.
5454// Once Scan directive is encountered, the code after scan directive should be
5455// dominated by AfterScanBB. Scan directive splits the code sequence to
5456// scan and input phase. Based on whether inclusive or exclusive
5457// clause is used in the scan directive and whether input loop or scan loop
5458// is lowered, it adds jumps to input and scan phase. First Scan loop is the
5459// input loop and second is the scan loop. The code generated handles only
5460// inclusive scans now.
5462 const LocationDescription &Loc, InsertPointTy AllocaIP,
5463 ArrayRef<llvm::Value *> ScanVars, ArrayRef<llvm::Type *> ScanVarsType,
5464 bool IsInclusive, ScanInfo *ScanRedInfo) {
5465 if (ScanRedInfo->OMPFirstScanLoop) {
5466 llvm::Error Err = emitScanBasedDirectiveDeclsIR(AllocaIP, ScanVars,
5467 ScanVarsType, ScanRedInfo);
5468 if (Err)
5469 return Err;
5470 }
5471 if (!updateToLocation(Loc))
5472 return Loc.IP;
5473
5474 llvm::Value *IV = ScanRedInfo->IV;
5475
5476 if (ScanRedInfo->OMPFirstScanLoop) {
5477 // Emit buffer[i] = red; at the end of the input phase.
5478 for (size_t i = 0; i < ScanVars.size(); i++) {
5479 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5480 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5481 Type *DestTy = ScanVarsType[i];
5482 Value *Val = Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5483 Value *Src = Builder.CreateLoad(DestTy, ScanVars[i]);
5484
5485 Builder.CreateStore(Src, Val);
5486 }
5487 }
5488 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5489 emitBlock(ScanRedInfo->OMPScanDispatch,
5490 Builder.GetInsertBlock()->getParent());
5491
5492 if (!ScanRedInfo->OMPFirstScanLoop) {
5493 IV = ScanRedInfo->IV;
5494 // Emit red = buffer[i]; at the entrance to the scan phase.
5495 // TODO: if exclusive scan, the red = buffer[i-1] needs to be updated.
5496 for (size_t i = 0; i < ScanVars.size(); i++) {
5497 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]];
5498 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5499 Type *DestTy = ScanVarsType[i];
5500 Value *SrcPtr =
5501 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5502 Value *Src = Builder.CreateLoad(DestTy, SrcPtr);
5503 Builder.CreateStore(Src, ScanVars[i]);
5504 }
5505 }
5506
5507 // TODO: Update it to CreateBr and remove dead blocks
5508 llvm::Value *CmpI = Builder.getInt1(true);
5509 if (ScanRedInfo->OMPFirstScanLoop == IsInclusive) {
5510 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPBeforeScanBlock,
5511 ScanRedInfo->OMPAfterScanBlock);
5512 } else {
5513 Builder.CreateCondBr(CmpI, ScanRedInfo->OMPAfterScanBlock,
5514 ScanRedInfo->OMPBeforeScanBlock);
5515 }
5516 emitBlock(ScanRedInfo->OMPAfterScanBlock,
5517 Builder.GetInsertBlock()->getParent());
5518 Builder.SetInsertPoint(ScanRedInfo->OMPAfterScanBlock);
5519 return Builder.saveIP();
5520}
5521
5522Error OpenMPIRBuilder::emitScanBasedDirectiveDeclsIR(
5523 InsertPointTy AllocaIP, ArrayRef<Value *> ScanVars,
5524 ArrayRef<Type *> ScanVarsType, ScanInfo *ScanRedInfo) {
5525
5526 Builder.restoreIP(AllocaIP);
5527 // Create the shared pointer at alloca IP.
5528 for (size_t i = 0; i < ScanVars.size(); i++) {
5529 llvm::Value *BuffPtr =
5530 Builder.CreateAlloca(Builder.getPtrTy(), nullptr, "vla");
5531 (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]] = BuffPtr;
5532 }
5533
5534 // Allocate temporary buffer by master thread
5535 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5536 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5537 Builder.restoreIP(CodeGenIP);
5538 Value *AllocSpan =
5539 Builder.CreateAdd(ScanRedInfo->Span, Builder.getInt32(1));
5540 for (size_t i = 0; i < ScanVars.size(); i++) {
5541 Type *IntPtrTy = Builder.getInt32Ty();
5542 Constant *Allocsize = ConstantExpr::getSizeOf(ScanVarsType[i]);
5543 Allocsize = ConstantExpr::getTruncOrBitCast(Allocsize, IntPtrTy);
5544 Value *Buff = Builder.CreateMalloc(IntPtrTy, ScanVarsType[i], Allocsize,
5545 AllocSpan, nullptr, "arr");
5546 Builder.CreateStore(Buff, (*(ScanRedInfo->ScanBuffPtrs))[ScanVars[i]]);
5547 }
5548 return Error::success();
5549 };
5550 // TODO: Perform finalization actions for variables. This has to be
5551 // called for variables which have destructors/finalizers.
5552 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5553
5554 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit->getTerminator());
5555 llvm::Value *FilterVal = Builder.getInt32(0);
5557 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5558
5559 if (!AfterIP)
5560 return AfterIP.takeError();
5561 Builder.restoreIP(*AfterIP);
5562 BasicBlock *InputBB = Builder.GetInsertBlock();
5563 if (InputBB->hasTerminator())
5564 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5565 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5566 if (!AfterIP)
5567 return AfterIP.takeError();
5568 Builder.restoreIP(*AfterIP);
5569
5570 return Error::success();
5571}
5572
5573Error OpenMPIRBuilder::emitScanBasedDirectiveFinalsIR(
5574 ArrayRef<ReductionInfo> ReductionInfos, ScanInfo *ScanRedInfo) {
5575 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5576 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5577 Builder.restoreIP(CodeGenIP);
5578 for (ReductionInfo RedInfo : ReductionInfos) {
5579 Value *PrivateVar = RedInfo.PrivateVariable;
5580 Value *OrigVar = RedInfo.Variable;
5581 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[PrivateVar];
5582 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5583
5584 Type *SrcTy = RedInfo.ElementType;
5585 Value *Val = Builder.CreateInBoundsGEP(SrcTy, Buff, ScanRedInfo->Span,
5586 "arrayOffset");
5587 Value *Src = Builder.CreateLoad(SrcTy, Val);
5588
5589 Builder.CreateStore(Src, OrigVar);
5590 Builder.CreateFree(Buff);
5591 }
5592 return Error::success();
5593 };
5594 // TODO: Perform finalization actions for variables. This has to be
5595 // called for variables which have destructors/finalizers.
5596 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5597
5598 if (Instruction *TI = ScanRedInfo->OMPScanFinish->getTerminatorOrNull())
5599 Builder.SetInsertPoint(TI);
5600 else
5601 Builder.SetInsertPoint(ScanRedInfo->OMPScanFinish);
5602
5603 llvm::Value *FilterVal = Builder.getInt32(0);
5605 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5606
5607 if (!AfterIP)
5608 return AfterIP.takeError();
5609 Builder.restoreIP(*AfterIP);
5610 BasicBlock *InputBB = Builder.GetInsertBlock();
5611 if (InputBB->hasTerminator())
5612 Builder.SetInsertPoint(Builder.GetInsertBlock()->getTerminator());
5613 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5614 if (!AfterIP)
5615 return AfterIP.takeError();
5616 Builder.restoreIP(*AfterIP);
5617 return Error::success();
5618}
5619
5621 const LocationDescription &Loc,
5623 ScanInfo *ScanRedInfo) {
5624
5625 if (!updateToLocation(Loc))
5626 return Loc.IP;
5627 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
5628 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
5629 Builder.restoreIP(CodeGenIP);
5630 Function *CurFn = Builder.GetInsertBlock()->getParent();
5631 // for (int k = 0; k <= ceil(log2(n)); ++k)
5632 llvm::BasicBlock *LoopBB =
5633 BasicBlock::Create(CurFn->getContext(), "omp.outer.log.scan.body");
5634 llvm::BasicBlock *ExitBB =
5635 splitBB(Builder, false, "omp.outer.log.scan.exit");
5637 Builder.GetInsertBlock()->getModule(),
5638 (llvm::Intrinsic::ID)llvm::Intrinsic::log2, Builder.getDoubleTy());
5639 llvm::BasicBlock *InputBB = Builder.GetInsertBlock();
5640 llvm::Value *Arg =
5641 Builder.CreateUIToFP(ScanRedInfo->Span, Builder.getDoubleTy());
5642 llvm::Value *LogVal = emitNoUnwindRuntimeCall(Builder, F, Arg, "");
5644 Builder.GetInsertBlock()->getModule(),
5645 (llvm::Intrinsic::ID)llvm::Intrinsic::ceil, Builder.getDoubleTy());
5646 LogVal = emitNoUnwindRuntimeCall(Builder, F, LogVal, "");
5647 LogVal = Builder.CreateFPToUI(LogVal, Builder.getInt32Ty());
5648 llvm::Value *NMin1 = Builder.CreateNUWSub(
5649 ScanRedInfo->Span,
5650 llvm::ConstantInt::get(ScanRedInfo->Span->getType(), 1));
5651 Builder.SetInsertPoint(InputBB);
5652 Builder.CreateBr(LoopBB);
5653 emitBlock(LoopBB, CurFn);
5654 Builder.SetInsertPoint(LoopBB);
5655
5656 PHINode *Counter = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5657 // size pow2k = 1;
5658 PHINode *Pow2K = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5659 Counter->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 0),
5660 InputBB);
5661 Pow2K->addIncoming(llvm::ConstantInt::get(Builder.getInt32Ty(), 1),
5662 InputBB);
5663 // for (size i = n - 1; i >= 2 ^ k; --i)
5664 // tmp[i] op= tmp[i-pow2k];
5665 llvm::BasicBlock *InnerLoopBB =
5666 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.body");
5667 llvm::BasicBlock *InnerExitBB =
5668 BasicBlock::Create(CurFn->getContext(), "omp.inner.log.scan.exit");
5669 llvm::Value *CmpI = Builder.CreateICmpUGE(NMin1, Pow2K);
5670 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5671 emitBlock(InnerLoopBB, CurFn);
5672 Builder.SetInsertPoint(InnerLoopBB);
5673 PHINode *IVal = Builder.CreatePHI(Builder.getInt32Ty(), 2);
5674 IVal->addIncoming(NMin1, LoopBB);
5675 for (ReductionInfo RedInfo : ReductionInfos) {
5676 Value *ReductionVal = RedInfo.PrivateVariable;
5677 Value *BuffPtr = (*(ScanRedInfo->ScanBuffPtrs))[ReductionVal];
5678 Value *Buff = Builder.CreateLoad(Builder.getPtrTy(), BuffPtr);
5679 Type *DestTy = RedInfo.ElementType;
5680 Value *IV = Builder.CreateAdd(IVal, Builder.getInt32(1));
5681 Value *LHSPtr =
5682 Builder.CreateInBoundsGEP(DestTy, Buff, IV, "arrayOffset");
5683 Value *OffsetIval = Builder.CreateNUWSub(IV, Pow2K);
5684 Value *RHSPtr =
5685 Builder.CreateInBoundsGEP(DestTy, Buff, OffsetIval, "arrayOffset");
5686 Value *LHS = Builder.CreateLoad(DestTy, LHSPtr);
5687 Value *RHS = Builder.CreateLoad(DestTy, RHSPtr);
5688 llvm::Value *Result;
5689 InsertPointOrErrorTy AfterIP =
5690 RedInfo.ReductionGen(Builder.saveIP(), LHS, RHS, Result);
5691 if (!AfterIP)
5692 return AfterIP.takeError();
5693 Builder.CreateStore(Result, LHSPtr);
5694 }
5695 llvm::Value *NextIVal = Builder.CreateNUWSub(
5696 IVal, llvm::ConstantInt::get(Builder.getInt32Ty(), 1));
5697 IVal->addIncoming(NextIVal, Builder.GetInsertBlock());
5698 CmpI = Builder.CreateICmpUGE(NextIVal, Pow2K);
5699 Builder.CreateCondBr(CmpI, InnerLoopBB, InnerExitBB);
5700 emitBlock(InnerExitBB, CurFn);
5701 llvm::Value *Next = Builder.CreateNUWAdd(
5702 Counter, llvm::ConstantInt::get(Counter->getType(), 1));
5703 Counter->addIncoming(Next, Builder.GetInsertBlock());
5704 // pow2k <<= 1;
5705 llvm::Value *NextPow2K = Builder.CreateShl(Pow2K, 1, "", /*HasNUW=*/true);
5706 Pow2K->addIncoming(NextPow2K, Builder.GetInsertBlock());
5707 llvm::Value *Cmp = Builder.CreateICmpNE(Next, LogVal);
5708 Builder.CreateCondBr(Cmp, LoopBB, ExitBB);
5709 Builder.SetInsertPoint(ExitBB->getFirstInsertionPt());
5710 return Error::success();
5711 };
5712
5713 // TODO: Perform finalization actions for variables. This has to be
5714 // called for variables which have destructors/finalizers.
5715 auto FiniCB = [&](InsertPointTy CodeGenIP) { return llvm::Error::success(); };
5716
5717 llvm::Value *FilterVal = Builder.getInt32(0);
5719 createMasked(Builder.saveIP(), BodyGenCB, FiniCB, FilterVal);
5720
5721 if (!AfterIP)
5722 return AfterIP.takeError();
5723 Builder.restoreIP(*AfterIP);
5724 AfterIP = createBarrier(Builder.saveIP(), llvm::omp::OMPD_barrier);
5725
5726 if (!AfterIP)
5727 return AfterIP.takeError();
5728 Builder.restoreIP(*AfterIP);
5729 Error Err = emitScanBasedDirectiveFinalsIR(ReductionInfos, ScanRedInfo);
5730 if (Err)
5731 return Err;
5732
5733 return AfterIP;
5734}
5735
5736Error OpenMPIRBuilder::emitScanBasedDirectiveIR(
5737 llvm::function_ref<Error()> InputLoopGen,
5738 llvm::function_ref<Error(LocationDescription Loc)> ScanLoopGen,
5739 ScanInfo *ScanRedInfo) {
5740
5741 {
5742 // Emit loop with input phase:
5743 // for (i: 0..<num_iters>) {
5744 // <input phase>;
5745 // buffer[i] = red;
5746 // }
5747 ScanRedInfo->OMPFirstScanLoop = true;
5748 Error Err = InputLoopGen();
5749 if (Err)
5750 return Err;
5751 }
5752 {
5753 // Emit loop with scan phase:
5754 // for (i: 0..<num_iters>) {
5755 // red = buffer[i];
5756 // <scan phase>;
5757 // }
5758 ScanRedInfo->OMPFirstScanLoop = false;
5759 Error Err = ScanLoopGen(Builder.saveIP());
5760 if (Err)
5761 return Err;
5762 }
5763 return Error::success();
5764}
5765
5766void OpenMPIRBuilder::createScanBBs(ScanInfo *ScanRedInfo) {
5767 Function *Fun = Builder.GetInsertBlock()->getParent();
5768 ScanRedInfo->OMPScanDispatch =
5769 BasicBlock::Create(Fun->getContext(), "omp.inscan.dispatch");
5770 ScanRedInfo->OMPAfterScanBlock =
5771 BasicBlock::Create(Fun->getContext(), "omp.after.scan.bb");
5772 ScanRedInfo->OMPBeforeScanBlock =
5773 BasicBlock::Create(Fun->getContext(), "omp.before.scan.bb");
5774 ScanRedInfo->OMPScanLoopExit =
5775 BasicBlock::Create(Fun->getContext(), "omp.scan.loop.exit");
5776}
5778 DebugLoc DL, Value *TripCount, Function *F, BasicBlock *PreInsertBefore,
5779 BasicBlock *PostInsertBefore, const Twine &Name, bool IsCollapsed) {
5780 Module *M = F->getParent();
5781 LLVMContext &Ctx = M->getContext();
5782 Type *IndVarTy = TripCount->getType();
5783
5784 // Create the basic block structure.
5785 BasicBlock *Preheader =
5786 BasicBlock::Create(Ctx, "omp_" + Name + ".preheader", F, PreInsertBefore);
5787 BasicBlock *Header =
5788 BasicBlock::Create(Ctx, "omp_" + Name + ".header", F, PreInsertBefore);
5789 BasicBlock *Cond =
5790 BasicBlock::Create(Ctx, "omp_" + Name + ".cond", F, PreInsertBefore);
5791 BasicBlock *Body =
5792 BasicBlock::Create(Ctx, "omp_" + Name + ".body", F, PreInsertBefore);
5793 BasicBlock *Latch =
5794 BasicBlock::Create(Ctx, "omp_" + Name + ".inc", F, PostInsertBefore);
5795 BasicBlock *Exit =
5796 BasicBlock::Create(Ctx, "omp_" + Name + ".exit", F, PostInsertBefore);
5797 BasicBlock *After =
5798 BasicBlock::Create(Ctx, "omp_" + Name + ".after", F, PostInsertBefore);
5799
5800 // Use specified DebugLoc for new instructions.
5801 Builder.SetCurrentDebugLocation(DL);
5802
5803 Builder.SetInsertPoint(Preheader);
5804 Builder.CreateBr(Header);
5805
5806 Builder.SetInsertPoint(Header);
5807 PHINode *IndVarPHI = Builder.CreatePHI(IndVarTy, 2, "omp_" + Name + ".iv");
5808 IndVarPHI->addIncoming(ConstantInt::get(IndVarTy, 0), Preheader);
5809 Builder.CreateBr(Cond);
5810
5811 Builder.SetInsertPoint(Cond);
5812 Value *Cmp =
5813 Builder.CreateICmpULT(IndVarPHI, TripCount, "omp_" + Name + ".cmp");
5814 Builder.CreateCondBr(Cmp, Body, Exit);
5815
5816 Builder.SetInsertPoint(Body);
5817 Builder.CreateBr(Latch);
5818
5819 Builder.SetInsertPoint(Latch);
5820 // Decide whether the induction variable increment can carry nsw.
5821 //
5822 // Single loops: nsw is always kept (matching Clang). Any Fortran program
5823 // whose trip count overflows i32 is non-conforming per F2018 11.1.7.4.1, so
5824 // for valid programs 0 <= count <= INT_MAX always holds.
5825 //
5826 // Collapsed loops: the trip count is a product that can overflow i32 even for
5827 // a conforming program, so nsw is kept only when the product is a constant
5828 // that provably fits, dropped otherwise.
5829 bool HasNSW = Config.hasNoSignedWrap();
5830 if (HasNSW) {
5831 if (auto *CI = dyn_cast<ConstantInt>(TripCount)) {
5832 unsigned BitWidth = CI->getType()->getIntegerBitWidth();
5834 if (CI->getValue().ugt(SignedMax))
5835 HasNSW = false;
5836 } else if (IsCollapsed) {
5837 HasNSW = false;
5838 }
5839 }
5840 Value *Next =
5841 Builder.CreateAdd(IndVarPHI, ConstantInt::get(IndVarTy, 1),
5842 "omp_" + Name + ".next", /*HasNUW=*/true, HasNSW);
5843 Builder.CreateBr(Header);
5844 IndVarPHI->addIncoming(Next, Latch);
5845
5846 Builder.SetInsertPoint(Exit);
5847 Builder.CreateBr(After);
5848
5849 // Remember and return the canonical control flow.
5850 LoopInfos.emplace_front();
5851 CanonicalLoopInfo *CL = &LoopInfos.front();
5852
5853 CL->Header = Header;
5854 CL->Cond = Cond;
5855 CL->Latch = Latch;
5856 CL->Exit = Exit;
5857
5858#ifndef NDEBUG
5859 CL->assertOK();
5860#endif
5861 return CL;
5862}
5863
5866 LoopBodyGenCallbackTy BodyGenCB,
5867 Value *TripCount, const Twine &Name) {
5868 BasicBlock *BB = Loc.IP.getBlock();
5869 BasicBlock *NextBB = BB->getNextNode();
5870
5871 CanonicalLoopInfo *CL = createLoopSkeleton(Loc.DL, TripCount, BB->getParent(),
5872 NextBB, NextBB, Name);
5873 BasicBlock *After = CL->getAfter();
5874
5875 // If location is not set, don't connect the loop.
5876 if (updateToLocation(Loc)) {
5877 // Split the loop at the insertion point: Branch to the preheader and move
5878 // every following instruction to after the loop (the After BB). Also, the
5879 // new successor is the loop's after block.
5880 spliceBB(Builder, After, /*CreateBranch=*/false);
5881 Builder.CreateBr(CL->getPreheader());
5882 }
5883
5884 // Emit the body content. We do it after connecting the loop to the CFG to
5885 // avoid that the callback encounters degenerate BBs.
5886 if (Error Err = BodyGenCB(CL->getBodyIP(), CL->getIndVar()))
5887 return Err;
5888
5889#ifndef NDEBUG
5890 CL->assertOK();
5891#endif
5892 return CL;
5893}
5894
5896 ScanInfos.emplace_front();
5897 ScanInfo *Result = &ScanInfos.front();
5898 return Result;
5899}
5900
5904 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
5905 InsertPointTy ComputeIP, const Twine &Name, ScanInfo *ScanRedInfo) {
5906 LocationDescription ComputeLoc =
5907 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
5908 updateToLocation(ComputeLoc);
5909
5911
5913 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
5914 ScanRedInfo->Span = TripCount;
5915 ScanRedInfo->OMPScanInit = splitBB(Builder, true, "scan.init");
5916 Builder.SetInsertPoint(ScanRedInfo->OMPScanInit);
5917
5918 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
5919 Builder.restoreIP(CodeGenIP);
5920 ScanRedInfo->IV = IV;
5921 createScanBBs(ScanRedInfo);
5922 BasicBlock *InputBlock = Builder.GetInsertBlock();
5923 Instruction *Terminator = InputBlock->getTerminator();
5924 assert(Terminator->getNumSuccessors() == 1);
5925 BasicBlock *ContinueBlock = Terminator->getSuccessor(0);
5926 Terminator->setSuccessor(0, ScanRedInfo->OMPScanDispatch);
5927 emitBlock(ScanRedInfo->OMPBeforeScanBlock,
5928 Builder.GetInsertBlock()->getParent());
5929 Builder.CreateBr(ScanRedInfo->OMPScanLoopExit);
5930 emitBlock(ScanRedInfo->OMPScanLoopExit,
5931 Builder.GetInsertBlock()->getParent());
5932 Builder.CreateBr(ContinueBlock);
5933 Builder.SetInsertPoint(
5934 ScanRedInfo->OMPBeforeScanBlock->getFirstInsertionPt());
5935 return BodyGenCB(Builder.saveIP(), IV);
5936 };
5937
5938 const auto &&InputLoopGen = [&]() -> Error {
5940 Builder.saveIP(), BodyGen, Start, Stop, Step, IsSigned, InclusiveStop,
5941 ComputeIP, Name, true, ScanRedInfo);
5942 if (!LoopInfo)
5943 return LoopInfo.takeError();
5944 Result.push_back(*LoopInfo);
5945 Builder.restoreIP((*LoopInfo)->getAfterIP());
5946 return Error::success();
5947 };
5948 const auto &&ScanLoopGen = [&](LocationDescription Loc) -> Error {
5950 createCanonicalLoop(Loc, BodyGen, Start, Stop, Step, IsSigned,
5951 InclusiveStop, ComputeIP, Name, true, ScanRedInfo);
5952 if (!LoopInfo)
5953 return LoopInfo.takeError();
5954 Result.push_back(*LoopInfo);
5955 Builder.restoreIP((*LoopInfo)->getAfterIP());
5956 ScanRedInfo->OMPScanFinish = Builder.GetInsertBlock();
5957 return Error::success();
5958 };
5959 Error Err = emitScanBasedDirectiveIR(InputLoopGen, ScanLoopGen, ScanRedInfo);
5960 if (Err)
5961 return Err;
5962 return Result;
5963}
5964
5966 const LocationDescription &Loc, Value *Start, Value *Stop, Value *Step,
5967 bool IsSigned, bool InclusiveStop, const Twine &Name) {
5968
5969 // Consider the following difficulties (assuming 8-bit signed integers):
5970 // * Adding \p Step to the loop counter which passes \p Stop may overflow:
5971 // DO I = 1, 100, 50
5972 /// * A \p Step of INT_MIN cannot not be normalized to a positive direction:
5973 // DO I = 100, 0, -128
5974
5975 // Start, Stop and Step must be of the same integer type.
5976 auto *IndVarTy = cast<IntegerType>(Start->getType());
5977 assert(IndVarTy == Stop->getType() && "Stop type mismatch");
5978 assert(IndVarTy == Step->getType() && "Step type mismatch");
5979
5981
5982 ConstantInt *Zero = ConstantInt::get(IndVarTy, 0);
5983 ConstantInt *One = ConstantInt::get(IndVarTy, 1);
5984
5985 // Like Step, but always positive.
5986 Value *Incr = Step;
5987
5988 // Distance between Start and Stop; always positive.
5989 Value *Span;
5990
5991 // Condition whether there are no iterations are executed at all, e.g. because
5992 // UB < LB.
5993 Value *ZeroCmp;
5994
5995 if (IsSigned) {
5996 // Ensure that increment is positive. If not, negate and invert LB and UB.
5997 Value *IsNeg = Builder.CreateICmpSLT(Step, Zero);
5998 Incr = Builder.CreateSelect(IsNeg, Builder.CreateNeg(Step), Step);
5999 Value *LB = Builder.CreateSelect(IsNeg, Stop, Start);
6000 Value *UB = Builder.CreateSelect(IsNeg, Start, Stop);
6001 Span = Builder.CreateSub(UB, LB, "", false, true);
6002 ZeroCmp = Builder.CreateICmp(
6003 InclusiveStop ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, UB, LB);
6004 } else {
6005 Span = Builder.CreateSub(Stop, Start, "", true);
6006 ZeroCmp = Builder.CreateICmp(
6007 InclusiveStop ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE, Stop, Start);
6008 }
6009
6010 Value *CountIfLooping;
6011 if (InclusiveStop) {
6012 CountIfLooping = Builder.CreateAdd(Builder.CreateUDiv(Span, Incr), One);
6013 } else {
6014 // Avoid incrementing past stop since it could overflow.
6015 Value *CountIfTwo = Builder.CreateAdd(
6016 Builder.CreateUDiv(Builder.CreateSub(Span, One), Incr), One);
6017 Value *OneCmp = Builder.CreateICmp(CmpInst::ICMP_ULE, Span, Incr);
6018 CountIfLooping = Builder.CreateSelect(OneCmp, One, CountIfTwo);
6019 }
6020
6021 return Builder.CreateSelect(ZeroCmp, Zero, CountIfLooping,
6022 "omp_" + Name + ".tripcount");
6023}
6024
6027 Value *Start, Value *Stop, Value *Step, bool IsSigned, bool InclusiveStop,
6028 InsertPointTy ComputeIP, const Twine &Name, bool InScan,
6029 ScanInfo *ScanRedInfo) {
6030 LocationDescription ComputeLoc =
6031 ComputeIP.isSet() ? LocationDescription(ComputeIP, Loc.DL) : Loc;
6032
6034 ComputeLoc, Start, Stop, Step, IsSigned, InclusiveStop, Name);
6035
6036 auto BodyGen = [=](InsertPointTy CodeGenIP, Value *IV) {
6037 Builder.restoreIP(CodeGenIP);
6038 Value *Span = Builder.CreateMul(IV, Step, "", /*HasNUW=*/false,
6039 /*HasNSW=*/Config.hasNoSignedWrap());
6040 Value *IndVar = Builder.CreateAdd(Span, Start, "", /*HasNUW=*/false,
6041 /*HasNSW=*/Config.hasNoSignedWrap());
6042 if (InScan)
6043 ScanRedInfo->IV = IndVar;
6044 return BodyGenCB(Builder.saveIP(), IndVar);
6045 };
6046 LocationDescription LoopLoc =
6047 ComputeIP.isSet()
6048 ? Loc
6049 : LocationDescription(Builder.saveIP(),
6050 Builder.getCurrentDebugLocation());
6051 return createCanonicalLoop(LoopLoc, BodyGen, TripCount, Name);
6052}
6053
6054// Returns an LLVM function to call for initializing loop bounds using OpenMP
6055// static scheduling for composite `distribute parallel for` depending on
6056// `type`. Only i32 and i64 are supported by the runtime. Always interpret
6057// integers as unsigned similarly to CanonicalLoopInfo.
6058static FunctionCallee
6060 OpenMPIRBuilder &OMPBuilder) {
6061 unsigned Bitwidth = Ty->getIntegerBitWidth();
6062 if (Bitwidth == 32)
6063 return OMPBuilder.getOrCreateRuntimeFunction(
6064 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_4u);
6065 if (Bitwidth == 64)
6066 return OMPBuilder.getOrCreateRuntimeFunction(
6067 M, omp::RuntimeFunction::OMPRTL___kmpc_dist_for_static_init_8u);
6068 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6069}
6070
6071// Returns an LLVM function to call for initializing loop bounds using OpenMP
6072// static scheduling depending on `type`. Only i32 and i64 are supported by the
6073// runtime. Always interpret integers as unsigned similarly to
6074// CanonicalLoopInfo.
6076 OpenMPIRBuilder &OMPBuilder) {
6077 unsigned Bitwidth = Ty->getIntegerBitWidth();
6078 if (Bitwidth == 32)
6079 return OMPBuilder.getOrCreateRuntimeFunction(
6080 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_4u);
6081 if (Bitwidth == 64)
6082 return OMPBuilder.getOrCreateRuntimeFunction(
6083 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_init_8u);
6084 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6085}
6086
6087OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::applyStaticWorkshareLoop(
6088 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6089 WorksharingLoopType LoopType, bool NeedsBarrier, bool HasDistSchedule,
6090 OMPScheduleType DistScheduleSchedType) {
6091 assert(CLI->isValid() && "Requires a valid canonical loop");
6092 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6093 "Require dedicated allocate IP");
6094
6095 // Set up the source location value for OpenMP runtime.
6096 Builder.restoreIP(CLI->getPreheaderIP());
6097 Builder.SetCurrentDebugLocation(DL);
6098
6099 uint32_t SrcLocStrSize;
6100 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6102 switch (LoopType) {
6103 case WorksharingLoopType::ForStaticLoop:
6104 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6105 break;
6106 case WorksharingLoopType::DistributeStaticLoop:
6107 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6108 break;
6109 case WorksharingLoopType::DistributeForStaticLoop:
6110 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6111 break;
6112 }
6113 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6114
6115 // Declare useful OpenMP runtime functions.
6116 Value *IV = CLI->getIndVar();
6117 Type *IVTy = IV->getType();
6118 FunctionCallee StaticInit =
6119 LoopType == WorksharingLoopType::DistributeForStaticLoop
6120 ? getKmpcDistForStaticInitForType(IVTy, M, *this)
6121 : getKmpcForStaticInitForType(IVTy, M, *this);
6122 FunctionCallee StaticFini =
6123 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6124
6125 // Allocate space for computed loop bounds as expected by the "init" function.
6126 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6127
6128 Type *I32Type = Type::getInt32Ty(M.getContext());
6129 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6130 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6131 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6132 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6133 CLI->setLastIter(PLastIter);
6134
6135 // At the end of the preheader, prepare for calling the "init" function by
6136 // storing the current loop bounds into the allocated space. A canonical loop
6137 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6138 // and produces an inclusive upper bound.
6139 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6140 Constant *Zero = ConstantInt::get(IVTy, 0);
6141 Constant *One = ConstantInt::get(IVTy, 1);
6142 Builder.CreateStore(Zero, PLowerBound);
6143 Value *UpperBound = Builder.CreateSub(CLI->getTripCount(), One);
6144 Builder.CreateStore(UpperBound, PUpperBound);
6145 Builder.CreateStore(One, PStride);
6146
6147 Value *ThreadNum =
6148 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6149
6150 OMPScheduleType SchedType =
6151 (LoopType == WorksharingLoopType::DistributeStaticLoop)
6152 ? OMPScheduleType::OrderedDistribute
6154 Constant *SchedulingType =
6155 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6156
6157 // Call the "init" function and update the trip count of the loop with the
6158 // value it produced.
6159 auto BuildInitCall = [LoopType, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6160 PUpperBound, IVTy, PStride, One, Zero, StaticInit,
6161 this](Value *SchedulingType, auto &Builder) {
6162 SmallVector<Value *, 10> Args({SrcLoc, ThreadNum, SchedulingType, PLastIter,
6163 PLowerBound, PUpperBound});
6164 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6165 Value *PDistUpperBound =
6166 Builder.CreateAlloca(IVTy, nullptr, "p.distupperbound");
6167 Args.push_back(PDistUpperBound);
6168 }
6169 Args.append({PStride, One, Zero});
6170 createRuntimeFunctionCall(StaticInit, Args);
6171 };
6172 BuildInitCall(SchedulingType, Builder);
6173 if (HasDistSchedule &&
6174 LoopType != WorksharingLoopType::DistributeStaticLoop) {
6175 Constant *DistScheduleSchedType = ConstantInt::get(
6176 I32Type, static_cast<int>(omp::OMPScheduleType::OrderedDistribute));
6177 // We want to emit a second init function call for the dist_schedule clause
6178 // to the Distribute construct. This should only be done however if a
6179 // Workshare Loop is nested within a Distribute Construct
6180 BuildInitCall(DistScheduleSchedType, Builder);
6181 }
6182 Value *LowerBound = Builder.CreateLoad(IVTy, PLowerBound);
6183 Value *InclusiveUpperBound = Builder.CreateLoad(IVTy, PUpperBound);
6184 Value *TripCountMinusOne = Builder.CreateSub(InclusiveUpperBound, LowerBound);
6185 Value *TripCount = Builder.CreateAdd(TripCountMinusOne, One);
6186 CLI->setTripCount(TripCount);
6187
6188 // Update all uses of the induction variable except the one in the condition
6189 // block that compares it with the actual upper bound, and the increment in
6190 // the latch block.
6191
6192 CLI->mapIndVar([&](Instruction *OldIV) -> Value * {
6193 Builder.SetInsertPoint(CLI->getBody(),
6194 CLI->getBody()->getFirstInsertionPt());
6195 Builder.SetCurrentDebugLocation(DL);
6196 return Builder.CreateAdd(OldIV, LowerBound, "", /*HasNUW=*/false,
6197 /*HasNSW=*/Config.hasNoSignedWrap());
6198 });
6199
6200 // In the "exit" block, call the "fini" function.
6201 Builder.SetInsertPoint(CLI->getExit(),
6202 CLI->getExit()->getTerminator()->getIterator());
6203 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6204
6205 // Add the barrier if requested.
6206 if (NeedsBarrier) {
6207 InsertPointOrErrorTy BarrierIP =
6209 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6210 /* CheckCancelFlag */ false);
6211 if (!BarrierIP)
6212 return BarrierIP.takeError();
6213 }
6214
6215 InsertPointTy AfterIP = CLI->getAfterIP();
6216 CLI->invalidate();
6217
6218 return AfterIP;
6219}
6220
6221static void addAccessGroupMetadata(BasicBlock *Block, MDNode *AccessGroup,
6222 LoopInfo &LI);
6223static void addLoopMetadata(CanonicalLoopInfo *Loop,
6225
6227 LLVMContext &Ctx, Loop *Loop,
6229 SmallVector<Metadata *> &LoopMDList) {
6230 SmallSet<BasicBlock *, 8> Reachable;
6231
6232 // Get the basic blocks from the loop in which memref instructions
6233 // can be found.
6234 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
6235 // preferably without running any passes.
6236 for (BasicBlock *Block : Loop->getBlocks()) {
6237 if (Block == CLI->getCond() || Block == CLI->getHeader())
6238 continue;
6239 Reachable.insert(Block);
6240 }
6241
6242 // Add access group metadata to memory-access instructions.
6244 for (BasicBlock *BB : Reachable)
6246 // TODO: If the loop has existing parallel access metadata, have
6247 // to combine two lists.
6248 LoopMDList.push_back(MDNode::get(
6249 Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccessGroup}));
6250}
6251
6253OpenMPIRBuilder::applyStaticChunkedWorkshareLoop(
6254 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6255 bool NeedsBarrier, Value *ChunkSize, OMPScheduleType SchedType,
6256 Value *DistScheduleChunkSize, OMPScheduleType DistScheduleSchedType) {
6257 assert(CLI->isValid() && "Requires a valid canonical loop");
6258 assert((ChunkSize || DistScheduleChunkSize) && "Chunk size is required");
6259
6260 LLVMContext &Ctx = CLI->getFunction()->getContext();
6261 Value *IV = CLI->getIndVar();
6262 Value *OrigTripCount = CLI->getTripCount();
6263 Type *IVTy = IV->getType();
6264 assert(IVTy->getIntegerBitWidth() <= 64 &&
6265 "Max supported tripcount bitwidth is 64 bits");
6266 Type *InternalIVTy = IVTy->getIntegerBitWidth() <= 32 ? Type::getInt32Ty(Ctx)
6267 : Type::getInt64Ty(Ctx);
6268 Type *I32Type = Type::getInt32Ty(M.getContext());
6269 Constant *Zero = ConstantInt::get(InternalIVTy, 0);
6270 Constant *One = ConstantInt::get(InternalIVTy, 1);
6271
6272 Function *F = CLI->getFunction();
6273 // Blocks must have terminators.
6274 // FIXME: Don't run analyses on incomplete/invalid IR.
6275 SmallVector<Instruction *> UIs;
6276 for (BasicBlock &BB : *F)
6277 if (!BB.hasTerminator())
6278 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
6280 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
6281 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
6282 LoopAnalysis LIA;
6283 LoopInfo &&LI = LIA.run(*F, FAM);
6284 for (Instruction *I : UIs)
6285 I->eraseFromParent();
6286 Loop *L = LI.getLoopFor(CLI->getHeader());
6287 SmallVector<Metadata *> LoopMDList;
6288 if (ChunkSize || DistScheduleChunkSize)
6289 applyParallelAccessesMetadata(CLI, Ctx, L, LI, LoopMDList);
6290 addLoopMetadata(CLI, LoopMDList);
6291
6292 // Declare useful OpenMP runtime functions.
6293 FunctionCallee StaticInit =
6294 getKmpcForStaticInitForType(InternalIVTy, M, *this);
6295 FunctionCallee StaticFini =
6296 getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_for_static_fini);
6297
6298 // Allocate space for computed loop bounds as expected by the "init" function.
6299 Builder.restoreIP(AllocaIP);
6300 Builder.SetCurrentDebugLocation(DL);
6301 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6302 Value *PLowerBound =
6303 Builder.CreateAlloca(InternalIVTy, nullptr, "p.lowerbound");
6304 Value *PUpperBound =
6305 Builder.CreateAlloca(InternalIVTy, nullptr, "p.upperbound");
6306 Value *PStride = Builder.CreateAlloca(InternalIVTy, nullptr, "p.stride");
6307 CLI->setLastIter(PLastIter);
6308
6309 // Set up the source location value for the OpenMP runtime.
6310 Builder.restoreIP(CLI->getPreheaderIP());
6311 Builder.SetCurrentDebugLocation(DL);
6312
6313 // TODO: Detect overflow in ubsan or max-out with current tripcount.
6314 Value *CastedChunkSize = Builder.CreateZExtOrTrunc(
6315 ChunkSize ? ChunkSize : Zero, InternalIVTy, "chunksize");
6316 Value *CastedDistScheduleChunkSize = Builder.CreateZExtOrTrunc(
6317 DistScheduleChunkSize ? DistScheduleChunkSize : Zero, InternalIVTy,
6318 "distschedulechunksize");
6319 Value *CastedTripCount =
6320 Builder.CreateZExt(OrigTripCount, InternalIVTy, "tripcount");
6321
6322 Constant *SchedulingType =
6323 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6324 Constant *DistSchedulingType =
6325 ConstantInt::get(I32Type, static_cast<int>(DistScheduleSchedType));
6326 Builder.CreateStore(Zero, PLowerBound);
6327 Value *OrigUpperBound = Builder.CreateSub(CastedTripCount, One);
6328 Value *IsTripCountZero = Builder.CreateICmpEQ(CastedTripCount, Zero);
6329 Value *UpperBound =
6330 Builder.CreateSelect(IsTripCountZero, Zero, OrigUpperBound);
6331 Builder.CreateStore(UpperBound, PUpperBound);
6332 Builder.CreateStore(One, PStride);
6333
6334 // Call the "init" function and update the trip count of the loop with the
6335 // value it produced.
6336 uint32_t SrcLocStrSize;
6337 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6338 IdentFlag Flag = OMP_IDENT_FLAG_WORK_LOOP;
6339 if (DistScheduleSchedType != OMPScheduleType::None) {
6340 Flag |= OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6341 }
6342 Value *SrcLoc = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6343 Value *ThreadNum =
6344 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6345 auto BuildInitCall = [StaticInit, SrcLoc, ThreadNum, PLastIter, PLowerBound,
6346 PUpperBound, PStride, One,
6347 this](Value *SchedulingType, Value *ChunkSize,
6348 auto &Builder) {
6350 StaticInit, {/*loc=*/SrcLoc, /*global_tid=*/ThreadNum,
6351 /*schedtype=*/SchedulingType, /*plastiter=*/PLastIter,
6352 /*plower=*/PLowerBound, /*pupper=*/PUpperBound,
6353 /*pstride=*/PStride, /*incr=*/One,
6354 /*chunk=*/ChunkSize});
6355 };
6356 BuildInitCall(SchedulingType, CastedChunkSize, Builder);
6357 if (DistScheduleSchedType != OMPScheduleType::None &&
6358 SchedType != OMPScheduleType::OrderedDistributeChunked &&
6359 SchedType != OMPScheduleType::OrderedDistribute) {
6360 // We want to emit a second init function call for the dist_schedule clause
6361 // to the Distribute construct. This should only be done however if a
6362 // Workshare Loop is nested within a Distribute Construct
6363 BuildInitCall(DistSchedulingType, CastedDistScheduleChunkSize, Builder);
6364 }
6365
6366 // Load values written by the "init" function.
6367 Value *FirstChunkStart =
6368 Builder.CreateLoad(InternalIVTy, PLowerBound, "omp_firstchunk.lb");
6369 Value *FirstChunkStop =
6370 Builder.CreateLoad(InternalIVTy, PUpperBound, "omp_firstchunk.ub");
6371 Value *FirstChunkEnd = Builder.CreateAdd(FirstChunkStop, One);
6372 Value *ChunkRange =
6373 Builder.CreateSub(FirstChunkEnd, FirstChunkStart, "omp_chunk.range");
6374 Value *NextChunkStride =
6375 Builder.CreateLoad(InternalIVTy, PStride, "omp_dispatch.stride");
6376
6377 // Create outer "dispatch" loop for enumerating the chunks.
6378 BasicBlock *DispatchEnter = splitBB(Builder, true);
6379 Value *DispatchCounter;
6380
6381 // It is safe to assume this didn't return an error because the callback
6382 // passed into createCanonicalLoop is the only possible error source, and it
6383 // always returns success.
6384 CanonicalLoopInfo *DispatchCLI = cantFail(createCanonicalLoop(
6385 {Builder.saveIP(), DL},
6386 [&](InsertPointTy BodyIP, Value *Counter) {
6387 DispatchCounter = Counter;
6388 return Error::success();
6389 },
6390 FirstChunkStart, CastedTripCount, NextChunkStride,
6391 /*IsSigned=*/false, /*InclusiveStop=*/false, /*ComputeIP=*/{},
6392 "dispatch"));
6393
6394 // Remember the BasicBlocks of the dispatch loop we need, then invalidate to
6395 // not have to preserve the canonical invariant.
6396 BasicBlock *DispatchBody = DispatchCLI->getBody();
6397 BasicBlock *DispatchLatch = DispatchCLI->getLatch();
6398 BasicBlock *DispatchExit = DispatchCLI->getExit();
6399 BasicBlock *DispatchAfter = DispatchCLI->getAfter();
6400 DispatchCLI->invalidate();
6401
6402 // Rewire the original loop to become the chunk loop inside the dispatch loop.
6403 redirectTo(DispatchAfter, CLI->getAfter(), DL);
6404 redirectTo(CLI->getExit(), DispatchLatch, DL);
6405 redirectTo(DispatchBody, DispatchEnter, DL);
6406
6407 // Prepare the prolog of the chunk loop.
6408 Builder.restoreIP(CLI->getPreheaderIP());
6409 Builder.SetCurrentDebugLocation(DL);
6410
6411 // Compute the number of iterations of the chunk loop.
6412 Builder.SetInsertPoint(CLI->getPreheader()->getTerminator());
6413 Value *ChunkEnd = Builder.CreateAdd(DispatchCounter, ChunkRange);
6414 Value *IsLastChunk =
6415 Builder.CreateICmpUGE(ChunkEnd, CastedTripCount, "omp_chunk.is_last");
6416 Value *CountUntilOrigTripCount =
6417 Builder.CreateSub(CastedTripCount, DispatchCounter);
6418 Value *ChunkTripCount = Builder.CreateSelect(
6419 IsLastChunk, CountUntilOrigTripCount, ChunkRange, "omp_chunk.tripcount");
6420 Value *BackcastedChunkTC =
6421 Builder.CreateTrunc(ChunkTripCount, IVTy, "omp_chunk.tripcount.trunc");
6422 CLI->setTripCount(BackcastedChunkTC);
6423
6424 // Update all uses of the induction variable except the one in the condition
6425 // block that compares it with the actual upper bound, and the increment in
6426 // the latch block.
6427 Value *BackcastedDispatchCounter =
6428 Builder.CreateTrunc(DispatchCounter, IVTy, "omp_dispatch.iv.trunc");
6429 CLI->mapIndVar([&](Instruction *) -> Value * {
6430 Builder.restoreIP(CLI->getBodyIP());
6431 return Builder.CreateAdd(IV, BackcastedDispatchCounter);
6432 });
6433
6434 // In the "exit" block, call the "fini" function.
6435 Builder.SetInsertPoint(DispatchExit, DispatchExit->getFirstInsertionPt());
6436 createRuntimeFunctionCall(StaticFini, {SrcLoc, ThreadNum});
6437
6438 // Add the barrier if requested.
6439 if (NeedsBarrier) {
6440 InsertPointOrErrorTy AfterIP =
6441 createBarrier(LocationDescription(Builder.saveIP(), DL), OMPD_for,
6442 /*ForceSimpleCall=*/false, /*CheckCancelFlag=*/false);
6443 if (!AfterIP)
6444 return AfterIP.takeError();
6445 }
6446
6447#ifndef NDEBUG
6448 // Even though we currently do not support applying additional methods to it,
6449 // the chunk loop should remain a canonical loop.
6450 CLI->assertOK();
6451#endif
6452
6453 return InsertPointTy(DispatchAfter, DispatchAfter->getFirstInsertionPt());
6454}
6455
6456// Returns an LLVM function to call for executing an OpenMP static worksharing
6457// for loop depending on `type`. Only i32 and i64 are supported by the runtime.
6458// Always interpret integers as unsigned similarly to CanonicalLoopInfo.
6459static FunctionCallee
6461 WorksharingLoopType LoopType) {
6462 unsigned Bitwidth = Ty->getIntegerBitWidth();
6463 Module &M = OMPBuilder->M;
6464 switch (LoopType) {
6465 case WorksharingLoopType::ForStaticLoop:
6466 if (Bitwidth == 32)
6467 return OMPBuilder->getOrCreateRuntimeFunction(
6468 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_4u);
6469 if (Bitwidth == 64)
6470 return OMPBuilder->getOrCreateRuntimeFunction(
6471 M, omp::RuntimeFunction::OMPRTL___kmpc_for_static_loop_8u);
6472 break;
6473 case WorksharingLoopType::DistributeStaticLoop:
6474 if (Bitwidth == 32)
6475 return OMPBuilder->getOrCreateRuntimeFunction(
6476 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_4u);
6477 if (Bitwidth == 64)
6478 return OMPBuilder->getOrCreateRuntimeFunction(
6479 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_static_loop_8u);
6480 break;
6481 case WorksharingLoopType::DistributeForStaticLoop:
6482 if (Bitwidth == 32)
6483 return OMPBuilder->getOrCreateRuntimeFunction(
6484 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_4u);
6485 if (Bitwidth == 64)
6486 return OMPBuilder->getOrCreateRuntimeFunction(
6487 M, omp::RuntimeFunction::OMPRTL___kmpc_distribute_for_static_loop_8u);
6488 break;
6489 }
6490 if (Bitwidth != 32 && Bitwidth != 64) {
6491 llvm_unreachable("Unknown OpenMP loop iterator bitwidth");
6492 }
6493 llvm_unreachable("Unknown type of OpenMP worksharing loop");
6494}
6495
6496// Inserts a call to proper OpenMP Device RTL function which handles
6497// loop worksharing.
6499 WorksharingLoopType LoopType,
6500 BasicBlock *InsertBlock, Value *Ident,
6501 Value *LoopBodyArg, Value *TripCount,
6502 Function &LoopBodyFn, bool NoLoop) {
6503 Type *TripCountTy = TripCount->getType();
6504 Module &M = OMPBuilder->M;
6505 IRBuilder<> &Builder = OMPBuilder->Builder;
6506 FunctionCallee RTLFn =
6507 getKmpcForStaticLoopForType(TripCountTy, OMPBuilder, LoopType);
6508 SmallVector<Value *, 8> RealArgs;
6509 RealArgs.push_back(Ident);
6510 RealArgs.push_back(&LoopBodyFn);
6511 RealArgs.push_back(LoopBodyArg);
6512 RealArgs.push_back(TripCount);
6513 if (LoopType == WorksharingLoopType::DistributeStaticLoop) {
6514 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6515 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6516 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6517 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6518 return;
6519 }
6520 FunctionCallee RTLNumThreads = OMPBuilder->getOrCreateRuntimeFunction(
6521 M, omp::RuntimeFunction::OMPRTL_omp_get_num_threads);
6522 Builder.restoreIP({InsertBlock, std::prev(InsertBlock->end())});
6523 Value *NumThreads = OMPBuilder->createRuntimeFunctionCall(RTLNumThreads, {});
6524
6525 RealArgs.push_back(
6526 Builder.CreateZExtOrTrunc(NumThreads, TripCountTy, "num.threads.cast"));
6527 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6528 if (LoopType == WorksharingLoopType::DistributeForStaticLoop) {
6529 RealArgs.push_back(ConstantInt::get(TripCountTy, 0));
6530 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), NoLoop));
6531 } else {
6532 RealArgs.push_back(ConstantInt::get(Builder.getInt8Ty(), 0));
6533 }
6534
6535 OMPBuilder->createRuntimeFunctionCall(RTLFn, RealArgs);
6536}
6537
6539 OpenMPIRBuilder *OMPIRBuilder, CanonicalLoopInfo *CLI, Value *Ident,
6540 Function &OutlinedFn, const SmallVector<Instruction *, 4> &ToBeDeleted,
6541 WorksharingLoopType LoopType, bool NoLoop) {
6542 IRBuilder<> &Builder = OMPIRBuilder->Builder;
6543 BasicBlock *Preheader = CLI->getPreheader();
6544 Value *TripCount = CLI->getTripCount();
6545
6546 // After loop body outling, the loop body contains only set up
6547 // of loop body argument structure and the call to the outlined
6548 // loop body function. Firstly, we need to move setup of loop body args
6549 // into loop preheader.
6550 Preheader->splice(std::prev(Preheader->end()), CLI->getBody(),
6551 CLI->getBody()->begin(), std::prev(CLI->getBody()->end()));
6552
6553 // The next step is to remove the whole loop. We do not it need anymore.
6554 // That's why make an unconditional branch from loop preheader to loop
6555 // exit block
6556 Builder.restoreIP({Preheader, Preheader->end()});
6557 Builder.SetCurrentDebugLocation(Preheader->getTerminator()->getDebugLoc());
6558 Preheader->getTerminator()->eraseFromParent();
6559 Builder.CreateBr(CLI->getExit());
6560
6561 // Delete dead loop blocks
6562 OpenMPIRBuilder::OutlineInfo CleanUpInfo;
6563 SmallPtrSet<BasicBlock *, 32> RegionBlockSet;
6564 SmallVector<BasicBlock *, 32> BlocksToBeRemoved;
6565 CleanUpInfo.EntryBB = CLI->getHeader();
6566 CleanUpInfo.ExitBB = CLI->getExit();
6567 CleanUpInfo.collectBlocks(RegionBlockSet, BlocksToBeRemoved);
6568 DeleteDeadBlocks(BlocksToBeRemoved);
6569
6570 // Find the instruction which corresponds to loop body argument structure
6571 // and remove the call to loop body function instruction.
6572 Value *LoopBodyArg;
6573 User *OutlinedFnUser = OutlinedFn.getUniqueUndroppableUser();
6574 assert(OutlinedFnUser &&
6575 "Expected unique undroppable user of outlined function");
6576 CallInst *OutlinedFnCallInstruction = dyn_cast<CallInst>(OutlinedFnUser);
6577 assert(OutlinedFnCallInstruction && "Expected outlined function call");
6578 assert((OutlinedFnCallInstruction->getParent() == Preheader) &&
6579 "Expected outlined function call to be located in loop preheader");
6580 // Check in case no argument structure has been passed.
6581 if (OutlinedFnCallInstruction->arg_size() > 1)
6582 LoopBodyArg = OutlinedFnCallInstruction->getArgOperand(1);
6583 else
6584 LoopBodyArg = Constant::getNullValue(Builder.getPtrTy());
6585 OutlinedFnCallInstruction->eraseFromParent();
6586
6587 createTargetLoopWorkshareCall(OMPIRBuilder, LoopType, Preheader, Ident,
6588 LoopBodyArg, TripCount, OutlinedFn, NoLoop);
6589
6590 for (auto &ToBeDeletedItem : ToBeDeleted)
6591 ToBeDeletedItem->eraseFromParent();
6592 CLI->invalidate();
6593}
6594
6595OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::applyWorkshareLoopTarget(
6596 DebugLoc DL, CanonicalLoopInfo *CLI, InsertPointTy AllocaIP,
6597 WorksharingLoopType LoopType, bool NoLoop) {
6598 uint32_t SrcLocStrSize;
6599 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6601 switch (LoopType) {
6602 case WorksharingLoopType::ForStaticLoop:
6603 Flag = OMP_IDENT_FLAG_WORK_LOOP;
6604 break;
6605 case WorksharingLoopType::DistributeStaticLoop:
6606 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE;
6607 break;
6608 case WorksharingLoopType::DistributeForStaticLoop:
6609 Flag = OMP_IDENT_FLAG_WORK_DISTRIBUTE | OMP_IDENT_FLAG_WORK_LOOP;
6610 break;
6611 }
6612 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize, Flag);
6613
6614 auto OI = std::make_unique<OutlineInfo>();
6615 OI->OuterAllocBB = CLI->getPreheader();
6616 Function *OuterFn = CLI->getPreheader()->getParent();
6617
6618 // Instructions which need to be deleted at the end of code generation
6619 SmallVector<Instruction *, 4> ToBeDeleted;
6620
6621 OI->OuterAllocBB = AllocaIP.getBlock();
6622
6623 // Mark the body loop as region which needs to be extracted
6624 OI->EntryBB = CLI->getBody();
6625 OI->ExitBB = CLI->getLatch()->splitBasicBlockBefore(CLI->getLatch()->begin(),
6626 "omp.prelatch");
6627
6628 // Prepare loop body for extraction
6629 Builder.restoreIP({CLI->getPreheader(), CLI->getPreheader()->begin()});
6630
6631 // Insert new loop counter variable which will be used only in loop
6632 // body.
6633 AllocaInst *NewLoopCnt = Builder.CreateAlloca(CLI->getIndVarType(), 0, "");
6634 Instruction *NewLoopCntLoad =
6635 Builder.CreateLoad(CLI->getIndVarType(), NewLoopCnt);
6636 // New loop counter instructions are redundant in the loop preheader when
6637 // code generation for workshare loop is finshed. That's why mark them as
6638 // ready for deletion.
6639 ToBeDeleted.push_back(NewLoopCntLoad);
6640 ToBeDeleted.push_back(NewLoopCnt);
6641
6642 // Analyse loop body region. Find all input variables which are used inside
6643 // loop body region.
6644 SmallPtrSet<BasicBlock *, 32> ParallelRegionBlockSet;
6646 OI->collectBlocks(ParallelRegionBlockSet, Blocks);
6647
6648 CodeExtractorAnalysisCache CEAC(*OuterFn);
6649 CodeExtractor Extractor(Blocks,
6650 /* DominatorTree */ nullptr,
6651 /* AggregateArgs */ true,
6652 /* BlockFrequencyInfo */ nullptr,
6653 /* BranchProbabilityInfo */ nullptr,
6654 /* AssumptionCache */ nullptr,
6655 /* AllowVarArgs */ true,
6656 /* AllowAlloca */ true,
6657 /* AllocationBlock */ CLI->getPreheader(),
6658 /* DeallocationBlocks */ {},
6659 /* Suffix */ ".omp_wsloop",
6660 /* AggrArgsIn0AddrSpace */ true);
6661
6662 BasicBlock *CommonExit = nullptr;
6663 SetVector<Value *> SinkingCands, HoistingCands;
6664
6665 // Find allocas outside the loop body region which are used inside loop
6666 // body
6667 Extractor.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
6668
6669 // We need to model loop body region as the function f(cnt, loop_arg).
6670 // That's why we replace loop induction variable by the new counter
6671 // which will be one of loop body function argument
6673 CLI->getIndVar()->user_end());
6674 for (auto Use : Users) {
6675 if (Instruction *Inst = dyn_cast<Instruction>(Use)) {
6676 if (ParallelRegionBlockSet.count(Inst->getParent())) {
6677 Inst->replaceUsesOfWith(CLI->getIndVar(), NewLoopCntLoad);
6678 }
6679 }
6680 }
6681 // Make sure that loop counter variable is not merged into loop body
6682 // function argument structure and it is passed as separate variable
6683 OI->ExcludeArgsFromAggregate.push_back(NewLoopCntLoad);
6684
6685 // PostOutline CB is invoked when loop body function is outlined and
6686 // loop body is replaced by call to outlined function. We need to add
6687 // call to OpenMP device rtl inside loop preheader. OpenMP device rtl
6688 // function will handle loop control logic.
6689 //
6690 OI->PostOutlineCB = [=, ToBeDeletedVec =
6691 std::move(ToBeDeleted)](Function &OutlinedFn) {
6692 workshareLoopTargetCallback(this, CLI, Ident, OutlinedFn, ToBeDeletedVec,
6693 LoopType, NoLoop);
6694 };
6695 addOutlineInfo(std::move(OI));
6696 return CLI->getAfterIP();
6697}
6698
6701 bool NeedsBarrier, omp::ScheduleKind SchedKind, Value *ChunkSize,
6702 bool HasSimdModifier, bool HasMonotonicModifier,
6703 bool HasNonmonotonicModifier, bool HasOrderedClause,
6704 WorksharingLoopType LoopType, bool NoLoop, bool HasDistSchedule,
6705 Value *DistScheduleChunkSize) {
6706 if (Config.isTargetDevice())
6707 return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
6708 OMPScheduleType EffectiveScheduleType = computeOpenMPScheduleType(
6709 SchedKind, ChunkSize, HasSimdModifier, HasMonotonicModifier,
6710 HasNonmonotonicModifier, HasOrderedClause, DistScheduleChunkSize);
6711
6712 bool IsOrdered = (EffectiveScheduleType & OMPScheduleType::ModifierOrdered) ==
6713 OMPScheduleType::ModifierOrdered;
6714 OMPScheduleType DistScheduleSchedType = OMPScheduleType::None;
6715 if (HasDistSchedule) {
6716 DistScheduleSchedType = DistScheduleChunkSize
6717 ? OMPScheduleType::OrderedDistributeChunked
6718 : OMPScheduleType::OrderedDistribute;
6719 }
6720 switch (EffectiveScheduleType & ~OMPScheduleType::ModifierMask) {
6721 case OMPScheduleType::BaseStatic:
6722 case OMPScheduleType::BaseDistribute:
6723 assert((!ChunkSize || !DistScheduleChunkSize) &&
6724 "No chunk size with static-chunked schedule");
6725 if (IsOrdered && !HasDistSchedule)
6726 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6727 NeedsBarrier, ChunkSize);
6728 // FIXME: Monotonicity ignored?
6729 if (DistScheduleChunkSize)
6730 return applyStaticChunkedWorkshareLoop(
6731 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6732 DistScheduleChunkSize, DistScheduleSchedType);
6733 return applyStaticWorkshareLoop(DL, CLI, AllocaIP, LoopType, NeedsBarrier,
6734 HasDistSchedule);
6735
6736 case OMPScheduleType::BaseStaticChunked:
6737 case OMPScheduleType::BaseDistributeChunked:
6738 if (IsOrdered && !HasDistSchedule)
6739 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6740 NeedsBarrier, ChunkSize);
6741 // FIXME: Monotonicity ignored?
6742 return applyStaticChunkedWorkshareLoop(
6743 DL, CLI, AllocaIP, NeedsBarrier, ChunkSize, EffectiveScheduleType,
6744 DistScheduleChunkSize, DistScheduleSchedType);
6745
6746 case OMPScheduleType::BaseRuntime:
6747 case OMPScheduleType::BaseAuto:
6748 case OMPScheduleType::BaseGreedy:
6749 case OMPScheduleType::BaseBalanced:
6750 case OMPScheduleType::BaseSteal:
6751 case OMPScheduleType::BaseRuntimeSimd:
6752 assert(!ChunkSize &&
6753 "schedule type does not support user-defined chunk sizes");
6754 [[fallthrough]];
6755 case OMPScheduleType::BaseGuidedSimd:
6756 case OMPScheduleType::BaseDynamicChunked:
6757 case OMPScheduleType::BaseGuidedChunked:
6758 case OMPScheduleType::BaseGuidedIterativeChunked:
6759 case OMPScheduleType::BaseGuidedAnalyticalChunked:
6760 case OMPScheduleType::BaseStaticBalancedChunked:
6761 return applyDynamicWorkshareLoop(DL, CLI, AllocaIP, EffectiveScheduleType,
6762 NeedsBarrier, ChunkSize);
6763
6764 default:
6765 llvm_unreachable("Unknown/unimplemented schedule kind");
6766 }
6767}
6768
6769/// Returns an LLVM function to call for initializing loop bounds using OpenMP
6770/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6771/// the runtime. Always interpret integers as unsigned similarly to
6772/// CanonicalLoopInfo.
6773static FunctionCallee
6775 unsigned Bitwidth = Ty->getIntegerBitWidth();
6776 if (Bitwidth == 32)
6777 return OMPBuilder.getOrCreateRuntimeFunction(
6778 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_4u);
6779 if (Bitwidth == 64)
6780 return OMPBuilder.getOrCreateRuntimeFunction(
6781 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_init_8u);
6782 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6783}
6784
6785/// Returns an LLVM function to call for updating the next loop using OpenMP
6786/// dynamic scheduling depending on `type`. Only i32 and i64 are supported by
6787/// the runtime. Always interpret integers as unsigned similarly to
6788/// CanonicalLoopInfo.
6789static FunctionCallee
6791 unsigned Bitwidth = Ty->getIntegerBitWidth();
6792 if (Bitwidth == 32)
6793 return OMPBuilder.getOrCreateRuntimeFunction(
6794 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_4u);
6795 if (Bitwidth == 64)
6796 return OMPBuilder.getOrCreateRuntimeFunction(
6797 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_next_8u);
6798 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6799}
6800
6801/// Returns an LLVM function to call for finalizing the dynamic loop using
6802/// depending on `type`. Only i32 and i64 are supported by the runtime. Always
6803/// interpret integers as unsigned similarly to CanonicalLoopInfo.
6804static FunctionCallee
6806 unsigned Bitwidth = Ty->getIntegerBitWidth();
6807 if (Bitwidth == 32)
6808 return OMPBuilder.getOrCreateRuntimeFunction(
6809 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_4u);
6810 if (Bitwidth == 64)
6811 return OMPBuilder.getOrCreateRuntimeFunction(
6812 M, omp::RuntimeFunction::OMPRTL___kmpc_dispatch_fini_8u);
6813 llvm_unreachable("unknown OpenMP loop iterator bitwidth");
6814}
6815
6817OpenMPIRBuilder::applyDynamicWorkshareLoop(DebugLoc DL, CanonicalLoopInfo *CLI,
6818 InsertPointTy AllocaIP,
6819 OMPScheduleType SchedType,
6820 bool NeedsBarrier, Value *Chunk) {
6821 assert(CLI->isValid() && "Requires a valid canonical loop");
6822 assert(!isConflictIP(AllocaIP, CLI->getPreheaderIP()) &&
6823 "Require dedicated allocate IP");
6825 "Require valid schedule type");
6826
6827 bool Ordered = (SchedType & OMPScheduleType::ModifierOrdered) ==
6828 OMPScheduleType::ModifierOrdered;
6829
6830 // Set up the source location value for OpenMP runtime.
6831 Builder.SetCurrentDebugLocation(DL);
6832
6833 uint32_t SrcLocStrSize;
6834 Constant *SrcLocStr = getOrCreateSrcLocStr(DL, SrcLocStrSize);
6835 Value *SrcLoc =
6836 getOrCreateIdent(SrcLocStr, SrcLocStrSize, OMP_IDENT_FLAG_WORK_LOOP);
6837
6838 // Declare useful OpenMP runtime functions.
6839 Value *IV = CLI->getIndVar();
6840 Type *IVTy = IV->getType();
6841 FunctionCallee DynamicInit = getKmpcForDynamicInitForType(IVTy, M, *this);
6842 FunctionCallee DynamicNext = getKmpcForDynamicNextForType(IVTy, M, *this);
6843
6844 // Allocate space for computed loop bounds as expected by the "init" function.
6845 Builder.SetInsertPoint(AllocaIP.getBlock()->getFirstNonPHIOrDbgOrAlloca());
6846 Type *I32Type = Type::getInt32Ty(M.getContext());
6847 Value *PLastIter = Builder.CreateAlloca(I32Type, nullptr, "p.lastiter");
6848 Value *PLowerBound = Builder.CreateAlloca(IVTy, nullptr, "p.lowerbound");
6849 Value *PUpperBound = Builder.CreateAlloca(IVTy, nullptr, "p.upperbound");
6850 Value *PStride = Builder.CreateAlloca(IVTy, nullptr, "p.stride");
6851 CLI->setLastIter(PLastIter);
6852
6853 // At the end of the preheader, prepare for calling the "init" function by
6854 // storing the current loop bounds into the allocated space. A canonical loop
6855 // always iterates from 0 to trip-count with step 1. Note that "init" expects
6856 // and produces an inclusive upper bound.
6857 BasicBlock *PreHeader = CLI->getPreheader();
6858 Builder.SetInsertPoint(PreHeader->getTerminator());
6859 Constant *One = ConstantInt::get(IVTy, 1);
6860 Builder.CreateStore(One, PLowerBound);
6861 Value *UpperBound = CLI->getTripCount();
6862 Builder.CreateStore(UpperBound, PUpperBound);
6863 Builder.CreateStore(One, PStride);
6864
6865 BasicBlock *Header = CLI->getHeader();
6866 BasicBlock *Exit = CLI->getExit();
6867 BasicBlock *Cond = CLI->getCond();
6868 BasicBlock *Latch = CLI->getLatch();
6869 InsertPointTy AfterIP = CLI->getAfterIP();
6870
6871 // The CLI will be "broken" in the code below, as the loop is no longer
6872 // a valid canonical loop.
6873
6874 if (!Chunk)
6875 Chunk = One;
6876
6877 Value *ThreadNum =
6878 getOrCreateThreadID(getOrCreateIdent(SrcLocStr, SrcLocStrSize));
6879
6880 Constant *SchedulingType =
6881 ConstantInt::get(I32Type, static_cast<int>(SchedType));
6882
6883 // Call the "init" function.
6884 createRuntimeFunctionCall(DynamicInit, {SrcLoc, ThreadNum, SchedulingType,
6885 /* LowerBound */ One, UpperBound,
6886 /* step */ One, Chunk});
6887
6888 // An outer loop around the existing one.
6889 BasicBlock *OuterCond = BasicBlock::Create(
6890 PreHeader->getContext(), Twine(PreHeader->getName()) + ".outer.cond",
6891 PreHeader->getParent());
6892 // This needs to be 32-bit always, so can't use the IVTy Zero above.
6893 Builder.SetInsertPoint(OuterCond, OuterCond->getFirstInsertionPt());
6895 DynamicNext,
6896 {SrcLoc, ThreadNum, PLastIter, PLowerBound, PUpperBound, PStride});
6897 Constant *Zero32 = ConstantInt::get(I32Type, 0);
6898 Value *MoreWork = Builder.CreateCmp(CmpInst::ICMP_NE, Res, Zero32);
6899 Value *LowerBound =
6900 Builder.CreateSub(Builder.CreateLoad(IVTy, PLowerBound), One, "lb");
6901 Builder.CreateCondBr(MoreWork, Header, Exit);
6902
6903 // Change PHI-node in loop header to use outer cond rather than preheader,
6904 // and set IV to the LowerBound.
6905 Instruction *Phi = &Header->front();
6906 auto *PI = cast<PHINode>(Phi);
6907 PI->setIncomingBlock(0, OuterCond);
6908 PI->setIncomingValue(0, LowerBound);
6909
6910 // Then set the pre-header to jump to the OuterCond
6911 Instruction *Term = PreHeader->getTerminator();
6912 auto *Br = cast<UncondBrInst>(Term);
6913 Br->setSuccessor(OuterCond);
6914
6915 // Modify the inner condition:
6916 // * Use the UpperBound returned from the DynamicNext call.
6917 // * jump to the loop outer loop when done with one of the inner loops.
6918 Builder.SetInsertPoint(Cond, Cond->getFirstInsertionPt());
6919 UpperBound = Builder.CreateLoad(IVTy, PUpperBound, "ub");
6920 Instruction *Comp = &*Builder.GetInsertPoint();
6921 auto *CI = cast<CmpInst>(Comp);
6922 CI->setOperand(1, UpperBound);
6923 // Redirect the inner exit to branch to outer condition.
6924 Instruction *Branch = &Cond->back();
6925 auto *BI = cast<CondBrInst>(Branch);
6926 assert(BI->getSuccessor(1) == Exit);
6927 BI->setSuccessor(1, OuterCond);
6928
6929 // Call the "fini" function if "ordered" is present in wsloop directive.
6930 if (Ordered) {
6931 Builder.SetInsertPoint(&Latch->back());
6932 FunctionCallee DynamicFini = getKmpcForDynamicFiniForType(IVTy, M, *this);
6933 createRuntimeFunctionCall(DynamicFini, {SrcLoc, ThreadNum});
6934 }
6935
6936 // Add the barrier if requested.
6937 if (NeedsBarrier) {
6938 Builder.SetInsertPoint(&Exit->back());
6939 InsertPointOrErrorTy BarrierIP =
6941 omp::Directive::OMPD_for, /* ForceSimpleCall */ false,
6942 /* CheckCancelFlag */ false);
6943 if (!BarrierIP)
6944 return BarrierIP.takeError();
6945 }
6946
6947 CLI->invalidate();
6948 return AfterIP;
6949}
6950
6951/// Redirect all edges that branch to \p OldTarget to \p NewTarget. That is,
6952/// after this \p OldTarget will be orphaned.
6954 BasicBlock *NewTarget, DebugLoc DL) {
6955 for (BasicBlock *Pred : make_early_inc_range(predecessors(OldTarget)))
6956 redirectTo(Pred, NewTarget, DL);
6957}
6958
6960 SmallPtrSet<BasicBlock *, 8> InternalBBs(from_range, BBs);
6961 // We add a block to BBsToKeep iff we have proven it has an external use.
6963
6964 while (true) {
6965 bool Changed = false;
6966
6967 for (BasicBlock *BB : BBs) {
6968 if (BBsToKeep.contains(BB))
6969 continue;
6970
6971 for (Use &U : BB->uses()) {
6972 auto *UseInst = dyn_cast<Instruction>(U.getUser());
6973 if (!UseInst)
6974 continue;
6975 BasicBlock *UseBB = UseInst->getParent();
6976 if (!InternalBBs.contains(UseBB) || BBsToKeep.contains(UseBB)) {
6977 BBsToKeep.insert(BB);
6978 Changed = true;
6979 break;
6980 }
6981 }
6982 }
6983
6984 if (!Changed)
6985 break;
6986 }
6987
6989 BBs, [&BBsToKeep](BasicBlock *BB) { return !BBsToKeep.contains(BB); });
6990 DeleteDeadBlocks(BBsToDelete);
6991}
6992
6993CanonicalLoopInfo *
6995 InsertPointTy ComputeIP) {
6996 assert(Loops.size() >= 1 && "At least one loop required");
6997 size_t NumLoops = Loops.size();
6998
6999 // Nothing to do if there is already just one loop.
7000 if (NumLoops == 1)
7001 return Loops.front();
7002
7003 CanonicalLoopInfo *Outermost = Loops.front();
7004 CanonicalLoopInfo *Innermost = Loops.back();
7005 BasicBlock *OrigPreheader = Outermost->getPreheader();
7006 BasicBlock *OrigAfter = Outermost->getAfter();
7007 Function *F = OrigPreheader->getParent();
7008
7009 // Loop control blocks that may become orphaned later.
7010 SmallVector<BasicBlock *, 12> OldControlBBs;
7011 OldControlBBs.reserve(6 * Loops.size());
7013 Loop->collectControlBlocks(OldControlBBs);
7014
7015 // Setup the IRBuilder for inserting the trip count computation.
7016 Builder.SetCurrentDebugLocation(DL);
7017 if (ComputeIP.isSet())
7018 Builder.restoreIP(ComputeIP);
7019 else
7020 Builder.restoreIP(Outermost->getPreheaderIP());
7021
7022 // Derive the collapsed' loop trip count.
7023 // TODO: Find common/largest indvar type.
7024 Value *CollapsedTripCount = nullptr;
7025 for (CanonicalLoopInfo *L : Loops) {
7026 assert(L->isValid() &&
7027 "All loops to collapse must be valid canonical loops");
7028 Value *OrigTripCount = L->getTripCount();
7029 if (!CollapsedTripCount) {
7030 CollapsedTripCount = OrigTripCount;
7031 continue;
7032 }
7033
7034 // TODO: Enable UndefinedSanitizer to diagnose an overflow here.
7035 CollapsedTripCount =
7036 Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
7037 }
7038
7039 // Create the collapsed loop control flow.
7040 CanonicalLoopInfo *Result =
7041 createLoopSkeleton(DL, CollapsedTripCount, F,
7042 OrigPreheader->getNextNode(), OrigAfter, "collapsed",
7043 /*IsCollapsed=*/true);
7044
7045 // Build the collapsed loop body code.
7046 // Start with deriving the input loop induction variables from the collapsed
7047 // one, using a divmod scheme. To preserve the original loops' order, the
7048 // innermost loop use the least significant bits.
7049 Builder.restoreIP(Result->getBodyIP());
7050
7051 Value *Leftover = Result->getIndVar();
7052 SmallVector<Value *> NewIndVars;
7053 NewIndVars.resize(NumLoops);
7054 for (int i = NumLoops - 1; i >= 1; --i) {
7055 Value *OrigTripCount = Loops[i]->getTripCount();
7056
7057 Value *NewIndVar = Builder.CreateURem(Leftover, OrigTripCount);
7058 NewIndVars[i] = NewIndVar;
7059
7060 Leftover = Builder.CreateUDiv(Leftover, OrigTripCount);
7061 }
7062 // Outermost loop gets all the remaining bits.
7063 NewIndVars[0] = Leftover;
7064
7065 // Construct the loop body control flow.
7066 // We progressively construct the branch structure following in direction of
7067 // the control flow, from the leading in-between code, the loop nest body, the
7068 // trailing in-between code, and rejoining the collapsed loop's latch.
7069 // ContinueBlock and ContinuePred keep track of the source(s) of next edge. If
7070 // the ContinueBlock is set, continue with that block. If ContinuePred, use
7071 // its predecessors as sources.
7072 BasicBlock *ContinueBlock = Result->getBody();
7073 BasicBlock *ContinuePred = nullptr;
7074 auto ContinueWith = [&ContinueBlock, &ContinuePred, DL](BasicBlock *Dest,
7075 BasicBlock *NextSrc) {
7076 if (ContinueBlock)
7077 redirectTo(ContinueBlock, Dest, DL);
7078 else
7079 redirectAllPredecessorsTo(ContinuePred, Dest, DL);
7080
7081 ContinueBlock = nullptr;
7082 ContinuePred = NextSrc;
7083 };
7084
7085 // The code before the nested loop of each level.
7086 // Because we are sinking it into the nest, it will be executed more often
7087 // that the original loop. More sophisticated schemes could keep track of what
7088 // the in-between code is and instantiate it only once per thread.
7089 for (size_t i = 0; i < NumLoops - 1; ++i)
7090 ContinueWith(Loops[i]->getBody(), Loops[i + 1]->getHeader());
7091
7092 // Connect the loop nest body.
7093 ContinueWith(Innermost->getBody(), Innermost->getLatch());
7094
7095 // The code after the nested loop at each level.
7096 for (size_t i = NumLoops - 1; i > 0; --i)
7097 ContinueWith(Loops[i]->getAfter(), Loops[i - 1]->getLatch());
7098
7099 // Connect the finished loop to the collapsed loop latch.
7100 ContinueWith(Result->getLatch(), nullptr);
7101
7102 // Replace the input loops with the new collapsed loop.
7103 redirectTo(Outermost->getPreheader(), Result->getPreheader(), DL);
7104 redirectTo(Result->getAfter(), Outermost->getAfter(), DL);
7105
7106 // Replace the input loop indvars with the derived ones.
7107 for (size_t i = 0; i < NumLoops; ++i)
7108 Loops[i]->getIndVar()->replaceAllUsesWith(NewIndVars[i]);
7109
7110 // Remove unused parts of the input loops.
7111 removeUnusedBlocksFromParent(OldControlBBs);
7112
7113 for (CanonicalLoopInfo *L : Loops)
7114 L->invalidate();
7115
7116#ifndef NDEBUG
7117 Result->assertOK();
7118#endif
7119 return Result;
7120}
7121
7122std::vector<CanonicalLoopInfo *>
7124 ArrayRef<Value *> TileSizes) {
7125 assert(TileSizes.size() == Loops.size() &&
7126 "Must pass as many tile sizes as there are loops");
7127 int NumLoops = Loops.size();
7128 assert(NumLoops >= 1 && "At least one loop to tile required");
7129
7130 CanonicalLoopInfo *OutermostLoop = Loops.front();
7131 CanonicalLoopInfo *InnermostLoop = Loops.back();
7132 Function *F = OutermostLoop->getBody()->getParent();
7133 BasicBlock *InnerEnter = InnermostLoop->getBody();
7134 BasicBlock *InnerLatch = InnermostLoop->getLatch();
7135
7136 // Loop control blocks that may become orphaned later.
7137 SmallVector<BasicBlock *, 12> OldControlBBs;
7138 OldControlBBs.reserve(6 * Loops.size());
7140 Loop->collectControlBlocks(OldControlBBs);
7141
7142 // Collect original trip counts and induction variable to be accessible by
7143 // index. Also, the structure of the original loops is not preserved during
7144 // the construction of the tiled loops, so do it before we scavenge the BBs of
7145 // any original CanonicalLoopInfo.
7146 SmallVector<Value *, 4> OrigTripCounts, OrigIndVars;
7147 for (CanonicalLoopInfo *L : Loops) {
7148 assert(L->isValid() && "All input loops must be valid canonical loops");
7149 OrigTripCounts.push_back(L->getTripCount());
7150 OrigIndVars.push_back(L->getIndVar());
7151 }
7152
7153 // Collect the code between loop headers. These may contain SSA definitions
7154 // that are used in the loop nest body. To be usable with in the innermost
7155 // body, these BasicBlocks will be sunk into the loop nest body. That is,
7156 // these instructions may be executed more often than before the tiling.
7157 // TODO: It would be sufficient to only sink them into body of the
7158 // corresponding tile loop.
7160 for (int i = 0; i < NumLoops - 1; ++i) {
7161 CanonicalLoopInfo *Surrounding = Loops[i];
7162 CanonicalLoopInfo *Nested = Loops[i + 1];
7163
7164 BasicBlock *EnterBB = Surrounding->getBody();
7165 BasicBlock *ExitBB = Nested->getHeader();
7166 InbetweenCode.emplace_back(EnterBB, ExitBB);
7167 }
7168
7169 // Compute the trip counts of the floor loops.
7170 Builder.SetCurrentDebugLocation(DL);
7171 Builder.restoreIP(OutermostLoop->getPreheaderIP());
7172 SmallVector<Value *, 4> FloorCompleteCount, FloorCount, FloorRems;
7173 for (int i = 0; i < NumLoops; ++i) {
7174 Value *TileSize = TileSizes[i];
7175 Value *OrigTripCount = OrigTripCounts[i];
7176 Type *IVType = OrigTripCount->getType();
7177
7178 Value *FloorCompleteTripCount = Builder.CreateUDiv(OrigTripCount, TileSize);
7179 Value *FloorTripRem = Builder.CreateURem(OrigTripCount, TileSize);
7180
7181 // 0 if tripcount divides the tilesize, 1 otherwise.
7182 // 1 means we need an additional iteration for a partial tile.
7183 //
7184 // Unfortunately we cannot just use the roundup-formula
7185 // (tripcount + tilesize - 1)/tilesize
7186 // because the summation might overflow. We do not want introduce undefined
7187 // behavior when the untiled loop nest did not.
7188 Value *FloorTripOverflow =
7189 Builder.CreateICmpNE(FloorTripRem, ConstantInt::get(IVType, 0));
7190
7191 FloorTripOverflow = Builder.CreateZExt(FloorTripOverflow, IVType);
7192 Value *FloorTripCount =
7193 Builder.CreateAdd(FloorCompleteTripCount, FloorTripOverflow,
7194 "omp_floor" + Twine(i) + ".tripcount", true);
7195
7196 // Remember some values for later use.
7197 FloorCompleteCount.push_back(FloorCompleteTripCount);
7198 FloorCount.push_back(FloorTripCount);
7199 FloorRems.push_back(FloorTripRem);
7200 }
7201
7202 // Generate the new loop nest, from the outermost to the innermost.
7203 std::vector<CanonicalLoopInfo *> Result;
7204 Result.reserve(NumLoops * 2);
7205
7206 // The basic block of the surrounding loop that enters the nest generated
7207 // loop.
7208 BasicBlock *Enter = OutermostLoop->getPreheader();
7209
7210 // The basic block of the surrounding loop where the inner code should
7211 // continue.
7212 BasicBlock *Continue = OutermostLoop->getAfter();
7213
7214 // Where the next loop basic block should be inserted.
7215 BasicBlock *OutroInsertBefore = InnermostLoop->getExit();
7216
7217 auto EmbeddNewLoop =
7218 [this, DL, F, InnerEnter, &Enter, &Continue, &OutroInsertBefore](
7219 Value *TripCount, const Twine &Name) -> CanonicalLoopInfo * {
7220 CanonicalLoopInfo *EmbeddedLoop = createLoopSkeleton(
7221 DL, TripCount, F, InnerEnter, OutroInsertBefore, Name);
7222 redirectTo(Enter, EmbeddedLoop->getPreheader(), DL);
7223 redirectTo(EmbeddedLoop->getAfter(), Continue, DL);
7224
7225 // Setup the position where the next embedded loop connects to this loop.
7226 Enter = EmbeddedLoop->getBody();
7227 Continue = EmbeddedLoop->getLatch();
7228 OutroInsertBefore = EmbeddedLoop->getLatch();
7229 return EmbeddedLoop;
7230 };
7231
7232 auto EmbeddNewLoops = [&Result, &EmbeddNewLoop](ArrayRef<Value *> TripCounts,
7233 const Twine &NameBase) {
7234 for (auto P : enumerate(TripCounts)) {
7235 CanonicalLoopInfo *EmbeddedLoop =
7236 EmbeddNewLoop(P.value(), NameBase + Twine(P.index()));
7237 Result.push_back(EmbeddedLoop);
7238 }
7239 };
7240
7241 EmbeddNewLoops(FloorCount, "floor");
7242
7243 // Within the innermost floor loop, emit the code that computes the tile
7244 // sizes.
7245 Builder.SetInsertPoint(Enter->getTerminator());
7246 SmallVector<Value *, 4> TileCounts;
7247 for (int i = 0; i < NumLoops; ++i) {
7248 CanonicalLoopInfo *FloorLoop = Result[i];
7249 Value *TileSize = TileSizes[i];
7250
7251 Value *FloorIsEpilogue =
7252 Builder.CreateICmpEQ(FloorLoop->getIndVar(), FloorCompleteCount[i]);
7253 Value *TileTripCount =
7254 Builder.CreateSelect(FloorIsEpilogue, FloorRems[i], TileSize);
7255
7256 TileCounts.push_back(TileTripCount);
7257 }
7258
7259 // Create the tile loops.
7260 EmbeddNewLoops(TileCounts, "tile");
7261
7262 // Insert the inbetween code into the body.
7263 BasicBlock *BodyEnter = Enter;
7264 BasicBlock *BodyEntered = nullptr;
7265 for (std::pair<BasicBlock *, BasicBlock *> P : InbetweenCode) {
7266 BasicBlock *EnterBB = P.first;
7267 BasicBlock *ExitBB = P.second;
7268
7269 if (BodyEnter)
7270 redirectTo(BodyEnter, EnterBB, DL);
7271 else
7272 redirectAllPredecessorsTo(BodyEntered, EnterBB, DL);
7273
7274 BodyEnter = nullptr;
7275 BodyEntered = ExitBB;
7276 }
7277
7278 // Append the original loop nest body into the generated loop nest body.
7279 if (BodyEnter)
7280 redirectTo(BodyEnter, InnerEnter, DL);
7281 else
7282 redirectAllPredecessorsTo(BodyEntered, InnerEnter, DL);
7284
7285 // Replace the original induction variable with an induction variable computed
7286 // from the tile and floor induction variables.
7287 Builder.restoreIP(Result.back()->getBodyIP());
7288 for (int i = 0; i < NumLoops; ++i) {
7289 CanonicalLoopInfo *FloorLoop = Result[i];
7290 CanonicalLoopInfo *TileLoop = Result[NumLoops + i];
7291 Value *OrigIndVar = OrigIndVars[i];
7292 Value *Size = TileSizes[i];
7293
7294 Value *Scale =
7295 Builder.CreateMul(Size, FloorLoop->getIndVar(), {}, /*HasNUW=*/true);
7296 Value *Shift =
7297 Builder.CreateAdd(Scale, TileLoop->getIndVar(), {}, /*HasNUW=*/true);
7298 OrigIndVar->replaceAllUsesWith(Shift);
7299 }
7300
7301 // Remove unused parts of the original loops.
7302 removeUnusedBlocksFromParent(OldControlBBs);
7303
7304 for (CanonicalLoopInfo *L : Loops)
7305 L->invalidate();
7306
7307#ifndef NDEBUG
7308 for (CanonicalLoopInfo *GenL : Result)
7309 GenL->assertOK();
7310#endif
7311 return Result;
7312}
7313
7314/// Attach metadata \p Properties to the basic block described by \p BB. If the
7315/// basic block already has metadata, the basic block properties are appended.
7318 // Nothing to do if no property to attach.
7319 if (Properties.empty())
7320 return;
7321
7322 LLVMContext &Ctx = BB->getContext();
7323 SmallVector<Metadata *> NewProperties;
7324 NewProperties.push_back(nullptr);
7325
7326 // If the basic block already has metadata, prepend it to the new metadata.
7327 MDNode *Existing = BB->getTerminator()->getMetadata(LLVMContext::MD_loop);
7328 if (Existing)
7329 append_range(NewProperties, drop_begin(Existing->operands(), 1));
7330
7331 append_range(NewProperties, Properties);
7332 MDNode *BasicBlockID = MDNode::getDistinct(Ctx, NewProperties);
7333 BasicBlockID->replaceOperandWith(0, BasicBlockID);
7334
7335 BB->getTerminator()->setMetadata(LLVMContext::MD_loop, BasicBlockID);
7336}
7337
7338/// Attach loop metadata \p Properties to the loop described by \p Loop. If the
7339/// loop already has metadata, the loop properties are appended.
7342 assert(Loop->isValid() && "Expecting a valid CanonicalLoopInfo");
7343
7344 // Attach metadata to the loop's latch
7345 BasicBlock *Latch = Loop->getLatch();
7346 assert(Latch && "A valid CanonicalLoopInfo must have a unique latch");
7348}
7349
7350/// Attach llvm.access.group metadata to the memref instructions of \p Block
7352 LoopInfo &LI) {
7353 for (Instruction &I : *Block) {
7354 if (I.mayReadOrWriteMemory()) {
7355 // TODO: This instruction may already have access group from
7356 // other pragmas e.g. #pragma clang loop vectorize. Append
7357 // so that the existing metadata is not overwritten.
7358 I.setMetadata(LLVMContext::MD_access_group, AccessGroup);
7359 }
7360 }
7361}
7362
7363CanonicalLoopInfo *
7365 CanonicalLoopInfo *firstLoop = Loops.front();
7366 CanonicalLoopInfo *lastLoop = Loops.back();
7367 Function *F = firstLoop->getPreheader()->getParent();
7368
7369 // Loop control blocks that will become orphaned later
7370 SmallVector<BasicBlock *> oldControlBBs;
7372 Loop->collectControlBlocks(oldControlBBs);
7373
7374 // Collect original trip counts
7375 SmallVector<Value *> origTripCounts;
7376 for (CanonicalLoopInfo *L : Loops) {
7377 assert(L->isValid() && "All input loops must be valid canonical loops");
7378 origTripCounts.push_back(L->getTripCount());
7379 }
7380
7381 Builder.SetCurrentDebugLocation(DL);
7382
7383 // Compute max trip count.
7384 // The fused loop will be from 0 to max(origTripCounts)
7385 BasicBlock *TCBlock = BasicBlock::Create(F->getContext(), "omp.fuse.comp.tc",
7386 F, firstLoop->getHeader());
7387 Builder.SetInsertPoint(TCBlock);
7388 Value *fusedTripCount = nullptr;
7389 for (CanonicalLoopInfo *L : Loops) {
7390 assert(L->isValid() && "All loops to fuse must be valid canonical loops");
7391 Value *origTripCount = L->getTripCount();
7392 if (!fusedTripCount) {
7393 fusedTripCount = origTripCount;
7394 continue;
7395 }
7396 Value *condTP = Builder.CreateICmpSGT(fusedTripCount, origTripCount);
7397 fusedTripCount = Builder.CreateSelect(condTP, fusedTripCount, origTripCount,
7398 ".omp.fuse.tc");
7399 }
7400
7401 // Generate new loop
7402 CanonicalLoopInfo *fused =
7403 createLoopSkeleton(DL, fusedTripCount, F, firstLoop->getBody(),
7404 lastLoop->getLatch(), "fused");
7405
7406 // Replace original loops with the fused loop
7407 // Preheader and After are not considered inside the CLI.
7408 // These are used to compute the individual TCs of the loops
7409 // so they have to be put before the resulting fused loop.
7410 // Moving them up for readability.
7411 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7412 Loops[i]->getPreheader()->moveBefore(TCBlock);
7413 Loops[i]->getAfter()->moveBefore(TCBlock);
7414 }
7415 lastLoop->getPreheader()->moveBefore(TCBlock);
7416
7417 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7418 redirectTo(Loops[i]->getPreheader(), Loops[i]->getAfter(), DL);
7419 redirectTo(Loops[i]->getAfter(), Loops[i + 1]->getPreheader(), DL);
7420 }
7421 redirectTo(lastLoop->getPreheader(), TCBlock, DL);
7422 redirectTo(TCBlock, fused->getPreheader(), DL);
7423 redirectTo(fused->getAfter(), lastLoop->getAfter(), DL);
7424
7425 // Build the fused body
7426 // Create new Blocks with conditions that jump to the original loop bodies
7428 SmallVector<Value *> condValues;
7429 for (size_t i = 0; i < Loops.size(); ++i) {
7430 BasicBlock *condBlock = BasicBlock::Create(
7431 F->getContext(), "omp.fused.inner.cond", F, Loops[i]->getBody());
7432 Builder.SetInsertPoint(condBlock);
7433 Value *condValue =
7434 Builder.CreateICmpSLT(fused->getIndVar(), origTripCounts[i]);
7435 condBBs.push_back(condBlock);
7436 condValues.push_back(condValue);
7437 }
7438 // Join the condition blocks with the bodies of the original loops
7439 redirectTo(fused->getBody(), condBBs[0], DL);
7440 for (size_t i = 0; i < Loops.size() - 1; ++i) {
7441 Builder.SetInsertPoint(condBBs[i]);
7442 Builder.CreateCondBr(condValues[i], Loops[i]->getBody(), condBBs[i + 1]);
7443 redirectAllPredecessorsTo(Loops[i]->getLatch(), condBBs[i + 1], DL);
7444 // Replace the IV with the fused IV
7445 Loops[i]->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7446 }
7447 // Last body jumps to the created end body block
7448 Builder.SetInsertPoint(condBBs.back());
7449 Builder.CreateCondBr(condValues.back(), lastLoop->getBody(),
7450 fused->getLatch());
7451 redirectAllPredecessorsTo(lastLoop->getLatch(), fused->getLatch(), DL);
7452 // Replace the IV with the fused IV
7453 lastLoop->getIndVar()->replaceAllUsesWith(fused->getIndVar());
7454
7455 // The loop latch must have only one predecessor. Currently it is branched to
7456 // from both the last condition block and the last loop body
7457 fused->getLatch()->splitBasicBlockBefore(fused->getLatch()->begin(),
7458 "omp.fused.pre_latch");
7459
7460 // Remove unused parts
7461 removeUnusedBlocksFromParent(oldControlBBs);
7462
7463 // Invalidate old CLIs
7464 for (CanonicalLoopInfo *L : Loops)
7465 L->invalidate();
7466
7467#ifndef NDEBUG
7468 fused->assertOK();
7469#endif
7470 return fused;
7471}
7472
7474 LLVMContext &Ctx = Builder.getContext();
7476 Loop, {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7477 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.full"))});
7478}
7479
7481 LLVMContext &Ctx = Builder.getContext();
7483 Loop, {
7484 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7485 });
7486}
7487
7488void OpenMPIRBuilder::createIfVersion(CanonicalLoopInfo *CanonicalLoop,
7489 Value *IfCond, ValueToValueMapTy &VMap,
7490 LoopAnalysis &LIA, LoopInfo &LI, Loop *L,
7491 const Twine &NamePrefix) {
7492 Function *F = CanonicalLoop->getFunction();
7493
7494 // We can't do
7495 // if (cond) {
7496 // simd_loop;
7497 // } else {
7498 // non_simd_loop;
7499 // }
7500 // because then the CanonicalLoopInfo would only point to one of the loops:
7501 // leading to other constructs operating on the same loop to malfunction.
7502 // Instead generate
7503 // while (...) {
7504 // if (cond) {
7505 // simd_body;
7506 // } else {
7507 // not_simd_body;
7508 // }
7509 // }
7510 // At least for simple loops, LLVM seems able to hoist the if out of the loop
7511 // body at -O3
7512
7513 // Define where if branch should be inserted
7514 auto SplitBeforeIt = CanonicalLoop->getBody()->getFirstNonPHIIt();
7515
7516 // Create additional blocks for the if statement
7517 BasicBlock *Cond = SplitBeforeIt->getParent();
7518 llvm::LLVMContext &C = Cond->getContext();
7520 C, NamePrefix + ".if.then", Cond->getParent(), Cond->getNextNode());
7522 C, NamePrefix + ".if.else", Cond->getParent(), CanonicalLoop->getExit());
7523
7524 // Create if condition branch.
7525 Builder.SetInsertPoint(SplitBeforeIt);
7526 Instruction *BrInstr =
7527 Builder.CreateCondBr(IfCond, ThenBlock, /*ifFalse*/ ElseBlock);
7528 InsertPointTy IP{BrInstr->getParent(), ++BrInstr->getIterator()};
7529 // Then block contains branch to omp loop body which needs to be vectorized
7530 spliceBB(IP, ThenBlock, false, Builder.getCurrentDebugLocation());
7531 ThenBlock->replaceSuccessorsPhiUsesWith(Cond, ThenBlock);
7532
7533 Builder.SetInsertPoint(ElseBlock);
7534
7535 // Clone loop for the else branch
7537
7538 SmallVector<BasicBlock *, 8> ExistingBlocks;
7539 ExistingBlocks.reserve(L->getNumBlocks() + 1);
7540 ExistingBlocks.push_back(ThenBlock);
7541 ExistingBlocks.append(L->block_begin(), L->block_end());
7542 // Cond is the block that has the if clause condition
7543 // LoopCond is omp_loop.cond
7544 // LoopHeader is omp_loop.header
7545 BasicBlock *LoopCond = Cond->getUniquePredecessor();
7546 BasicBlock *LoopHeader = LoopCond->getUniquePredecessor();
7547 assert(LoopCond && LoopHeader && "Invalid loop structure");
7548 for (BasicBlock *Block : ExistingBlocks) {
7549 if (Block == L->getLoopPreheader() || Block == L->getLoopLatch() ||
7550 Block == LoopHeader || Block == LoopCond || Block == Cond) {
7551 continue;
7552 }
7553 BasicBlock *NewBB = CloneBasicBlock(Block, VMap, "", F);
7554
7555 // fix name not to be omp.if.then
7556 if (Block == ThenBlock)
7557 NewBB->setName(NamePrefix + ".if.else");
7558
7559 NewBB->moveBefore(CanonicalLoop->getExit());
7560 VMap[Block] = NewBB;
7561 NewBlocks.push_back(NewBB);
7562 }
7563 remapInstructionsInBlocks(NewBlocks, VMap);
7564 Builder.CreateBr(NewBlocks.front());
7565
7566 // The loop latch must have only one predecessor. Currently it is branched to
7567 // from both the 'then' and 'else' branches.
7568 L->getLoopLatch()->splitBasicBlockBefore(L->getLoopLatch()->begin(),
7569 NamePrefix + ".pre_latch");
7570
7571 // Ensure that the then block is added to the loop so we add the attributes in
7572 // the next step
7573 L->addBasicBlockToLoop(ThenBlock, LI);
7574}
7575
7576unsigned
7578 const StringMap<bool> &Features) {
7579 if (TargetTriple.isX86()) {
7580 if (Features.lookup("avx512f"))
7581 return 512;
7582 else if (Features.lookup("avx"))
7583 return 256;
7584 return 128;
7585 }
7586 if (TargetTriple.isPPC())
7587 return 128;
7588 if (TargetTriple.isWasm())
7589 return 128;
7590 return 0;
7591}
7592
7594 MapVector<Value *, Value *> AlignedVars,
7595 Value *IfCond, OrderKind Order,
7596 ConstantInt *Simdlen, ConstantInt *Safelen) {
7597 LLVMContext &Ctx = Builder.getContext();
7598
7599 Function *F = CanonicalLoop->getFunction();
7600
7601 // Blocks must have terminators.
7602 // FIXME: Don't run analyses on incomplete/invalid IR.
7604 for (BasicBlock &BB : *F)
7605 if (!BB.hasTerminator())
7606 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7607
7608 // TODO: We should not rely on pass manager. Currently we use pass manager
7609 // only for getting llvm::Loop which corresponds to given CanonicalLoopInfo
7610 // object. We should have a method which returns all blocks between
7611 // CanonicalLoopInfo::getHeader() and CanonicalLoopInfo::getAfter()
7613 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7614 FAM.registerPass([]() { return LoopAnalysis(); });
7615 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7616
7617 LoopAnalysis LIA;
7618 LoopInfo &&LI = LIA.run(*F, FAM);
7619
7620 for (Instruction *I : UIs)
7621 I->eraseFromParent();
7622
7623 Loop *L = LI.getLoopFor(CanonicalLoop->getHeader());
7624 if (AlignedVars.size()) {
7625 InsertPointTy IP = Builder.saveIP();
7626 for (auto &AlignedItem : AlignedVars) {
7627 Value *AlignedPtr = AlignedItem.first;
7628 Value *Alignment = AlignedItem.second;
7629 Instruction *loadInst = dyn_cast<Instruction>(AlignedPtr);
7630 Builder.SetInsertPoint(loadInst->getNextNode());
7631 Builder.CreateAlignmentAssumption(F->getDataLayout(), AlignedPtr,
7632 Alignment);
7633 }
7634 Builder.restoreIP(IP);
7635 }
7636
7637 if (IfCond) {
7638 ValueToValueMapTy VMap;
7639 createIfVersion(CanonicalLoop, IfCond, VMap, LIA, LI, L, "simd");
7640 }
7641
7643
7644 // Get the basic blocks from the loop in which memref instructions
7645 // can be found.
7646 // TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo,
7647 // preferably without running any passes.
7648 for (BasicBlock *Block : L->getBlocks()) {
7649 if (Block == CanonicalLoop->getCond() ||
7650 Block == CanonicalLoop->getHeader())
7651 continue;
7652 Reachable.insert(Block);
7653 }
7654
7655 SmallVector<Metadata *> LoopMDList;
7656
7657 // In presence of finite 'safelen', it may be unsafe to mark all
7658 // the memory instructions parallel, because loop-carried
7659 // dependences of 'safelen' iterations are possible.
7660 // If clause order(concurrent) is specified then the memory instructions
7661 // are marked parallel even if 'safelen' is finite.
7662 if ((Safelen == nullptr) || (Order == OrderKind::OMP_ORDER_concurrent))
7663 applyParallelAccessesMetadata(CanonicalLoop, Ctx, L, LI, LoopMDList);
7664
7665 // FIXME: the IF clause shares a loop backedge for the SIMD and non-SIMD
7666 // versions so we can't add the loop attributes in that case.
7667 if (IfCond) {
7668 // we can still add llvm.loop.parallel_access
7669 addLoopMetadata(CanonicalLoop, LoopMDList);
7670 return;
7671 }
7672
7673 // Use the above access group metadata to create loop level
7674 // metadata, which should be distinct for each loop.
7675 LoopMDList.push_back(
7676 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable")}));
7677
7678 if (Simdlen || Safelen) {
7679 // If both simdlen and safelen clauses are specified, the value of the
7680 // simdlen parameter must be less than or equal to the value of the safelen
7681 // parameter. Therefore, use safelen only in the absence of simdlen.
7682 ConstantInt *VectorizeWidth = Simdlen == nullptr ? Safelen : Simdlen;
7683 LoopMDList.push_back(
7684 MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.width"),
7685 ConstantAsMetadata::get(VectorizeWidth)}));
7686 }
7687
7688 addLoopMetadata(CanonicalLoop, LoopMDList);
7689}
7690
7691/// Create the TargetMachine object to query the backend for optimization
7692/// preferences.
7693///
7694/// Ideally, this would be passed from the front-end to the OpenMPBuilder, but
7695/// e.g. Clang does not pass it to its CodeGen layer and creates it only when
7696/// needed for the LLVM pass pipline. We use some default options to avoid
7697/// having to pass too many settings from the frontend that probably do not
7698/// matter.
7699///
7700/// Currently, TargetMachine is only used sometimes by the unrollLoopPartial
7701/// method. If we are going to use TargetMachine for more purposes, especially
7702/// those that are sensitive to TargetOptions, RelocModel and CodeModel, it
7703/// might become be worth requiring front-ends to pass on their TargetMachine,
7704/// or at least cache it between methods. Note that while fontends such as Clang
7705/// have just a single main TargetMachine per translation unit, "target-cpu" and
7706/// "target-features" that determine the TargetMachine are per-function and can
7707/// be overrided using __attribute__((target("OPTIONS"))).
7708static std::unique_ptr<TargetMachine>
7710 Module *M = F->getParent();
7711
7712 StringRef CPU = F->getFnAttribute("target-cpu").getValueAsString();
7713 StringRef Features = F->getFnAttribute("target-features").getValueAsString();
7714 const llvm::Triple &Triple = M->getTargetTriple();
7715
7716 std::string Error;
7718 if (!TheTarget)
7719 return {};
7720
7722 return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
7723 Triple, CPU, Features, Options, /*RelocModel=*/std::nullopt,
7724 /*CodeModel=*/std::nullopt, OptLevel));
7725}
7726
7727/// Heuristically determine the best-performant unroll factor for \p CLI. This
7728/// depends on the target processor. We are re-using the same heuristics as the
7729/// LoopUnrollPass.
7731 Function *F = CLI->getFunction();
7732
7733 // Assume the user requests the most aggressive unrolling, even if the rest of
7734 // the code is optimized using a lower setting.
7736 std::unique_ptr<TargetMachine> TM = createTargetMachine(F, OptLevel);
7737
7738 // Blocks must have terminators.
7739 // FIXME: Don't run analyses on incomplete/invalid IR.
7741 for (BasicBlock &BB : *F)
7742 if (!BB.hasTerminator())
7743 UIs.push_back(new UnreachableInst(F->getContext(), &BB));
7744
7746 FAM.registerPass([]() { return TargetLibraryAnalysis(); });
7747 FAM.registerPass([]() { return AssumptionAnalysis(); });
7748 FAM.registerPass([]() { return DominatorTreeAnalysis(); });
7749 FAM.registerPass([]() { return LoopAnalysis(); });
7750 FAM.registerPass([]() { return ScalarEvolutionAnalysis(); });
7751 FAM.registerPass([]() { return PassInstrumentationAnalysis(); });
7752 TargetIRAnalysis TIRA;
7753 if (TM)
7754 TIRA = TargetIRAnalysis(
7755 [&](const Function &F) { return TM->getTargetTransformInfo(F); });
7756 FAM.registerPass([&]() { return TIRA; });
7757
7758 TargetIRAnalysis::Result &&TTI = TIRA.run(*F, FAM);
7760 ScalarEvolution &&SE = SEA.run(*F, FAM);
7762 DominatorTree &&DT = DTA.run(*F, FAM);
7763 LoopAnalysis LIA;
7764 LoopInfo &&LI = LIA.run(*F, FAM);
7766 AssumptionCache &&AC = ACT.run(*F, FAM);
7768
7769 for (Instruction *I : UIs)
7770 I->eraseFromParent();
7771
7772 Loop *L = LI.getLoopFor(CLI->getHeader());
7773 assert(L && "Expecting CanonicalLoopInfo to be recognized as a loop");
7774
7776 L, SE, TTI,
7777 /*BlockFrequencyInfo=*/nullptr,
7778 /*ProfileSummaryInfo=*/nullptr, ORE, static_cast<int>(OptLevel),
7779 /*UserThreshold=*/std::nullopt,
7780 /*UserAllowPartial=*/true,
7781 /*UserAllowRuntime=*/true,
7782 /*UserUpperBound=*/std::nullopt,
7783 /*UserFullUnrollMaxCount=*/std::nullopt);
7784
7785 UP.Force = true;
7786
7787 // Account for additional optimizations taking place before the LoopUnrollPass
7788 // would unroll the loop.
7791
7792 // Use normal unroll factors even if the rest of the code is optimized for
7793 // size.
7796
7797 LLVM_DEBUG(dbgs() << "Unroll heuristic thresholds:\n"
7798 << " Threshold=" << UP.Threshold << "\n"
7799 << " PartialThreshold=" << UP.PartialThreshold << "\n"
7800 << " OptSizeThreshold=" << UP.OptSizeThreshold << "\n"
7801 << " PartialOptSizeThreshold="
7802 << UP.PartialOptSizeThreshold << "\n");
7803
7804 // Disable peeling.
7807 /*UserAllowPeeling=*/false,
7808 /*UserAllowProfileBasedPeeling=*/false,
7809 /*UnrollingSpecficValues=*/false);
7810
7812 CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
7813
7814 // Assume that reads and writes to stack variables can be eliminated by
7815 // Mem2Reg, SROA or LICM. That is, don't count them towards the loop body's
7816 // size.
7817 for (BasicBlock *BB : L->blocks()) {
7818 for (Instruction &I : *BB) {
7819 Value *Ptr;
7820 if (auto *Load = dyn_cast<LoadInst>(&I)) {
7821 Ptr = Load->getPointerOperand();
7822 } else if (auto *Store = dyn_cast<StoreInst>(&I)) {
7823 Ptr = Store->getPointerOperand();
7824 } else
7825 continue;
7826
7827 Ptr = Ptr->stripPointerCasts();
7828
7829 if (auto *Alloca = dyn_cast<AllocaInst>(Ptr)) {
7830 if (Alloca->getParent() == &F->getEntryBlock())
7831 EphValues.insert(&I);
7832 }
7833 }
7834 }
7835
7836 UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
7837
7838 // Loop is not unrollable if the loop contains certain instructions.
7839 if (!UCE.canUnroll()) {
7840 LLVM_DEBUG(dbgs() << "Loop not considered unrollable\n");
7841 return 1;
7842 }
7843
7844 LLVM_DEBUG(dbgs() << "Estimated loop size is " << UCE.getRolledLoopSize()
7845 << "\n");
7846
7847 // TODO: Determine trip count of \p CLI if constant, computeUnrollCount might
7848 // be able to use it.
7849 int TripCount = 0;
7850 int MaxTripCount = 0;
7851 bool MaxOrZero = false;
7852 unsigned TripMultiple = 0;
7853
7854 unsigned Factor =
7855 computeUnrollCount(L, TTI, DT, &LI, &AC, SE, EphValues, &ORE, TripCount,
7856 MaxTripCount, MaxOrZero, TripMultiple, UCE, UP, PP);
7857 LLVM_DEBUG(dbgs() << "Suggesting unroll factor of " << Factor << "\n");
7858
7859 // This function returns 1 to signal to not unroll a loop.
7860 if (Factor == 0)
7861 return 1;
7862 return Factor;
7863}
7864
7866 int32_t Factor,
7867 CanonicalLoopInfo **UnrolledCLI) {
7868 assert(Factor >= 0 && "Unroll factor must not be negative");
7869
7870 Function *F = Loop->getFunction();
7871 LLVMContext &Ctx = F->getContext();
7872
7873 // If the unrolled loop is not used for another loop-associated directive, it
7874 // is sufficient to add metadata for the LoopUnrollPass.
7875 if (!UnrolledCLI) {
7876 SmallVector<Metadata *, 2> LoopMetadata;
7877 LoopMetadata.push_back(
7878 MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")));
7879
7880 if (Factor >= 1) {
7882 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7883 LoopMetadata.push_back(MDNode::get(
7884 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst}));
7885 }
7886
7887 addLoopMetadata(Loop, LoopMetadata);
7888 return;
7889 }
7890
7891 // Heuristically determine the unroll factor.
7892 if (Factor == 0)
7894
7895 // No change required with unroll factor 1.
7896 if (Factor == 1) {
7897 *UnrolledCLI = Loop;
7898 return;
7899 }
7900
7901 assert(Factor >= 2 &&
7902 "unrolling only makes sense with a factor of 2 or larger");
7903
7904 Type *IndVarTy = Loop->getIndVarType();
7905
7906 // Apply partial unrolling by tiling the loop by the unroll-factor, then fully
7907 // unroll the inner loop.
7908 Value *FactorVal =
7909 ConstantInt::get(IndVarTy, APInt(IndVarTy->getIntegerBitWidth(), Factor,
7910 /*isSigned=*/false));
7911 std::vector<CanonicalLoopInfo *> LoopNest =
7912 tileLoops(DL, {Loop}, {FactorVal});
7913 assert(LoopNest.size() == 2 && "Expect 2 loops after tiling");
7914 *UnrolledCLI = LoopNest[0];
7915 CanonicalLoopInfo *InnerLoop = LoopNest[1];
7916
7917 // LoopUnrollPass can only fully unroll loops with constant trip count.
7918 // Unroll by the unroll factor with a fallback epilog for the remainder
7919 // iterations if necessary.
7921 ConstantInt::get(Type::getInt32Ty(Ctx), APInt(32, Factor)));
7923 InnerLoop,
7924 {MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.unroll.enable")),
7926 Ctx, {MDString::get(Ctx, "llvm.loop.unroll.count"), FactorConst})});
7927
7928#ifndef NDEBUG
7929 (*UnrolledCLI)->assertOK();
7930#endif
7931}
7932
7935 llvm::Value *BufSize, llvm::Value *CpyBuf,
7936 llvm::Value *CpyFn, llvm::Value *DidIt) {
7937 if (!updateToLocation(Loc))
7938 return Loc.IP;
7939
7940 uint32_t SrcLocStrSize;
7941 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7942 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7943 Value *ThreadId = getOrCreateThreadID(Ident);
7944
7945 llvm::Value *DidItLD = Builder.CreateLoad(Builder.getInt32Ty(), DidIt);
7946
7947 Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD};
7948
7949 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate);
7950 createRuntimeFunctionCall(Fn, Args);
7951
7952 return Builder.saveIP();
7953}
7954
7956 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
7957 FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
7959
7960 if (!updateToLocation(Loc))
7961 return Loc.IP;
7962
7963 // If needed allocate and initialize `DidIt` with 0.
7964 // DidIt: flag variable: 1=single thread; 0=not single thread.
7965 llvm::Value *DidIt = nullptr;
7966 if (!CPVars.empty()) {
7967 DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
7968 Builder.CreateStore(Builder.getInt32(0), DidIt);
7969 }
7970
7971 Directive OMPD = Directive::OMPD_single;
7972 uint32_t SrcLocStrSize;
7973 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
7974 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
7975 Value *ThreadId = getOrCreateThreadID(Ident);
7976 Value *Args[] = {Ident, ThreadId};
7977
7978 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single);
7979 Instruction *EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
7980
7981 Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
7982 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
7983
7984 auto FiniCBWrapper = [&](InsertPointTy IP) -> Error {
7985 if (Error Err = FiniCB(IP))
7986 return Err;
7987
7988 // The thread that executes the single region must set `DidIt` to 1.
7989 // This is used by __kmpc_copyprivate, to know if the caller is the
7990 // single thread or not.
7991 if (DidIt)
7992 Builder.CreateStore(Builder.getInt32(1), DidIt);
7993
7994 return Error::success();
7995 };
7996
7997 // generates the following:
7998 // if (__kmpc_single()) {
7999 // .... single region ...
8000 // __kmpc_end_single
8001 // }
8002 // __kmpc_copyprivate
8003 // __kmpc_barrier
8004
8005 InsertPointOrErrorTy AfterIP =
8006 EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
8007 /*Conditional*/ true,
8008 /*hasFinalize*/ true);
8009 if (!AfterIP)
8010 return AfterIP.takeError();
8011
8012 if (DidIt) {
8013 for (size_t I = 0, E = CPVars.size(); I < E; ++I)
8014 // NOTE BufSize is currently unused, so just pass 0.
8016 /*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
8017 CPFuncs[I], DidIt);
8018 // NOTE __kmpc_copyprivate already inserts a barrier
8019 } else if (!IsNowait) {
8020 InsertPointOrErrorTy AfterIP =
8022 omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
8023 /* CheckCancelFlag */ false);
8024 if (!AfterIP)
8025 return AfterIP.takeError();
8026 }
8027 return Builder.saveIP();
8028}
8029
8032 BodyGenCallbackTy BodyGenCB,
8033 FinalizeCallbackTy FiniCB, bool IsNowait) {
8034
8035 if (!updateToLocation(Loc))
8036 return Loc.IP;
8037
8038 // All threads execute the scope body — no conditional entry.
8039 InsertPointOrErrorTy AfterIP = EmitOMPInlinedRegion(
8040 Directive::OMPD_scope, /*EntryCall=*/nullptr, /*ExitCall=*/nullptr,
8041 BodyGenCB, FiniCB, /*Conditional=*/false, /*HasFinalize=*/true,
8042 /*IsCancellable=*/false);
8043 if (!AfterIP)
8044 return AfterIP.takeError();
8045
8046 Builder.restoreIP(*AfterIP);
8047 if (!IsNowait) {
8048 AfterIP = createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
8049 omp::Directive::OMPD_unknown,
8050 /*ForceSimpleCall=*/false,
8051 /*CheckCancelFlag=*/false);
8052 if (!AfterIP)
8053 return AfterIP.takeError();
8054 }
8055 return Builder.saveIP();
8056}
8057
8059 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8060 FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) {
8061
8062 if (!updateToLocation(Loc))
8063 return Loc.IP;
8064
8065 Directive OMPD = Directive::OMPD_critical;
8066 uint32_t SrcLocStrSize;
8067 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8068 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8069 Value *ThreadId = getOrCreateThreadID(Ident);
8070 Value *LockVar = getOMPCriticalRegionLock(CriticalName);
8071 Value *Args[] = {Ident, ThreadId, LockVar};
8072
8073 SmallVector<llvm::Value *, 4> EnterArgs(std::begin(Args), std::end(Args));
8074 Function *RTFn = nullptr;
8075 if (HintInst) {
8076 // Add Hint to entry Args and create call
8077 EnterArgs.push_back(HintInst);
8078 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical_with_hint);
8079 } else {
8080 RTFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_critical);
8081 }
8082 Instruction *EntryCall = createRuntimeFunctionCall(RTFn, EnterArgs);
8083
8084 Function *ExitRTLFn =
8085 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_critical);
8086 Instruction *ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8087
8088 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8089 /*Conditional*/ false, /*hasFinalize*/ true);
8090}
8091
8094 InsertPointTy AllocaIP, unsigned NumLoops,
8095 ArrayRef<llvm::Value *> StoreValues,
8096 const Twine &Name, bool IsDependSource) {
8097 assert(
8098 llvm::all_of(StoreValues,
8099 [](Value *SV) { return SV->getType()->isIntegerTy(64); }) &&
8100 "OpenMP runtime requires depend vec with i64 type");
8101
8102 if (!updateToLocation(Loc))
8103 return Loc.IP;
8104
8105 // Allocate space for vector and generate alloc instruction.
8106 auto *ArrI64Ty = ArrayType::get(Int64, NumLoops);
8107 Builder.restoreIP(AllocaIP);
8108 AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI64Ty, nullptr, Name);
8109 ArgsBase->setAlignment(Align(8));
8111
8112 // Store the index value with offset in depend vector.
8113 for (unsigned I = 0; I < NumLoops; ++I) {
8114 Value *DependAddrGEPIter = Builder.CreateInBoundsGEP(
8115 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(I)});
8116 StoreInst *STInst = Builder.CreateStore(StoreValues[I], DependAddrGEPIter);
8117 STInst->setAlignment(Align(8));
8118 }
8119
8120 Value *DependBaseAddrGEP = Builder.CreateInBoundsGEP(
8121 ArrI64Ty, ArgsBase, {Builder.getInt64(0), Builder.getInt64(0)});
8122
8123 uint32_t SrcLocStrSize;
8124 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8125 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8126 Value *ThreadId = getOrCreateThreadID(Ident);
8127 Value *Args[] = {Ident, ThreadId, DependBaseAddrGEP};
8128
8129 Function *RTLFn = nullptr;
8130 if (IsDependSource)
8131 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_post);
8132 else
8133 RTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_doacross_wait);
8134 createRuntimeFunctionCall(RTLFn, Args);
8135
8136 return Builder.saveIP();
8137}
8138
8140 const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
8141 FinalizeCallbackTy FiniCB, bool IsThreads) {
8142 if (!updateToLocation(Loc))
8143 return Loc.IP;
8144
8145 Directive OMPD = Directive::OMPD_ordered_blockassoc;
8146 Instruction *EntryCall = nullptr;
8147 Instruction *ExitCall = nullptr;
8148
8149 if (IsThreads) {
8150 uint32_t SrcLocStrSize;
8151 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8152 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8153 Value *ThreadId = getOrCreateThreadID(Ident);
8154 Value *Args[] = {Ident, ThreadId};
8155
8156 Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_ordered);
8157 EntryCall = createRuntimeFunctionCall(EntryRTLFn, Args);
8158
8159 Function *ExitRTLFn =
8160 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_ordered);
8161 ExitCall = createRuntimeFunctionCall(ExitRTLFn, Args);
8162 }
8163
8164 return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
8165 /*Conditional*/ false, /*hasFinalize*/ true);
8166}
8167
8168OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::EmitOMPInlinedRegion(
8169 Directive OMPD, Instruction *EntryCall, Instruction *ExitCall,
8170 BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional,
8171 bool HasFinalize, bool IsCancellable) {
8172
8173 if (HasFinalize)
8174 FinalizationStack.push_back({FiniCB, OMPD, IsCancellable});
8175
8176 // Create inlined region's entry and body blocks, in preparation
8177 // for conditional creation
8178 BasicBlock *EntryBB = Builder.GetInsertBlock();
8179 Instruction *SplitPos = EntryBB->getTerminatorOrNull();
8181 SplitPos = new UnreachableInst(Builder.getContext(), EntryBB);
8182 BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end");
8183 BasicBlock *FiniBB =
8184 EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize");
8185
8186 Builder.SetInsertPoint(EntryBB->getTerminator());
8187 emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional);
8188
8189 // generate body
8190 if (Error Err =
8191 BodyGenCB(/* AllocaIP */ InsertPointTy(),
8192 /* CodeGenIP */ Builder.saveIP(), /* DeallocBlocks */ {}))
8193 return Err;
8194
8195 // emit exit call and do any needed finalization.
8196 auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt());
8197 assert(FiniBB->getTerminator()->getNumSuccessors() == 1 &&
8198 FiniBB->getTerminator()->getSuccessor(0) == ExitBB &&
8199 "Unexpected control flow graph state!!");
8200 InsertPointOrErrorTy AfterIP =
8201 emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize);
8202 if (!AfterIP)
8203 return AfterIP.takeError();
8204
8205 // If we are skipping the region of a non conditional, remove the exit
8206 // block, and clear the builder's insertion point.
8207 assert(SplitPos->getParent() == ExitBB &&
8208 "Unexpected Insertion point location!");
8209 auto merged = MergeBlockIntoPredecessor(ExitBB);
8210 BasicBlock *ExitPredBB = SplitPos->getParent();
8211 auto InsertBB = merged ? ExitPredBB : ExitBB;
8213 SplitPos->eraseFromParent();
8214 Builder.SetInsertPoint(InsertBB);
8215
8216 return Builder.saveIP();
8217}
8218
8219OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry(
8220 Directive OMPD, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) {
8221 // if nothing to do, Return current insertion point.
8222 if (!Conditional || !EntryCall)
8223 return Builder.saveIP();
8224
8225 BasicBlock *EntryBB = Builder.GetInsertBlock();
8226 Value *CallBool = Builder.CreateIsNotNull(EntryCall);
8227 auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body");
8228 auto *UI = new UnreachableInst(Builder.getContext(), ThenBB);
8229
8230 // Emit thenBB and set the Builder's insertion point there for
8231 // body generation next. Place the block after the current block.
8232 Function *CurFn = EntryBB->getParent();
8233 CurFn->insert(std::next(EntryBB->getIterator()), ThenBB);
8234
8235 // Move Entry branch to end of ThenBB, and replace with conditional
8236 // branch (If-stmt)
8237 Instruction *EntryBBTI = EntryBB->getTerminator();
8238 Builder.CreateCondBr(CallBool, ThenBB, ExitBB);
8239 EntryBBTI->removeFromParent();
8240 Builder.SetInsertPoint(UI);
8241 Builder.Insert(EntryBBTI);
8242 UI->eraseFromParent();
8243 Builder.SetInsertPoint(ThenBB->getTerminator());
8244
8245 // return an insertion point to ExitBB.
8246 return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt());
8247}
8248
8249OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::emitCommonDirectiveExit(
8250 omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall,
8251 bool HasFinalize) {
8252
8253 Builder.restoreIP(FinIP);
8254
8255 // If there is finalization to do, emit it before the exit call
8256 if (HasFinalize) {
8257 assert(!FinalizationStack.empty() &&
8258 "Unexpected finalization stack state!");
8259
8260 FinalizationInfo Fi = FinalizationStack.pop_back_val();
8261 assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!");
8262
8263 if (Error Err = Fi.mergeFiniBB(Builder, FinIP.getBlock()))
8264 return std::move(Err);
8265
8266 // Exit condition: insertion point is before the terminator of the new Fini
8267 // block
8268 Builder.SetInsertPoint(FinIP.getBlock()->getTerminator());
8269 }
8270
8271 if (!ExitCall)
8272 return Builder.saveIP();
8273
8274 // place the Exitcall as last instruction before Finalization block terminator
8275 ExitCall->removeFromParent();
8276 Builder.Insert(ExitCall);
8277
8278 return IRBuilder<>::InsertPoint(ExitCall->getParent(),
8279 ExitCall->getIterator());
8280}
8281
8283 InsertPointTy IP, Value *MasterAddr, Value *PrivateAddr,
8284 llvm::IntegerType *IntPtrTy, bool BranchtoEnd) {
8285 if (!IP.isSet())
8286 return IP;
8287
8289
8290 // creates the following CFG structure
8291 // OMP_Entry : (MasterAddr != PrivateAddr)?
8292 // F T
8293 // | \
8294 // | copin.not.master
8295 // | /
8296 // v /
8297 // copyin.not.master.end
8298 // |
8299 // v
8300 // OMP.Entry.Next
8301
8302 BasicBlock *OMP_Entry = IP.getBlock();
8303 Function *CurFn = OMP_Entry->getParent();
8304 BasicBlock *CopyBegin =
8305 BasicBlock::Create(M.getContext(), "copyin.not.master", CurFn);
8306 BasicBlock *CopyEnd = nullptr;
8307
8308 // If entry block is terminated, split to preserve the branch to following
8309 // basic block (i.e. OMP.Entry.Next), otherwise, leave everything as is.
8311 CopyEnd = OMP_Entry->splitBasicBlock(OMP_Entry->getTerminator(),
8312 "copyin.not.master.end");
8313 OMP_Entry->getTerminator()->eraseFromParent();
8314 } else {
8315 CopyEnd =
8316 BasicBlock::Create(M.getContext(), "copyin.not.master.end", CurFn);
8317 }
8318
8319 Builder.SetInsertPoint(OMP_Entry);
8320 Value *MasterPtr = Builder.CreatePtrToInt(MasterAddr, IntPtrTy);
8321 Value *PrivatePtr = Builder.CreatePtrToInt(PrivateAddr, IntPtrTy);
8322 Value *cmp = Builder.CreateICmpNE(MasterPtr, PrivatePtr);
8323 Builder.CreateCondBr(cmp, CopyBegin, CopyEnd);
8324
8325 Builder.SetInsertPoint(CopyBegin);
8326 if (BranchtoEnd)
8327 Builder.SetInsertPoint(Builder.CreateBr(CopyEnd));
8328
8329 return Builder.saveIP();
8330}
8331
8333 Value *Size, Value *Allocator,
8334 std::string Name) {
8336 if (!updateToLocation(Loc))
8337 return nullptr;
8338
8339 uint32_t SrcLocStrSize;
8340 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8341 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8342 Value *ThreadId = getOrCreateThreadID(Ident);
8343 Value *Args[] = {ThreadId, Size, Allocator};
8344
8345 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc);
8346
8347 return createRuntimeFunctionCall(Fn, Args, Name);
8348}
8349
8351 Value *Align, Value *Size,
8352 Value *Allocator,
8353 std::string Name) {
8355 if (!updateToLocation(Loc))
8356 return nullptr;
8357
8358 uint32_t SrcLocStrSize;
8359 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8360 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8361 Value *ThreadId = getOrCreateThreadID(Ident);
8362 Value *Args[] = {ThreadId, Align, Size, Allocator};
8363
8364 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_aligned_alloc);
8365
8366 return Builder.CreateCall(Fn, Args, Name);
8367}
8368
8370 Value *Addr, Value *Allocator,
8371 std::string Name) {
8373 if (!updateToLocation(Loc))
8374 return nullptr;
8375
8376 uint32_t SrcLocStrSize;
8377 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8378 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8379 Value *ThreadId = getOrCreateThreadID(Ident);
8380 Value *Args[] = {ThreadId, Addr, Allocator};
8381 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free);
8382 return createRuntimeFunctionCall(Fn, Args, Name);
8383}
8384
8386 Value *Size,
8387 const Twine &Name) {
8390
8391 Value *Args[] = {Size};
8392 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_alloc_shared);
8393 CallInst *Call = Builder.CreateCall(Fn, Args, Name);
8395 M.getContext(), M.getDataLayout().getPrefTypeAlign(Int64)));
8396 return Call;
8397}
8398
8400 Type *VarType,
8401 const Twine &Name) {
8402 return createOMPAllocShared(
8403 Loc, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)), Name);
8404}
8405
8407 Value *Addr, Value *Size,
8408 const Twine &Name) {
8411
8412 Value *Args[] = {Addr, Size};
8413 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_free_shared);
8414 return Builder.CreateCall(Fn, Args, Name);
8415}
8416
8418 Value *Addr, Type *VarType,
8419 const Twine &Name) {
8420 return createOMPFreeShared(
8421 Loc, Addr, Builder.getInt64(M.getDataLayout().getTypeAllocSize(VarType)),
8422 Name);
8423}
8424
8426 const LocationDescription &Loc, Value *InteropVar,
8428 Value *DependenceAddress, bool HaveNowaitClause) {
8431
8432 uint32_t SrcLocStrSize;
8433 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8434 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8435 Value *ThreadId = getOrCreateThreadID(Ident);
8436 if (Device == nullptr)
8438 else if (Device->getType() != Int32)
8439 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8440 Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
8441 if (NumDependences == nullptr) {
8442 NumDependences = ConstantInt::get(Int32, 0);
8443 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8444 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8445 }
8446 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8447 Value *Args[] = {
8448 Ident, ThreadId, InteropVar, InteropTypeVal,
8449 Device, NumDependences, DependenceAddress, HaveNowaitClauseVal};
8450
8451 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_init);
8452
8453 return createRuntimeFunctionCall(Fn, Args);
8454}
8455
8457 const LocationDescription &Loc, Value *InteropVar, Value *Device,
8458 Value *NumDependences, Value *DependenceAddress, bool HaveNowaitClause) {
8461
8462 uint32_t SrcLocStrSize;
8463 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8464 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8465 Value *ThreadId = getOrCreateThreadID(Ident);
8466 if (Device == nullptr)
8468 else if (Device->getType() != Int32)
8469 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8470 if (NumDependences == nullptr) {
8471 NumDependences = ConstantInt::get(Int32, 0);
8472 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8473 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8474 }
8475 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8476 Value *Args[] = {
8477 Ident, ThreadId, InteropVar, Device,
8478 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8479
8480 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_destroy);
8481
8482 return createRuntimeFunctionCall(Fn, Args);
8483}
8484
8486 Value *InteropVar, Value *Device,
8487 Value *NumDependences,
8488 Value *DependenceAddress,
8489 bool HaveNowaitClause) {
8492 uint32_t SrcLocStrSize;
8493 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8494 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8495 Value *ThreadId = getOrCreateThreadID(Ident);
8496 if (Device == nullptr)
8498 else if (Device->getType() != Int32)
8499 Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
8500 if (NumDependences == nullptr) {
8501 NumDependences = ConstantInt::get(Int32, 0);
8502 PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
8503 DependenceAddress = ConstantPointerNull::get(PointerTypeVar);
8504 }
8505 Value *HaveNowaitClauseVal = ConstantInt::get(Int32, HaveNowaitClause);
8506 Value *Args[] = {
8507 Ident, ThreadId, InteropVar, Device,
8508 NumDependences, DependenceAddress, HaveNowaitClauseVal};
8509
8510 Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___tgt_interop_use);
8511
8512 return createRuntimeFunctionCall(Fn, Args);
8513}
8514
8517 llvm::ConstantInt *Size, const llvm::Twine &Name) {
8520
8521 uint32_t SrcLocStrSize;
8522 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8523 Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8524 Value *ThreadId = getOrCreateThreadID(Ident);
8525 Constant *ThreadPrivateCache =
8526 getOrCreateInternalVariable(Int8PtrPtr, Name.str());
8527 llvm::Value *Args[] = {Ident, ThreadId, Pointer, Size, ThreadPrivateCache};
8528
8529 Function *Fn =
8530 getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_threadprivate_cached);
8531
8532 return createRuntimeFunctionCall(Fn, Args);
8533}
8534
8536 const LocationDescription &Loc,
8538 assert(!Attrs.MaxThreads.empty() && !Attrs.MaxTeams.empty() &&
8539 "expected num_threads and num_teams to be specified");
8540
8541 if (!updateToLocation(Loc))
8542 return Loc.IP;
8543
8544 uint32_t SrcLocStrSize;
8545 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8546 Constant *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8547 Constant *IsSPMDVal = ConstantInt::getSigned(Int8, Attrs.ExecFlags);
8548 Constant *UseGenericStateMachineVal = ConstantInt::getSigned(
8549 Int8, Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD &&
8550 Attrs.ExecFlags != omp::OMP_TGT_EXEC_MODE_SPMD_NO_LOOP);
8551 Constant *MayUseNestedParallelismVal = ConstantInt::getSigned(Int8, true);
8552 Constant *DebugIndentionLevelVal = ConstantInt::getSigned(Int16, 0);
8553
8554 Function *DebugKernelWrapper = Builder.GetInsertBlock()->getParent();
8555 Function *Kernel = DebugKernelWrapper;
8556
8557 // We need to strip the debug prefix to get the correct kernel name.
8558 StringRef KernelName = Kernel->getName();
8559 const std::string DebugPrefix = "_debug__";
8560 if (KernelName.ends_with(DebugPrefix)) {
8561 KernelName = KernelName.drop_back(DebugPrefix.length());
8562 Kernel = M.getFunction(KernelName);
8563 assert(Kernel && "Expected the real kernel to exist");
8564 }
8565
8566 // Manifest the launch configuration in the metadata matching the kernel
8567 // environment.
8568 if (Attrs.MinTeams.front() > 1 || Attrs.MaxTeams.front() > 0)
8569 writeTeamsForKernel(T, *Kernel, Attrs.MinTeams.front(),
8570 Attrs.MaxTeams.front());
8571
8572 // If MaxThreads is not set and needs adjustment, select the maximum between
8573 // the default workgroup size and the MinThreads value.
8574 int32_t MaxThreadsVal = Attrs.MaxThreads.front();
8575 if (MaxThreadsVal < 0 && UseDefaultMaxThreads) {
8576 if (hasGridValue(T)) {
8577 MaxThreadsVal =
8578 std::max(int32_t(getGridValue(T, Kernel).GV_Default_WG_Size),
8579 Attrs.MinThreads.front());
8580 } else {
8581 MaxThreadsVal = Attrs.MinThreads.front();
8582 }
8583 }
8584
8585 if (MaxThreadsVal > 0)
8586 writeThreadBoundsForKernel(T, *Kernel, Attrs.MinThreads.front(),
8587 MaxThreadsVal);
8588
8589 Constant *MinThreads =
8590 ConstantInt::getSigned(Int32, Attrs.MinThreads.front());
8591 Constant *MaxThreads = ConstantInt::getSigned(Int32, MaxThreadsVal);
8592 Constant *MinTeams = ConstantInt::getSigned(Int32, Attrs.MinTeams.front());
8593 Constant *MaxTeams = ConstantInt::getSigned(Int32, Attrs.MaxTeams.front());
8594 Constant *ReductionDataSize =
8595 ConstantInt::getSigned(Int32, Attrs.ReductionDataSize);
8596
8598 omp::RuntimeFunction::OMPRTL___kmpc_target_init);
8599 const DataLayout &DL = Fn->getDataLayout();
8600
8601 Twine DynamicEnvironmentName = KernelName + "_dynamic_environment";
8602 Constant *DynamicEnvironmentInitializer =
8603 ConstantStruct::get(DynamicEnvironment, {DebugIndentionLevelVal});
8604 GlobalVariable *DynamicEnvironmentGV = new GlobalVariable(
8605 M, DynamicEnvironment, /*IsConstant=*/false, GlobalValue::WeakODRLinkage,
8606 DynamicEnvironmentInitializer, DynamicEnvironmentName,
8607 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8608 DL.getDefaultGlobalsAddressSpace());
8609 DynamicEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8610
8611 Constant *DynamicEnvironment =
8612 DynamicEnvironmentGV->getType() == DynamicEnvironmentPtr
8613 ? DynamicEnvironmentGV
8614 : ConstantExpr::getAddrSpaceCast(DynamicEnvironmentGV,
8615 DynamicEnvironmentPtr);
8616
8617 Constant *ConfigurationEnvironmentInitializer = ConstantStruct::get(
8618 ConfigurationEnvironment, {
8619 UseGenericStateMachineVal,
8620 MayUseNestedParallelismVal,
8621 IsSPMDVal,
8622 MinThreads,
8623 MaxThreads,
8624 MinTeams,
8625 MaxTeams,
8626 ReductionDataSize,
8627 });
8628 Constant *KernelEnvironmentInitializer = ConstantStruct::get(
8629 KernelEnvironment, {
8630 ConfigurationEnvironmentInitializer,
8631 Ident,
8632 DynamicEnvironment,
8633 });
8634 std::string KernelEnvironmentName =
8635 (KernelName + "_kernel_environment").str();
8636 GlobalVariable *KernelEnvironmentGV = new GlobalVariable(
8637 M, KernelEnvironment, /*IsConstant=*/true, GlobalValue::WeakODRLinkage,
8638 KernelEnvironmentInitializer, KernelEnvironmentName,
8639 /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal,
8640 DL.getDefaultGlobalsAddressSpace());
8641 KernelEnvironmentGV->setVisibility(GlobalValue::ProtectedVisibility);
8642
8643 Constant *KernelEnvironment =
8644 KernelEnvironmentGV->getType() == KernelEnvironmentPtr
8645 ? KernelEnvironmentGV
8646 : ConstantExpr::getAddrSpaceCast(KernelEnvironmentGV,
8647 KernelEnvironmentPtr);
8648 Value *KernelLaunchEnvironment =
8649 DebugKernelWrapper->getArg(DebugKernelWrapper->arg_size() - 1);
8650 Type *KernelLaunchEnvParamTy = Fn->getFunctionType()->getParamType(1);
8651 KernelLaunchEnvironment =
8652 KernelLaunchEnvironment->getType() == KernelLaunchEnvParamTy
8653 ? KernelLaunchEnvironment
8654 : Builder.CreateAddrSpaceCast(KernelLaunchEnvironment,
8655 KernelLaunchEnvParamTy);
8656 CallInst *ThreadKind = createRuntimeFunctionCall(
8657 Fn, {KernelEnvironment, KernelLaunchEnvironment});
8658
8659 Value *ExecUserCode = Builder.CreateICmpEQ(
8660 ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()),
8661 "exec_user_code");
8662
8663 // ThreadKind = __kmpc_target_init(...)
8664 // if (ThreadKind == -1)
8665 // user_code
8666 // else
8667 // return;
8668
8669 auto *UI = Builder.CreateUnreachable();
8670 BasicBlock *CheckBB = UI->getParent();
8671 BasicBlock *UserCodeEntryBB = CheckBB->splitBasicBlock(UI, "user_code.entry");
8672
8673 BasicBlock *WorkerExitBB = BasicBlock::Create(
8674 CheckBB->getContext(), "worker.exit", CheckBB->getParent());
8675 Builder.SetInsertPoint(WorkerExitBB);
8676 Builder.CreateRetVoid();
8677
8678 auto *CheckBBTI = CheckBB->getTerminator();
8679 Builder.SetInsertPoint(CheckBBTI);
8680 Builder.CreateCondBr(ExecUserCode, UI->getParent(), WorkerExitBB);
8681
8682 CheckBBTI->eraseFromParent();
8683 UI->eraseFromParent();
8684
8685 // Continue in the "user_code" block, see diagram above and in
8686 // openmp/libomptarget/deviceRTLs/common/include/target.h .
8687 return InsertPointTy(UserCodeEntryBB, UserCodeEntryBB->getFirstInsertionPt());
8688}
8689
8691 int32_t TeamsReductionDataSize) {
8692 if (!updateToLocation(Loc))
8693 return;
8694
8696 omp::RuntimeFunction::OMPRTL___kmpc_target_deinit);
8697
8699
8700 if (!TeamsReductionDataSize)
8701 return;
8702
8703 Function *Kernel = Builder.GetInsertBlock()->getParent();
8704 // We need to strip the debug prefix to get the correct kernel name.
8705 StringRef KernelName = Kernel->getName();
8706 const std::string DebugPrefix = "_debug__";
8707 if (KernelName.ends_with(DebugPrefix))
8708 KernelName = KernelName.drop_back(DebugPrefix.length());
8709 auto *KernelEnvironmentGV =
8710 M.getNamedGlobal((KernelName + "_kernel_environment").str());
8711 assert(KernelEnvironmentGV && "Expected kernel environment global\n");
8712 auto *KernelEnvironmentInitializer = KernelEnvironmentGV->getInitializer();
8713 auto *NewInitializer = ConstantFoldInsertValueInstruction(
8714 KernelEnvironmentInitializer,
8715 ConstantInt::get(Int32, TeamsReductionDataSize), {0, 7});
8716 KernelEnvironmentGV->setInitializer(NewInitializer);
8717}
8718
8719static void updateNVPTXAttr(Function &Kernel, StringRef Name, int32_t Value,
8720 bool Min) {
8721 if (Kernel.hasFnAttribute(Name)) {
8722 int32_t OldLimit = Kernel.getFnAttributeAsParsedInteger(Name);
8723 Value = Min ? std::min(OldLimit, Value) : std::max(OldLimit, Value);
8724 }
8725 Kernel.addFnAttr(Name, llvm::utostr(Value));
8726}
8727
8728std::pair<int32_t, int32_t>
8730 int32_t ThreadLimit =
8731 Kernel.getFnAttributeAsParsedInteger("omp_target_thread_limit");
8732
8733 if (T.isAMDGPU()) {
8734 const auto &Attr = Kernel.getFnAttribute("amdgpu-flat-work-group-size");
8735 if (!Attr.isValid() || !Attr.isStringAttribute())
8736 return {0, ThreadLimit};
8737 auto [LBStr, UBStr] = Attr.getValueAsString().split(',');
8738 int32_t LB, UB;
8739 if (!llvm::to_integer(UBStr, UB, 10))
8740 return {0, ThreadLimit};
8741 UB = ThreadLimit ? std::min(ThreadLimit, UB) : UB;
8742 if (!llvm::to_integer(LBStr, LB, 10))
8743 return {0, UB};
8744 return {LB, UB};
8745 }
8746
8747 if (Kernel.hasFnAttribute(NVVMAttr::MaxNTID)) {
8748 int32_t UB = Kernel.getFnAttributeAsParsedInteger(NVVMAttr::MaxNTID);
8749 return {0, ThreadLimit ? std::min(ThreadLimit, UB) : UB};
8750 }
8751 return {0, ThreadLimit};
8752}
8753
8755 Function &Kernel, int32_t LB,
8756 int32_t UB) {
8757 Kernel.addFnAttr("omp_target_thread_limit", std::to_string(UB));
8758
8759 if (T.isAMDGPU()) {
8760 Kernel.addFnAttr("amdgpu-flat-work-group-size",
8761 llvm::utostr(LB) + "," + llvm::utostr(UB));
8762 return;
8763 }
8764
8766}
8767
8768std::pair<int32_t, int32_t>
8770 // TODO: Read from backend annotations if available.
8771 return {0, Kernel.getFnAttributeAsParsedInteger("omp_target_num_teams")};
8772}
8773
8775 int32_t LB, int32_t UB) {
8776 if (UB > 0) {
8777 if (T.isNVPTX())
8779 if (T.isAMDGPU())
8780 Kernel.addFnAttr("amdgpu-max-num-workgroups", llvm::utostr(UB) + ",1,1");
8781 }
8782
8783 Kernel.addFnAttr("omp_target_num_teams", std::to_string(LB));
8784}
8785
8786void OpenMPIRBuilder::setOutlinedTargetRegionFunctionAttributes(
8787 Function *OutlinedFn) {
8788 if (Config.isTargetDevice()) {
8790 // TODO: Determine if DSO local can be set to true.
8791 OutlinedFn->setDSOLocal(false);
8793 if (T.isAMDGCN())
8795 else if (T.isNVPTX())
8797 else if (T.isSPIRV())
8799 }
8800}
8801
8802Constant *OpenMPIRBuilder::createOutlinedFunctionID(Function *OutlinedFn,
8803 StringRef EntryFnIDName) {
8804 if (Config.isTargetDevice()) {
8805 assert(OutlinedFn && "The outlined function must exist if embedded");
8806 return OutlinedFn;
8807 }
8808
8809 return new GlobalVariable(
8810 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::WeakAnyLinkage,
8811 Constant::getNullValue(Builder.getInt8Ty()), EntryFnIDName);
8812}
8813
8814Constant *OpenMPIRBuilder::createTargetRegionEntryAddr(Function *OutlinedFn,
8815 StringRef EntryFnName) {
8816 if (OutlinedFn)
8817 return OutlinedFn;
8818
8819 assert(!M.getGlobalVariable(EntryFnName, true) &&
8820 "Named kernel already exists?");
8821 return new GlobalVariable(
8822 M, Builder.getInt8Ty(), /*isConstant=*/true, GlobalValue::InternalLinkage,
8823 Constant::getNullValue(Builder.getInt8Ty()), EntryFnName);
8824}
8825
8827 TargetRegionEntryInfo &EntryInfo,
8828 FunctionGenCallback &GenerateFunctionCallback, bool IsOffloadEntry,
8829 Function *&OutlinedFn, Constant *&OutlinedFnID) {
8830
8831 SmallString<64> EntryFnName;
8832 OffloadInfoManager.getTargetRegionEntryFnName(EntryFnName, EntryInfo);
8833
8834 if (Config.isTargetDevice() || !Config.openMPOffloadMandatory()) {
8835 Expected<Function *> CBResult = GenerateFunctionCallback(EntryFnName);
8836 if (!CBResult)
8837 return CBResult.takeError();
8838 OutlinedFn = *CBResult;
8839 } else {
8840 OutlinedFn = nullptr;
8841 }
8842
8843 // If this target outline function is not an offload entry, we don't need to
8844 // register it. This may be in the case of a false if clause, or if there are
8845 // no OpenMP targets.
8846 if (!IsOffloadEntry)
8847 return Error::success();
8848
8849 std::string EntryFnIDName =
8850 Config.isTargetDevice()
8851 ? std::string(EntryFnName)
8852 : createPlatformSpecificName({EntryFnName, "region_id"});
8853
8854 OutlinedFnID = registerTargetRegionFunction(EntryInfo, OutlinedFn,
8855 EntryFnName, EntryFnIDName);
8856 return Error::success();
8857}
8858
8860 TargetRegionEntryInfo &EntryInfo, Function *OutlinedFn,
8861 StringRef EntryFnName, StringRef EntryFnIDName) {
8862 if (OutlinedFn)
8863 setOutlinedTargetRegionFunctionAttributes(OutlinedFn);
8864 auto OutlinedFnID = createOutlinedFunctionID(OutlinedFn, EntryFnIDName);
8865 auto EntryAddr = createTargetRegionEntryAddr(OutlinedFn, EntryFnName);
8866 OffloadInfoManager.registerTargetRegionEntryInfo(
8867 EntryInfo, EntryAddr, OutlinedFnID,
8869 return OutlinedFnID;
8870}
8871
8873 const LocationDescription &Loc, InsertPointTy AllocaIP,
8874 InsertPointTy CodeGenIP, ArrayRef<BasicBlock *> DeallocBlocks,
8875 Value *DeviceID, Value *IfCond, TargetDataInfo &Info,
8876 GenMapInfoCallbackTy GenMapInfoCB, CustomMapperCallbackTy CustomMapperCB,
8877 omp::RuntimeFunction *MapperFunc,
8879 BodyGenTy BodyGenType)>
8880 BodyGenCB,
8881 function_ref<void(unsigned int, Value *)> DeviceAddrCB, Value *SrcLocInfo) {
8882 if (!updateToLocation(Loc))
8883 return InsertPointTy();
8884
8885 Builder.restoreIP(CodeGenIP);
8886
8887 bool IsStandAlone = !BodyGenCB;
8888 MapInfosTy *MapInfo;
8889 // Generate the code for the opening of the data environment. Capture all the
8890 // arguments of the runtime call by reference because they are used in the
8891 // closing of the region.
8892 auto BeginThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8893 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8894 MapInfo = &GenMapInfoCB(Builder.saveIP());
8895 if (Error Err = emitOffloadingArrays(
8896 AllocaIP, Builder.saveIP(), *MapInfo, Info, CustomMapperCB,
8897 /*IsNonContiguous=*/true, DeviceAddrCB))
8898 return Err;
8899
8900 TargetDataRTArgs RTArgs;
8902
8903 // Emit the number of elements in the offloading arrays.
8904 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
8905
8906 // Source location for the ident struct
8907 if (!SrcLocInfo) {
8908 uint32_t SrcLocStrSize;
8909 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
8910 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
8911 }
8912
8913 SmallVector<llvm::Value *, 13> OffloadingArgs = {
8914 SrcLocInfo, DeviceID,
8915 PointerNum, RTArgs.BasePointersArray,
8916 RTArgs.PointersArray, RTArgs.SizesArray,
8917 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
8918 RTArgs.MappersArray};
8919
8920 if (IsStandAlone) {
8921 assert(MapperFunc && "MapperFunc missing for standalone target data");
8922
8923 auto TaskBodyCB = [&](Value *, Value *,
8925 if (Info.HasNoWait) {
8926 OffloadingArgs.append({llvm::Constant::getNullValue(Int32),
8930 }
8931
8933 OffloadingArgs);
8934
8935 if (Info.HasNoWait) {
8936 BasicBlock *OffloadContBlock =
8937 BasicBlock::Create(Builder.getContext(), "omp_offload.cont");
8938 Function *CurFn = Builder.GetInsertBlock()->getParent();
8939 emitBlock(OffloadContBlock, CurFn, /*IsFinished=*/true);
8940 Builder.restoreIP(Builder.saveIP());
8941 }
8942 return Error::success();
8943 };
8944
8945 bool RequiresOuterTargetTask = Info.HasNoWait;
8946 if (!RequiresOuterTargetTask)
8947 cantFail(TaskBodyCB(/*DeviceID=*/nullptr, /*RTLoc=*/nullptr,
8948 /*TargetTaskAllocaIP=*/{}));
8949 else
8950 cantFail(emitTargetTask(TaskBodyCB, DeviceID, SrcLocInfo, AllocaIP,
8951 /*Dependencies=*/{}, RTArgs, Info.HasNoWait));
8952 } else {
8953 Function *BeginMapperFunc = getOrCreateRuntimeFunctionPtr(
8954 omp::OMPRTL___tgt_target_data_begin_mapper);
8955
8956 createRuntimeFunctionCall(BeginMapperFunc, OffloadingArgs);
8957
8958 for (auto DeviceMap : Info.DevicePtrInfoMap) {
8959 if (isa<AllocaInst>(DeviceMap.second.second)) {
8960 auto *LI =
8961 Builder.CreateLoad(Builder.getPtrTy(), DeviceMap.second.first);
8962 Builder.CreateStore(LI, DeviceMap.second.second);
8963 }
8964 }
8965
8966 // If device pointer privatization is required, emit the body of the
8967 // region here. It will have to be duplicated: with and without
8968 // privatization.
8969 InsertPointOrErrorTy AfterIP =
8970 BodyGenCB(Builder.saveIP(), BodyGenTy::Priv);
8971 if (!AfterIP)
8972 return AfterIP.takeError();
8973 Builder.restoreIP(*AfterIP);
8974 }
8975 return Error::success();
8976 };
8977
8978 // If we need device pointer privatization, we need to emit the body of the
8979 // region with no privatization in the 'else' branch of the conditional.
8980 // Otherwise, we don't have to do anything.
8981 auto BeginElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8982 ArrayRef<BasicBlock *> DeallocBlocks) -> Error {
8983 InsertPointOrErrorTy AfterIP =
8984 BodyGenCB(Builder.saveIP(), BodyGenTy::DupNoPriv);
8985 if (!AfterIP)
8986 return AfterIP.takeError();
8987 Builder.restoreIP(*AfterIP);
8988 return Error::success();
8989 };
8990
8991 // Generate code for the closing of the data region.
8992 auto EndThenGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
8993 ArrayRef<BasicBlock *> DeallocBlocks) {
8994 TargetDataRTArgs RTArgs;
8995 Info.EmitDebug = !MapInfo->Names.empty();
8996 emitOffloadingArraysArgument(Builder, RTArgs, Info, /*ForEndCall=*/true);
8997
8998 // Emit the number of elements in the offloading arrays.
8999 Value *PointerNum = Builder.getInt32(Info.NumberOfPtrs);
9000
9001 // Source location for the ident struct
9002 if (!SrcLocInfo) {
9003 uint32_t SrcLocStrSize;
9004 Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
9005 SrcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
9006 }
9007
9008 Value *OffloadingArgs[] = {SrcLocInfo, DeviceID,
9009 PointerNum, RTArgs.BasePointersArray,
9010 RTArgs.PointersArray, RTArgs.SizesArray,
9011 RTArgs.MapTypesArray, RTArgs.MapNamesArray,
9012 RTArgs.MappersArray};
9013 Function *EndMapperFunc =
9014 getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
9015
9016 createRuntimeFunctionCall(EndMapperFunc, OffloadingArgs);
9017 return Error::success();
9018 };
9019
9020 // We don't have to do anything to close the region if the if clause evaluates
9021 // to false.
9022 auto EndElseGen = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
9023 ArrayRef<BasicBlock *> DeallocBlocks) {
9024 return Error::success();
9025 };
9026
9027 Error Err = [&]() -> Error {
9028 if (BodyGenCB) {
9029 Error Err = [&]() {
9030 if (IfCond)
9031 return emitIfClause(IfCond, BeginThenGen, BeginElseGen, AllocaIP);
9032 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9033 }();
9034
9035 if (Err)
9036 return Err;
9037
9038 // If we don't require privatization of device pointers, we emit the body
9039 // in between the runtime calls. This avoids duplicating the body code.
9040 InsertPointOrErrorTy AfterIP =
9041 BodyGenCB(Builder.saveIP(), BodyGenTy::NoPriv);
9042 if (!AfterIP)
9043 return AfterIP.takeError();
9044 restoreIPandDebugLoc(Builder, *AfterIP);
9045
9046 if (IfCond)
9047 return emitIfClause(IfCond, EndThenGen, EndElseGen, AllocaIP);
9048 return EndThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9049 }
9050 if (IfCond)
9051 return emitIfClause(IfCond, BeginThenGen, EndElseGen, AllocaIP);
9052 return BeginThenGen(AllocaIP, Builder.saveIP(), DeallocBlocks);
9053 }();
9054
9055 if (Err)
9056 return Err;
9057
9058 return Builder.saveIP();
9059}
9060
9063 bool IsGPUDistribute) {
9064 assert((IVSize == 32 || IVSize == 64) &&
9065 "IV size is not compatible with the omp runtime");
9066 RuntimeFunction Name;
9067 if (IsGPUDistribute)
9068 Name = IVSize == 32
9069 ? (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_4
9070 : omp::OMPRTL___kmpc_distribute_static_init_4u)
9071 : (IVSigned ? omp::OMPRTL___kmpc_distribute_static_init_8
9072 : omp::OMPRTL___kmpc_distribute_static_init_8u);
9073 else
9074 Name = IVSize == 32 ? (IVSigned ? omp::OMPRTL___kmpc_for_static_init_4
9075 : omp::OMPRTL___kmpc_for_static_init_4u)
9076 : (IVSigned ? omp::OMPRTL___kmpc_for_static_init_8
9077 : omp::OMPRTL___kmpc_for_static_init_8u);
9078
9079 return getOrCreateRuntimeFunction(M, Name);
9080}
9081
9083 bool IVSigned) {
9084 assert((IVSize == 32 || IVSize == 64) &&
9085 "IV size is not compatible with the omp runtime");
9086 RuntimeFunction Name = IVSize == 32
9087 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_4
9088 : omp::OMPRTL___kmpc_dispatch_init_4u)
9089 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_init_8
9090 : omp::OMPRTL___kmpc_dispatch_init_8u);
9091
9092 return getOrCreateRuntimeFunction(M, Name);
9093}
9094
9096 bool IVSigned) {
9097 assert((IVSize == 32 || IVSize == 64) &&
9098 "IV size is not compatible with the omp runtime");
9099 RuntimeFunction Name = IVSize == 32
9100 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_4
9101 : omp::OMPRTL___kmpc_dispatch_next_4u)
9102 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_next_8
9103 : omp::OMPRTL___kmpc_dispatch_next_8u);
9104
9105 return getOrCreateRuntimeFunction(M, Name);
9106}
9107
9109 bool IVSigned) {
9110 assert((IVSize == 32 || IVSize == 64) &&
9111 "IV size is not compatible with the omp runtime");
9112 RuntimeFunction Name = IVSize == 32
9113 ? (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_4
9114 : omp::OMPRTL___kmpc_dispatch_fini_4u)
9115 : (IVSigned ? omp::OMPRTL___kmpc_dispatch_fini_8
9116 : omp::OMPRTL___kmpc_dispatch_fini_8u);
9117
9118 return getOrCreateRuntimeFunction(M, Name);
9119}
9120
9122 return getOrCreateRuntimeFunction(M, omp::OMPRTL___kmpc_dispatch_deinit);
9123}
9124
9126 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder, Function *Func,
9127 DenseMap<Value *, std::tuple<Value *, unsigned>> &ValueReplacementMap) {
9128
9129 DISubprogram *NewSP = Func->getSubprogram();
9130 if (!NewSP)
9131 return;
9132
9134
9135 auto GetUpdatedDIVariable = [&](DILocalVariable *OldVar, unsigned arg) {
9136 DILocalVariable *&NewVar = RemappedVariables[OldVar];
9137 // Only use cached variable if the arg number matches. This is important
9138 // so that DIVariable created for privatized variables are not discarded.
9139 if (NewVar && (arg == NewVar->getArg()))
9140 return NewVar;
9141
9143 Builder.getContext(), OldVar->getScope(), OldVar->getName(),
9144 OldVar->getFile(), OldVar->getLine(), OldVar->getType(), arg,
9145 OldVar->getFlags(), OldVar->getAlignInBits(), OldVar->getAnnotations());
9146 return NewVar;
9147 };
9148
9149 auto UpdateDebugRecord = [&](auto *DR) {
9150 DILocalVariable *OldVar = DR->getVariable();
9151 unsigned ArgNo = 0;
9152 for (auto Loc : DR->location_ops()) {
9153 auto Iter = ValueReplacementMap.find(Loc);
9154 if (Iter != ValueReplacementMap.end()) {
9155 DR->replaceVariableLocationOp(Loc, std::get<0>(Iter->second));
9156 ArgNo = std::get<1>(Iter->second) + 1;
9157 }
9158 }
9159 if (ArgNo != 0)
9160 DR->setVariable(GetUpdatedDIVariable(OldVar, ArgNo));
9161 };
9162
9164 auto MoveDebugRecordToCorrectBlock = [&](DbgVariableRecord *DVR) {
9165 if (DVR->getNumVariableLocationOps() != 1u) {
9166 DVR->setKillLocation();
9167 return;
9168 }
9169 Value *Loc = DVR->getVariableLocationOp(0u);
9170 BasicBlock *CurBB = DVR->getParent();
9171 BasicBlock *RequiredBB = nullptr;
9172
9173 if (Instruction *LocInst = dyn_cast<Instruction>(Loc))
9174 RequiredBB = LocInst->getParent();
9175 else if (isa<llvm::Argument>(Loc))
9176 RequiredBB = &DVR->getFunction()->getEntryBlock();
9177
9178 if (RequiredBB && RequiredBB != CurBB) {
9179 assert(!RequiredBB->empty());
9180 RequiredBB->insertDbgRecordBefore(DVR->clone(),
9181 RequiredBB->back().getIterator());
9182 DVRsToDelete.push_back(DVR);
9183 }
9184 };
9185
9186 // The location and scope of variable intrinsics and records still point to
9187 // the parent function of the target region. Update them.
9188 for (Instruction &I : instructions(Func)) {
9190 "Unexpected debug intrinsic");
9191 for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
9192 UpdateDebugRecord(&DVR);
9193 MoveDebugRecordToCorrectBlock(&DVR);
9194 }
9195 }
9196 for (auto *DVR : DVRsToDelete)
9197 DVR->getMarker()->MarkedInstr->dropOneDbgRecord(DVR);
9198 // An extra argument is passed to the device. Create the debug data for it.
9199 if (OMPBuilder.Config.isTargetDevice()) {
9200 DICompileUnit *CU = NewSP->getUnit();
9201 Module *M = Func->getParent();
9202 DIBuilder DB(*M, true, CU);
9203 DIType *VoidPtrTy =
9204 DB.createQualifiedType(dwarf::DW_TAG_pointer_type, nullptr);
9205 unsigned ArgNo = Func->arg_size();
9206 DILocalVariable *Var = DB.createParameterVariable(
9207 NewSP, "dyn_ptr", ArgNo, NewSP->getFile(), /*LineNo=*/0, VoidPtrTy,
9208 /*AlwaysPreserve=*/false, DINode::DIFlags::FlagArtificial);
9209 auto Loc = DILocation::get(Func->getContext(), 0, 0, NewSP, 0);
9210 Argument *LastArg = Func->getArg(Func->arg_size() - 1);
9211 DB.insertDeclare(LastArg, Var, DB.createExpression(), Loc,
9212 &(*Func->begin()));
9213 }
9214}
9215
9217 if (Operator::getOpcode(V) == Instruction::AddrSpaceCast)
9218 return cast<Operator>(V)->getOperand(0);
9219 return V;
9220}
9221
9223 OpenMPIRBuilder &OMPBuilder, IRBuilderBase &Builder,
9225 StringRef FuncName, SmallVectorImpl<Value *> &Inputs,
9228 SmallVector<Type *> ParameterTypes;
9229 if (OMPBuilder.Config.isTargetDevice()) {
9230 // All parameters to target devices are passed as pointers
9231 // or i64. This assumes 64-bit address spaces/pointers.
9232 for (auto &Arg : Inputs)
9233 ParameterTypes.push_back(Arg->getType()->isPointerTy()
9234 ? Arg->getType()
9235 : Type::getInt64Ty(Builder.getContext()));
9236 } else {
9237 for (auto &Arg : Inputs)
9238 ParameterTypes.push_back(Arg->getType());
9239 }
9240
9241 // The implicit dyn_ptr argument is always the last parameter on both host
9242 // and device so the argument counts match without runtime manipulation.
9243 auto *PtrTy = PointerType::getUnqual(Builder.getContext());
9244 ParameterTypes.push_back(PtrTy);
9245
9246 auto BB = Builder.GetInsertBlock();
9247 auto M = BB->getModule();
9248 auto FuncType = FunctionType::get(Builder.getVoidTy(), ParameterTypes,
9249 /*isVarArg*/ false);
9250 auto Func =
9251 Function::Create(FuncType, GlobalValue::InternalLinkage, FuncName, M);
9252
9253 // Forward target-cpu and target-features function attributes from the
9254 // original function to the new outlined function.
9255 Function *ParentFn = Builder.GetInsertBlock()->getParent();
9256
9257 auto TargetCpuAttr = ParentFn->getFnAttribute("target-cpu");
9258 if (TargetCpuAttr.isStringAttribute())
9259 Func->addFnAttr(TargetCpuAttr);
9260
9261 auto TargetFeaturesAttr = ParentFn->getFnAttribute("target-features");
9262 if (TargetFeaturesAttr.isStringAttribute())
9263 Func->addFnAttr(TargetFeaturesAttr);
9264
9265 if (OMPBuilder.Config.isTargetDevice()) {
9266 Value *ExecMode =
9267 OMPBuilder.emitKernelExecutionMode(FuncName, DefaultAttrs.ExecFlags);
9268 OMPBuilder.emitUsed("llvm.compiler.used", {ExecMode});
9269 }
9270
9271 // Save insert point.
9272 IRBuilder<>::InsertPointGuard IPG(Builder);
9273 // We will generate the entries in the outlined function but the debug
9274 // location may still be pointing to the parent function. Reset it now.
9275 Builder.SetCurrentDebugLocation(llvm::DebugLoc());
9276