clang 24.0.0git
CGOpenMPRuntimeGPU.cpp
Go to the documentation of this file.
1//===---- CGOpenMPRuntimeGPU.cpp - Interface to OpenMP GPU Runtimes ----===//
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//
9// This provides a generalized class for OpenMP runtime code generation
10// specialized by GPU targets NVPTX, AMDGCN and SPIR-V.
11//
12//===----------------------------------------------------------------------===//
13
14#include "CGOpenMPRuntimeGPU.h"
15#include "CGDebugInfo.h"
16#include "CodeGenFunction.h"
17#include "TargetInfo.h"
18#include "clang/AST/Attr.h"
23#include "llvm/ADT/SmallPtrSet.h"
24#include "llvm/Frontend/OpenMP/OMPDeviceConstants.h"
25#include "llvm/Frontend/OpenMP/OMPGridValues.h"
26#include "llvm/IR/IRBuilder.h"
27#include "llvm/IR/Instructions.h"
28#include "llvm/TargetParser/NVPTXTargetParser.h"
29
30using namespace clang;
31using namespace CodeGen;
32using namespace llvm::omp;
33
34namespace {
35/// Pre(post)-action for different OpenMP constructs specialized for NVPTX.
36class NVPTXActionTy final : public PrePostActionTy {
37 llvm::FunctionCallee EnterCallee = nullptr;
38 ArrayRef<llvm::Value *> EnterArgs;
39 llvm::FunctionCallee ExitCallee = nullptr;
40 ArrayRef<llvm::Value *> ExitArgs;
41 bool Conditional = false;
42 llvm::BasicBlock *ContBlock = nullptr;
43
44public:
45 NVPTXActionTy(llvm::FunctionCallee EnterCallee,
46 ArrayRef<llvm::Value *> EnterArgs,
47 llvm::FunctionCallee ExitCallee,
48 ArrayRef<llvm::Value *> ExitArgs, bool Conditional = false)
49 : EnterCallee(EnterCallee), EnterArgs(EnterArgs), ExitCallee(ExitCallee),
50 ExitArgs(ExitArgs), Conditional(Conditional) {}
51 void Enter(CodeGenFunction &CGF) override {
52 llvm::Value *EnterRes = CGF.EmitRuntimeCall(EnterCallee, EnterArgs);
53 if (Conditional) {
54 llvm::Value *CallBool = CGF.Builder.CreateIsNotNull(EnterRes);
55 auto *ThenBlock = CGF.createBasicBlock("omp_if.then");
56 ContBlock = CGF.createBasicBlock("omp_if.end");
57 // Generate the branch (If-stmt)
58 CGF.Builder.CreateCondBr(CallBool, ThenBlock, ContBlock);
59 CGF.EmitBlock(ThenBlock);
60 }
61 }
62 void Done(CodeGenFunction &CGF) {
63 // Emit the rest of blocks/branches
64 CGF.EmitBranch(ContBlock);
65 CGF.EmitBlock(ContBlock, true);
66 }
67 void Exit(CodeGenFunction &CGF) override {
68 CGF.EmitRuntimeCall(ExitCallee, ExitArgs);
69 }
70};
71
72/// A class to track the execution mode when codegening directives within
73/// a target region. The appropriate mode (SPMD|NON-SPMD) is set on entry
74/// to the target region and used by containing directives such as 'parallel'
75/// to emit optimized code.
76class ExecutionRuntimeModesRAII {
77private:
81
82public:
83 ExecutionRuntimeModesRAII(CGOpenMPRuntimeGPU::ExecutionMode &ExecMode,
85 : ExecMode(ExecMode) {
86 SavedExecMode = ExecMode;
87 ExecMode = EntryMode;
88 }
89 ~ExecutionRuntimeModesRAII() { ExecMode = SavedExecMode; }
90};
91
92static const ValueDecl *getPrivateItem(const Expr *RefExpr) {
93 RefExpr = RefExpr->IgnoreParens();
94 if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(RefExpr)) {
95 const Expr *Base = ASE->getBase()->IgnoreParenImpCasts();
96 while (const auto *TempASE = dyn_cast<ArraySubscriptExpr>(Base))
97 Base = TempASE->getBase()->IgnoreParenImpCasts();
98 RefExpr = Base;
99 } else if (auto *OASE = dyn_cast<ArraySectionExpr>(RefExpr)) {
100 const Expr *Base = OASE->getBase()->IgnoreParenImpCasts();
101 while (const auto *TempOASE = dyn_cast<ArraySectionExpr>(Base))
102 Base = TempOASE->getBase()->IgnoreParenImpCasts();
103 while (const auto *TempASE = dyn_cast<ArraySubscriptExpr>(Base))
104 Base = TempASE->getBase()->IgnoreParenImpCasts();
105 RefExpr = Base;
106 }
107 RefExpr = RefExpr->IgnoreParenImpCasts();
108 if (const auto *DE = dyn_cast<DeclRefExpr>(RefExpr))
109 return cast<ValueDecl>(DE->getDecl()->getCanonicalDecl());
110 const auto *ME = cast<MemberExpr>(RefExpr);
111 return cast<ValueDecl>(ME->getMemberDecl()->getCanonicalDecl());
112}
113
114static RecordDecl *buildRecordForGlobalizedVars(
116 ArrayRef<const ValueDecl *> EscapedDeclsForTeams,
117 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>
118 &MappedDeclsFields,
119 int BufSize) {
120 using VarsDataTy = std::pair<CharUnits /*Align*/, const ValueDecl *>;
121 if (EscapedDecls.empty() && EscapedDeclsForTeams.empty())
122 return nullptr;
123 SmallVector<VarsDataTy, 4> GlobalizedVars;
124 for (const ValueDecl *D : EscapedDecls)
125 GlobalizedVars.emplace_back(C.getDeclAlign(D), D);
126 for (const ValueDecl *D : EscapedDeclsForTeams)
127 GlobalizedVars.emplace_back(C.getDeclAlign(D), D);
128
129 // Build struct _globalized_locals_ty {
130 // /* globalized vars */[WarSize] align (decl_align)
131 // /* globalized vars */ for EscapedDeclsForTeams
132 // };
133 RecordDecl *GlobalizedRD = C.buildImplicitRecord("_globalized_locals_ty");
134 GlobalizedRD->startDefinition();
135 llvm::SmallPtrSet<const ValueDecl *, 16> SingleEscaped(llvm::from_range,
136 EscapedDeclsForTeams);
137 for (const auto &Pair : GlobalizedVars) {
138 const ValueDecl *VD = Pair.second;
139 QualType Type = VD->getType();
141 Type = C.getPointerType(Type.getNonReferenceType());
142 else
143 Type = Type.getNonReferenceType();
144 SourceLocation Loc = VD->getLocation();
145 FieldDecl *Field;
146 if (SingleEscaped.count(VD)) {
147 Field = FieldDecl::Create(
148 C, GlobalizedRD, Loc, Loc, VD->getIdentifier(), Type,
149 C.getTrivialTypeSourceInfo(Type, SourceLocation()),
150 /*BW=*/nullptr, /*Mutable=*/false,
151 /*InitStyle=*/ICIS_NoInit);
152 Field->setAccess(AS_public);
153 if (VD->hasAttrs()) {
154 for (specific_attr_iterator<AlignedAttr> I(VD->getAttrs().begin()),
155 E(VD->getAttrs().end());
156 I != E; ++I)
157 Field->addAttr(*I);
158 }
159 } else {
160 if (BufSize > 1) {
161 llvm::APInt ArraySize(32, BufSize);
162 Type = C.getConstantArrayType(Type, ArraySize, nullptr,
164 }
165 Field = FieldDecl::Create(
166 C, GlobalizedRD, Loc, Loc, VD->getIdentifier(), Type,
167 C.getTrivialTypeSourceInfo(Type, SourceLocation()),
168 /*BW=*/nullptr, /*Mutable=*/false,
169 /*InitStyle=*/ICIS_NoInit);
170 Field->setAccess(AS_public);
171 llvm::APInt Align(32, Pair.first.getQuantity());
172 Field->addAttr(AlignedAttr::CreateImplicit(
173 C, /*IsAlignmentExpr=*/true,
175 C.getIntTypeForBitwidth(32, /*Signed=*/0),
177 {}, AlignedAttr::GNU_aligned));
178 }
179 GlobalizedRD->addDecl(Field);
180 MappedDeclsFields.try_emplace(VD, Field);
181 }
182 GlobalizedRD->completeDefinition();
183 return GlobalizedRD;
184}
185
186/// Get the list of variables that can escape their declaration context.
187class CheckVarsEscapingDeclContext final
188 : public ConstStmtVisitor<CheckVarsEscapingDeclContext> {
189 CodeGenFunction &CGF;
190 llvm::SetVector<const ValueDecl *> EscapedDecls;
191 llvm::SetVector<const ValueDecl *> EscapedVariableLengthDecls;
192 llvm::SetVector<const ValueDecl *> DelayedVariableLengthDecls;
193 llvm::SmallPtrSet<const Decl *, 4> EscapedParameters;
194 RecordDecl *GlobalizedRD = nullptr;
195 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> MappedDeclsFields;
196 bool AllEscaped = false;
197 bool IsForCombinedParallelRegion = false;
198
199 void markAsEscaped(const ValueDecl *VD) {
200 // Do not globalize declare target variables.
201 if (!isa<VarDecl>(VD) ||
202 OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD))
203 return;
205 // Use user-specified allocation.
206 if (VD->hasAttrs() && VD->hasAttr<OMPAllocateDeclAttr>())
207 return;
208 // Variables captured by value must be globalized.
209 bool IsCaptured = false;
210 if (auto *CSI = CGF.CapturedStmtInfo) {
211 if (const FieldDecl *FD = CSI->lookup(cast<VarDecl>(VD))) {
212 // Check if need to capture the variable that was already captured by
213 // value in the outer region.
214 IsCaptured = true;
215 if (!IsForCombinedParallelRegion) {
216 if (!FD->hasAttrs())
217 return;
218 const auto *Attr = FD->getAttr<OMPCaptureKindAttr>();
219 if (!Attr)
220 return;
221 if (((Attr->getCaptureKind() != OMPC_map) &&
222 !isOpenMPPrivate(Attr->getCaptureKind())) ||
223 ((Attr->getCaptureKind() == OMPC_map) &&
224 !FD->getType()->isAnyPointerType()))
225 return;
226 }
227 if (!FD->getType()->isReferenceType()) {
228 assert(!VD->getType()->isVariablyModifiedType() &&
229 "Parameter captured by value with variably modified type");
230 EscapedParameters.insert(VD);
231 } else if (!IsForCombinedParallelRegion) {
232 return;
233 }
234 }
235 }
236 if ((!CGF.CapturedStmtInfo ||
237 (IsForCombinedParallelRegion && CGF.CapturedStmtInfo)) &&
238 VD->getType()->isReferenceType())
239 // Do not globalize variables with reference type.
240 return;
241 if (VD->getType()->isVariablyModifiedType()) {
242 // If not captured at the target region level then mark the escaped
243 // variable as delayed.
244 if (IsCaptured)
245 EscapedVariableLengthDecls.insert(VD);
246 else
247 DelayedVariableLengthDecls.insert(VD);
248 } else
249 EscapedDecls.insert(VD);
250 }
251
252 void VisitValueDecl(const ValueDecl *VD) {
253 if (VD->getType()->isLValueReferenceType())
254 markAsEscaped(VD);
255 if (const auto *VarD = dyn_cast<VarDecl>(VD)) {
256 if (!isa<ParmVarDecl>(VarD) && VarD->hasInit()) {
257 const bool SavedAllEscaped = AllEscaped;
258 AllEscaped = VD->getType()->isLValueReferenceType();
259 Visit(VarD->getInit());
260 AllEscaped = SavedAllEscaped;
261 }
262 }
263 }
264 void VisitOpenMPCapturedStmt(const CapturedStmt *S,
265 ArrayRef<OMPClause *> Clauses,
266 bool IsCombinedParallelRegion) {
267 if (!S)
268 return;
269 for (const CapturedStmt::Capture &C : S->captures()) {
270 if (C.capturesVariable() && !C.capturesVariableByCopy()) {
271 const ValueDecl *VD = C.getCapturedVar();
272 bool SavedIsForCombinedParallelRegion = IsForCombinedParallelRegion;
273 if (IsCombinedParallelRegion) {
274 // Check if the variable is privatized in the combined construct and
275 // those private copies must be shared in the inner parallel
276 // directive.
277 IsForCombinedParallelRegion = false;
278 for (const OMPClause *C : Clauses) {
279 if (!isOpenMPPrivate(C->getClauseKind()) ||
280 C->getClauseKind() == OMPC_reduction ||
281 C->getClauseKind() == OMPC_linear ||
282 C->getClauseKind() == OMPC_private)
283 continue;
284 ArrayRef<const Expr *> Vars;
285 if (const auto *PC = dyn_cast<OMPFirstprivateClause>(C))
286 Vars = PC->getVarRefs();
287 else if (const auto *PC = dyn_cast<OMPLastprivateClause>(C))
288 Vars = PC->getVarRefs();
289 else
290 llvm_unreachable("Unexpected clause.");
291 for (const auto *E : Vars) {
292 const Decl *D =
293 cast<DeclRefExpr>(E)->getDecl()->getCanonicalDecl();
294 if (D == VD->getCanonicalDecl()) {
295 IsForCombinedParallelRegion = true;
296 break;
297 }
298 }
299 if (IsForCombinedParallelRegion)
300 break;
301 }
302 }
303 markAsEscaped(VD);
305 VisitValueDecl(VD);
306 IsForCombinedParallelRegion = SavedIsForCombinedParallelRegion;
307 }
308 }
309 }
310
311 void buildRecordForGlobalizedVars(bool IsInTTDRegion) {
312 assert(!GlobalizedRD &&
313 "Record for globalized variables is built already.");
314 ArrayRef<const ValueDecl *> EscapedDeclsForParallel, EscapedDeclsForTeams;
315 unsigned WarpSize = CGF.getTarget().getGridValue().GV_Warp_Size;
316 if (IsInTTDRegion)
317 EscapedDeclsForTeams = EscapedDecls.getArrayRef();
318 else
319 EscapedDeclsForParallel = EscapedDecls.getArrayRef();
320 GlobalizedRD = ::buildRecordForGlobalizedVars(
321 CGF.getContext(), EscapedDeclsForParallel, EscapedDeclsForTeams,
322 MappedDeclsFields, WarpSize);
323 }
324
325public:
326 CheckVarsEscapingDeclContext(CodeGenFunction &CGF,
327 ArrayRef<const ValueDecl *> TeamsReductions)
328 : CGF(CGF), EscapedDecls(llvm::from_range, TeamsReductions) {}
329 ~CheckVarsEscapingDeclContext() = default;
330 void VisitDeclStmt(const DeclStmt *S) {
331 if (!S)
332 return;
333 for (const Decl *D : S->decls())
334 if (const auto *VD = dyn_cast_or_null<ValueDecl>(D))
335 VisitValueDecl(VD);
336 }
337 void VisitOMPExecutableDirective(const OMPExecutableDirective *D) {
338 if (!D)
339 return;
340 if (!D->hasAssociatedStmt())
341 return;
342 if (const auto *S =
343 dyn_cast_or_null<CapturedStmt>(D->getAssociatedStmt())) {
344 // Do not analyze directives that do not actually require capturing,
345 // like `omp for` or `omp simd` directives.
346 llvm::SmallVector<OpenMPDirectiveKind, 4> CaptureRegions;
347 getOpenMPCaptureRegions(CaptureRegions, D->getDirectiveKind());
348 if (CaptureRegions.size() == 1 && CaptureRegions.back() == OMPD_unknown) {
349 VisitStmt(S->getCapturedStmt());
350 return;
351 }
352 VisitOpenMPCapturedStmt(
353 S, D->clauses(),
354 CaptureRegions.back() == OMPD_parallel &&
355 isOpenMPDistributeDirective(D->getDirectiveKind()));
356 }
357 }
358 // A metadirective has no associated statement; visit its variants and
359 // conditions.
360 void VisitOMPMetaDirective(const OMPMetaDirective *D) { VisitStmt(D); }
361 void VisitCapturedStmt(const CapturedStmt *S) {
362 if (!S)
363 return;
364 for (const CapturedStmt::Capture &C : S->captures()) {
365 if (C.capturesVariable() && !C.capturesVariableByCopy()) {
366 const ValueDecl *VD = C.getCapturedVar();
367 markAsEscaped(VD);
369 VisitValueDecl(VD);
370 }
371 }
372 }
373 void VisitLambdaExpr(const LambdaExpr *E) {
374 if (!E)
375 return;
376 for (const LambdaCapture &C : E->captures()) {
377 if (C.capturesVariable()) {
378 if (C.getCaptureKind() == LCK_ByRef) {
379 const ValueDecl *VD = C.getCapturedVar();
380 markAsEscaped(VD);
382 VisitValueDecl(VD);
383 }
384 }
385 }
386 }
387 void VisitBlockExpr(const BlockExpr *E) {
388 if (!E)
389 return;
390 for (const BlockDecl::Capture &C : E->getBlockDecl()->captures()) {
391 if (C.isByRef()) {
392 const VarDecl *VD = C.getVariable();
393 markAsEscaped(VD);
395 VisitValueDecl(VD);
396 }
397 }
398 }
399 void VisitCallExpr(const CallExpr *E) {
400 if (!E)
401 return;
402 for (const Expr *Arg : E->arguments()) {
403 if (!Arg)
404 continue;
405 if (Arg->isLValue()) {
406 const bool SavedAllEscaped = AllEscaped;
407 AllEscaped = true;
408 Visit(Arg);
409 AllEscaped = SavedAllEscaped;
410 } else {
411 Visit(Arg);
412 }
413 }
414 Visit(E->getCallee());
415 }
416 void VisitDeclRefExpr(const DeclRefExpr *E) {
417 if (!E)
418 return;
419 const ValueDecl *VD = E->getDecl();
420 if (AllEscaped)
421 markAsEscaped(VD);
423 VisitValueDecl(VD);
424 else if (VD->isInitCapture())
425 VisitValueDecl(VD);
426 }
427 void VisitUnaryOperator(const UnaryOperator *E) {
428 if (!E)
429 return;
430 if (E->getOpcode() == UO_AddrOf) {
431 const bool SavedAllEscaped = AllEscaped;
432 AllEscaped = true;
433 Visit(E->getSubExpr());
434 AllEscaped = SavedAllEscaped;
435 } else {
436 Visit(E->getSubExpr());
437 }
438 }
439 void VisitImplicitCastExpr(const ImplicitCastExpr *E) {
440 if (!E)
441 return;
442 if (E->getCastKind() == CK_ArrayToPointerDecay) {
443 const bool SavedAllEscaped = AllEscaped;
444 AllEscaped = true;
445 Visit(E->getSubExpr());
446 AllEscaped = SavedAllEscaped;
447 } else {
448 Visit(E->getSubExpr());
449 }
450 }
451 void VisitExpr(const Expr *E) {
452 if (!E)
453 return;
454 bool SavedAllEscaped = AllEscaped;
455 if (!E->isLValue())
456 AllEscaped = false;
457 for (const Stmt *Child : E->children())
458 if (Child)
459 Visit(Child);
460 AllEscaped = SavedAllEscaped;
461 }
462 void VisitStmt(const Stmt *S) {
463 if (!S)
464 return;
465 for (const Stmt *Child : S->children())
466 if (Child)
467 Visit(Child);
468 }
469
470 /// Returns the record that handles all the escaped local variables and used
471 /// instead of their original storage.
472 const RecordDecl *getGlobalizedRecord(bool IsInTTDRegion) {
473 if (!GlobalizedRD)
474 buildRecordForGlobalizedVars(IsInTTDRegion);
475 return GlobalizedRD;
476 }
477
478 /// Returns the field in the globalized record for the escaped variable.
479 const FieldDecl *getFieldForGlobalizedVar(const ValueDecl *VD) const {
480 assert(GlobalizedRD &&
481 "Record for globalized variables must be generated already.");
482 return MappedDeclsFields.lookup(VD);
483 }
484
485 /// Returns the list of the escaped local variables/parameters.
486 ArrayRef<const ValueDecl *> getEscapedDecls() const {
487 return EscapedDecls.getArrayRef();
488 }
489
490 /// Checks if the escaped local variable is actually a parameter passed by
491 /// value.
492 const llvm::SmallPtrSetImpl<const Decl *> &getEscapedParameters() const {
493 return EscapedParameters;
494 }
495
496 /// Returns the list of the escaped variables with the variably modified
497 /// types.
498 ArrayRef<const ValueDecl *> getEscapedVariableLengthDecls() const {
499 return EscapedVariableLengthDecls.getArrayRef();
500 }
501
502 /// Returns the list of the delayed variables with the variably modified
503 /// types.
504 ArrayRef<const ValueDecl *> getDelayedVariableLengthDecls() const {
505 return DelayedVariableLengthDecls.getArrayRef();
506 }
507};
508} // anonymous namespace
509
511CGOpenMPRuntimeGPU::getExecutionMode() const {
512 return CurrentExecutionMode;
513}
514
516CGOpenMPRuntimeGPU::getDataSharingMode() const {
517 return CurrentDataSharingMode;
518}
519
520/// Check for inner (nested) SPMD construct, if any
522 const OMPExecutableDirective &D) {
523 const auto *CS = D.getInnermostCapturedStmt();
524 const auto *Body =
525 CS->getCapturedStmt()->IgnoreContainers(/*IgnoreCaptured=*/true);
526 const Stmt *ChildStmt = CGOpenMPRuntime::getSingleCompoundChild(Ctx, Body);
527
528 if (const auto *NestedDir =
529 dyn_cast_or_null<OMPExecutableDirective>(ChildStmt)) {
530 OpenMPDirectiveKind DKind = NestedDir->getDirectiveKind();
531 switch (D.getDirectiveKind()) {
532 case OMPD_target:
533 if (isOpenMPParallelDirective(DKind))
534 return true;
535 if (DKind == OMPD_teams) {
536 Body = NestedDir->getInnermostCapturedStmt()->IgnoreContainers(
537 /*IgnoreCaptured=*/true);
538 if (!Body)
539 return false;
540 ChildStmt = CGOpenMPRuntime::getSingleCompoundChild(Ctx, Body);
541 if (const auto *NND =
542 dyn_cast_or_null<OMPExecutableDirective>(ChildStmt)) {
543 DKind = NND->getDirectiveKind();
544 if (isOpenMPParallelDirective(DKind))
545 return true;
546 }
547 }
548 return false;
549 case OMPD_target_teams:
550 return isOpenMPParallelDirective(DKind);
551 case OMPD_target_simd:
552 case OMPD_target_parallel:
553 case OMPD_target_parallel_for:
554 case OMPD_target_parallel_for_simd:
555 case OMPD_target_teams_distribute:
556 case OMPD_target_teams_distribute_simd:
557 case OMPD_target_teams_distribute_parallel_for:
558 case OMPD_target_teams_distribute_parallel_for_simd:
559 case OMPD_parallel:
560 case OMPD_for:
561 case OMPD_parallel_for:
562 case OMPD_parallel_master:
563 case OMPD_parallel_sections:
564 case OMPD_for_simd:
565 case OMPD_parallel_for_simd:
566 case OMPD_cancel:
567 case OMPD_cancellation_point:
568 case OMPD_ordered_standalone:
569 case OMPD_ordered_blockassoc:
570 case OMPD_threadprivate:
571 case OMPD_allocate:
572 case OMPD_task:
573 case OMPD_simd:
574 case OMPD_sections:
575 case OMPD_section:
576 case OMPD_single:
577 case OMPD_master:
578 case OMPD_critical:
579 case OMPD_taskyield:
580 case OMPD_barrier:
581 case OMPD_taskwait:
582 case OMPD_taskgroup:
583 case OMPD_atomic:
584 case OMPD_flush:
585 case OMPD_depobj:
586 case OMPD_scan:
587 case OMPD_teams:
588 case OMPD_target_data:
589 case OMPD_target_exit_data:
590 case OMPD_target_enter_data:
591 case OMPD_distribute:
592 case OMPD_distribute_simd:
593 case OMPD_distribute_parallel_for:
594 case OMPD_distribute_parallel_for_simd:
595 case OMPD_teams_distribute:
596 case OMPD_teams_distribute_simd:
597 case OMPD_teams_distribute_parallel_for:
598 case OMPD_teams_distribute_parallel_for_simd:
599 case OMPD_target_update:
600 case OMPD_declare_simd:
601 case OMPD_declare_variant:
602 case OMPD_begin_declare_variant:
603 case OMPD_end_declare_variant:
604 case OMPD_declare_target:
605 case OMPD_end_declare_target:
606 case OMPD_declare_reduction:
607 case OMPD_declare_mapper:
608 case OMPD_taskloop:
609 case OMPD_taskloop_simd:
610 case OMPD_master_taskloop:
611 case OMPD_master_taskloop_simd:
612 case OMPD_parallel_master_taskloop:
613 case OMPD_parallel_master_taskloop_simd:
614 case OMPD_requires:
615 case OMPD_unknown:
616 default:
617 llvm_unreachable("Unexpected directive.");
618 }
619 }
620
621 return false;
622}
623
625 const OMPExecutableDirective &D) {
626 OpenMPDirectiveKind DirectiveKind = D.getDirectiveKind();
627 switch (DirectiveKind) {
628 case OMPD_target:
629 case OMPD_target_teams:
630 return hasNestedSPMDDirective(Ctx, D);
631 case OMPD_target_parallel_loop:
632 case OMPD_target_parallel:
633 case OMPD_target_parallel_for:
634 case OMPD_target_parallel_for_simd:
635 case OMPD_target_teams_distribute_parallel_for:
636 case OMPD_target_teams_distribute_parallel_for_simd:
637 case OMPD_target_simd:
638 case OMPD_target_teams_distribute_simd:
639 return true;
640 case OMPD_target_teams_distribute:
641 return false;
642 case OMPD_target_teams_loop:
643 // Whether this is true or not depends on how the directive will
644 // eventually be emitted.
645 if (auto *TTLD = dyn_cast<OMPTargetTeamsGenericLoopDirective>(&D))
646 return TTLD->canBeParallelFor();
647 return false;
648 case OMPD_parallel:
649 case OMPD_for:
650 case OMPD_parallel_for:
651 case OMPD_parallel_master:
652 case OMPD_parallel_sections:
653 case OMPD_for_simd:
654 case OMPD_parallel_for_simd:
655 case OMPD_cancel:
656 case OMPD_cancellation_point:
657 case OMPD_ordered_standalone:
658 case OMPD_ordered_blockassoc:
659 case OMPD_threadprivate:
660 case OMPD_allocate:
661 case OMPD_task:
662 case OMPD_simd:
663 case OMPD_sections:
664 case OMPD_section:
665 case OMPD_single:
666 case OMPD_master:
667 case OMPD_critical:
668 case OMPD_taskyield:
669 case OMPD_barrier:
670 case OMPD_taskwait:
671 case OMPD_taskgroup:
672 case OMPD_atomic:
673 case OMPD_flush:
674 case OMPD_depobj:
675 case OMPD_scan:
676 case OMPD_teams:
677 case OMPD_target_data:
678 case OMPD_target_exit_data:
679 case OMPD_target_enter_data:
680 case OMPD_distribute:
681 case OMPD_distribute_simd:
682 case OMPD_distribute_parallel_for:
683 case OMPD_distribute_parallel_for_simd:
684 case OMPD_teams_distribute:
685 case OMPD_teams_distribute_simd:
686 case OMPD_teams_distribute_parallel_for:
687 case OMPD_teams_distribute_parallel_for_simd:
688 case OMPD_target_update:
689 case OMPD_declare_simd:
690 case OMPD_declare_variant:
691 case OMPD_begin_declare_variant:
692 case OMPD_end_declare_variant:
693 case OMPD_declare_target:
694 case OMPD_end_declare_target:
695 case OMPD_declare_reduction:
696 case OMPD_declare_mapper:
697 case OMPD_taskloop:
698 case OMPD_taskloop_simd:
699 case OMPD_master_taskloop:
700 case OMPD_master_taskloop_simd:
701 case OMPD_parallel_master_taskloop:
702 case OMPD_parallel_master_taskloop_simd:
703 case OMPD_requires:
704 case OMPD_unknown:
705 default:
706 break;
707 }
708 llvm_unreachable(
709 "Unknown programming model for OpenMP directive on NVPTX target.");
710}
711
712void CGOpenMPRuntimeGPU::emitNonSPMDKernel(const OMPExecutableDirective &D,
713 StringRef ParentName,
714 llvm::Function *&OutlinedFn,
715 llvm::Constant *&OutlinedFnID,
716 bool IsOffloadEntry,
717 const RegionCodeGenTy &CodeGen) {
718 ExecutionRuntimeModesRAII ModeRAII(CurrentExecutionMode, EM_NonSPMD);
719 EntryFunctionState EST;
720 WrapperFunctionsMap.clear();
721
722 [[maybe_unused]] bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
723 assert(!IsBareKernel && "bare kernel should not be at generic mode");
724
725 // Emit target region as a standalone region.
726 class NVPTXPrePostActionTy : public PrePostActionTy {
727 CGOpenMPRuntimeGPU::EntryFunctionState &EST;
728 const OMPExecutableDirective &D;
729
730 public:
731 NVPTXPrePostActionTy(CGOpenMPRuntimeGPU::EntryFunctionState &EST,
732 const OMPExecutableDirective &D)
733 : EST(EST), D(D) {}
734 void Enter(CodeGenFunction &CGF) override {
735 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
736 RT.emitKernelInit(D, CGF, EST, /* IsSPMD */ false);
737 // Skip target region initialization.
738 RT.setLocThreadIdInsertPt(CGF, /*AtCurrentPoint=*/true);
739 }
740 void Exit(CodeGenFunction &CGF) override {
741 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
742 RT.clearLocThreadIdInsertPt(CGF);
743 RT.emitKernelDeinit(CGF, EST, /* IsSPMD */ false);
744 }
745 } Action(EST, D);
746 CodeGen.setAction(Action);
747 IsInTTDRegion = true;
748 emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,
749 IsOffloadEntry, CodeGen);
750 IsInTTDRegion = false;
751}
752
753void CGOpenMPRuntimeGPU::emitBareKernelEnvironment(
754 const OMPExecutableDirective &D, CodeGenFunction &CGF) {
755 // Bare kernels manage their own initialization and never call
756 // __kmpc_target_init, but the runtime still needs a
757 // '<kernel>_kernel_environment' global to know how the kernel was
758 // configured, so emit it directly here.
759 llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs Attrs;
760 Attrs.ExecFlags = llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_BARE;
761 CGBuilderTy &Bld = CGF.Builder;
762 OMPBuilder.emitKernelEnvironment(Bld, Attrs);
763}
764
765void CGOpenMPRuntimeGPU::emitKernelInit(const OMPExecutableDirective &D,
766 CodeGenFunction &CGF,
767 EntryFunctionState &EST, bool IsSPMD) {
768 llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs Attrs;
769 Attrs.ExecFlags =
770 IsSPMD ? llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_SPMD
771 : llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_GENERIC;
772 computeMinAndMaxThreadsAndTeams(D, CGF, Attrs);
773
774 CGBuilderTy &Bld = CGF.Builder;
775 Bld.restoreIP(OMPBuilder.createTargetInit(Bld, Attrs));
776 if (!IsSPMD)
777 emitGenericVarsProlog(CGF, EST.Loc);
778}
779
780void CGOpenMPRuntimeGPU::emitKernelDeinit(CodeGenFunction &CGF,
781 EntryFunctionState &EST,
782 bool IsSPMD) {
783 if (!IsSPMD)
784 emitGenericVarsEpilog(CGF);
785
786 // This is temporary until we remove the fixed sized buffer.
787 ASTContext &C = CGM.getContext();
788 RecordDecl *StaticRD = C.buildImplicitRecord(
789 "_openmp_teams_reduction_type_$_", RecordDecl::TagKind::Union);
790 StaticRD->startDefinition();
791 for (const RecordDecl *TeamReductionRec : TeamsReductions) {
792 CanQualType RecTy = C.getCanonicalTagType(TeamReductionRec);
793 auto *Field = FieldDecl::Create(
794 C, StaticRD, SourceLocation(), SourceLocation(), nullptr, RecTy,
795 C.getTrivialTypeSourceInfo(RecTy, SourceLocation()),
796 /*BW=*/nullptr, /*Mutable=*/false,
797 /*InitStyle=*/ICIS_NoInit);
798 Field->setAccess(AS_public);
799 StaticRD->addDecl(Field);
800 }
801 StaticRD->completeDefinition();
802 CanQualType StaticTy = C.getCanonicalTagType(StaticRD);
803 llvm::Type *LLVMReductionsBufferTy =
804 CGM.getTypes().ConvertTypeForMem(StaticTy);
805 const auto &DL = CGM.getModule().getDataLayout();
806 uint64_t ReductionDataSize =
807 TeamsReductions.empty()
808 ? 0
809 : DL.getTypeAllocSize(LLVMReductionsBufferTy).getFixedValue();
810 CGBuilderTy &Bld = CGF.Builder;
811 OMPBuilder.createTargetDeinit(Bld, ReductionDataSize);
812 TeamsReductions.clear();
813}
814
815void CGOpenMPRuntimeGPU::emitSPMDKernel(const OMPExecutableDirective &D,
816 StringRef ParentName,
817 llvm::Function *&OutlinedFn,
818 llvm::Constant *&OutlinedFnID,
819 bool IsOffloadEntry,
820 const RegionCodeGenTy &CodeGen) {
821 ExecutionRuntimeModesRAII ModeRAII(CurrentExecutionMode, EM_SPMD);
822 EntryFunctionState EST;
823
824 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
825
826 // Emit target region as a standalone region.
827 class NVPTXPrePostActionTy : public PrePostActionTy {
828 CGOpenMPRuntimeGPU &RT;
829 CGOpenMPRuntimeGPU::EntryFunctionState &EST;
830 bool IsBareKernel;
831 DataSharingMode Mode;
832 const OMPExecutableDirective &D;
833
834 public:
835 NVPTXPrePostActionTy(CGOpenMPRuntimeGPU &RT,
836 CGOpenMPRuntimeGPU::EntryFunctionState &EST,
837 bool IsBareKernel, const OMPExecutableDirective &D)
838 : RT(RT), EST(EST), IsBareKernel(IsBareKernel),
839 Mode(RT.CurrentDataSharingMode), D(D) {}
840 void Enter(CodeGenFunction &CGF) override {
841 if (IsBareKernel) {
842 RT.CurrentDataSharingMode = DataSharingMode::DS_CUDA;
843 RT.emitBareKernelEnvironment(D, CGF);
844 return;
845 }
846 RT.emitKernelInit(D, CGF, EST, /* IsSPMD */ true);
847 // Skip target region initialization.
848 RT.setLocThreadIdInsertPt(CGF, /*AtCurrentPoint=*/true);
849 }
850 void Exit(CodeGenFunction &CGF) override {
851 if (IsBareKernel) {
852 RT.CurrentDataSharingMode = Mode;
853 return;
854 }
855 RT.clearLocThreadIdInsertPt(CGF);
856 RT.emitKernelDeinit(CGF, EST, /* IsSPMD */ true);
857 }
858 } Action(*this, EST, IsBareKernel, D);
859 CodeGen.setAction(Action);
860 IsInTTDRegion = true;
861 emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,
862 IsOffloadEntry, CodeGen);
863 IsInTTDRegion = false;
864}
865
866void CGOpenMPRuntimeGPU::emitTargetOutlinedFunction(
867 const OMPExecutableDirective &D, StringRef ParentName,
868 llvm::Function *&OutlinedFn, llvm::Constant *&OutlinedFnID,
869 bool IsOffloadEntry, const RegionCodeGenTy &CodeGen) {
870 if (!IsOffloadEntry) // Nothing to do.
871 return;
872
873 assert(!ParentName.empty() && "Invalid target region parent name!");
874
875 bool Mode = supportsSPMDExecutionMode(CGM.getContext(), D);
876 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
877 if (Mode || IsBareKernel)
878 emitSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,
879 CodeGen);
880 else
881 emitNonSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,
882 CodeGen);
883}
884
887 llvm::OpenMPIRBuilderConfig Config(
888 CGM.getLangOpts().OpenMPIsTargetDevice, isGPU(),
889 CGM.getLangOpts().OpenMPOffloadMandatory,
890 /*HasRequiresReverseOffload*/ false, /*HasRequiresUnifiedAddress*/ false,
891 hasRequiresUnifiedSharedMemory(), /*HasRequiresDynamicAllocators*/ false);
892 Config.setDefaultTargetAS(
893 CGM.getContext().getTargetInfo().getTargetAddressSpace(LangAS::Default));
894 Config.setRuntimeCC(CGM.getRuntimeCC());
895
896 OMPBuilder.setConfig(Config);
897
898 if (!CGM.getLangOpts().OpenMPIsTargetDevice)
899 llvm_unreachable("OpenMP can only handle device code.");
900
901 if (CGM.getLangOpts().OpenMPCUDAMode)
902 CurrentDataSharingMode = CGOpenMPRuntimeGPU::DS_CUDA;
903
904 llvm::OpenMPIRBuilder &OMPBuilder = getOMPBuilder();
905 if (CGM.getLangOpts().NoGPULib || CGM.getLangOpts().OMPHostIRFile.empty())
906 return;
907
908 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTargetDebug,
909 "__omp_rtl_debug_kind");
910 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTeamSubscription,
911 "__omp_rtl_assume_teams_oversubscription");
912 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPThreadSubscription,
913 "__omp_rtl_assume_threads_oversubscription");
914 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoThreadState,
915 "__omp_rtl_assume_no_thread_state");
916 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoNestedParallelism,
917 "__omp_rtl_assume_no_nested_parallelism");
918}
919
921 ProcBindKind ProcBind,
922 SourceLocation Loc) {
923 // Nothing to do.
924}
925
927 const Expr *Message,
928 SourceLocation Loc) {
929 CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)
930 << getOpenMPClauseName(OMPC_message);
931 return nullptr;
932}
933
934llvm::Value *
936 SourceLocation Loc) {
937 CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)
938 << getOpenMPClauseName(OMPC_severity);
939 return nullptr;
940}
941
943 CodeGenFunction &CGF, llvm::Value *NumThreads, SourceLocation Loc,
945 SourceLocation SeverityLoc, const Expr *Message,
946 SourceLocation MessageLoc) {
947 if (Modifier == OMPC_NUMTHREADS_strict) {
948 CGM.getDiags().Report(Loc,
949 diag::warn_omp_gpu_unsupported_modifier_for_clause)
950 << "strict" << getOpenMPClauseName(OMPC_num_threads);
951 return;
952 }
953
954 // Nothing to do.
955}
956
958 const Expr *NumTeams,
959 const Expr *ThreadLimit,
960 SourceLocation Loc) {}
961
964 const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,
965 const RegionCodeGenTy &CodeGen) {
966 // Emit target region as a standalone region.
967 bool PrevIsInTTDRegion = IsInTTDRegion;
968 IsInTTDRegion = false;
969 auto *OutlinedFun =
971 CGF, D, ThreadIDVar, InnermostKind, CodeGen));
972 IsInTTDRegion = PrevIsInTTDRegion;
973 if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD) {
974 llvm::Function *WrapperFun =
975 createParallelDataSharingWrapper(OutlinedFun, D);
976 WrapperFunctionsMap[OutlinedFun] = WrapperFun;
977 }
978
979 return OutlinedFun;
980}
981
982/// Get list of lastprivate variables from the teams distribute ... or
983/// teams {distribute ...} directives.
984static void
987 assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&
988 "expected teams directive.");
989 const OMPExecutableDirective *Dir = &D;
990 if (!isOpenMPDistributeDirective(D.getDirectiveKind())) {
992 Ctx,
993 D.getInnermostCapturedStmt()->getCapturedStmt()->IgnoreContainers(
994 /*IgnoreCaptured=*/true))) {
995 Dir = dyn_cast_or_null<OMPExecutableDirective>(S);
996 if (Dir && !isOpenMPDistributeDirective(Dir->getDirectiveKind()))
997 Dir = nullptr;
998 }
999 }
1000 if (!Dir)
1001 return;
1002 for (const auto *C : Dir->getClausesOfKind<OMPLastprivateClause>()) {
1003 for (const Expr *E : C->getVarRefs())
1004 Vars.push_back(getPrivateItem(E));
1005 }
1006}
1007
1008/// Get list of reduction variables from the teams ... directives.
1009static void
1012 assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&
1013 "expected teams directive.");
1014 for (const auto *C : D.getClausesOfKind<OMPReductionClause>()) {
1015 for (const Expr *E : C->privates())
1016 Vars.push_back(getPrivateItem(E));
1017 }
1018}
1019
1022 const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,
1023 const RegionCodeGenTy &CodeGen) {
1024 SourceLocation Loc = D.getBeginLoc();
1025
1026 const RecordDecl *GlobalizedRD = nullptr;
1027 llvm::SmallVector<const ValueDecl *, 4> LastPrivatesReductions;
1028 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> MappedDeclsFields;
1029 unsigned WarpSize = CGM.getTarget().getGridValue().GV_Warp_Size;
1030 // Globalize team reductions variable unconditionally in all modes.
1031 if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD)
1032 getTeamsReductionVars(CGM.getContext(), D, LastPrivatesReductions);
1033 if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {
1034 getDistributeLastprivateVars(CGM.getContext(), D, LastPrivatesReductions);
1035 if (!LastPrivatesReductions.empty()) {
1036 GlobalizedRD = ::buildRecordForGlobalizedVars(
1037 CGM.getContext(), {}, LastPrivatesReductions, MappedDeclsFields,
1038 WarpSize);
1039 }
1040 } else if (!LastPrivatesReductions.empty()) {
1041 assert(!TeamAndReductions.first &&
1042 "Previous team declaration is not expected.");
1043 TeamAndReductions.first = D.getCapturedStmt(OMPD_teams)->getCapturedDecl();
1044 std::swap(TeamAndReductions.second, LastPrivatesReductions);
1045 }
1046
1047 // Emit target region as a standalone region.
1048 class NVPTXPrePostActionTy : public PrePostActionTy {
1049 SourceLocation &Loc;
1050 const RecordDecl *GlobalizedRD;
1051 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>
1052 &MappedDeclsFields;
1053
1054 public:
1055 NVPTXPrePostActionTy(
1056 SourceLocation &Loc, const RecordDecl *GlobalizedRD,
1057 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>
1058 &MappedDeclsFields)
1059 : Loc(Loc), GlobalizedRD(GlobalizedRD),
1060 MappedDeclsFields(MappedDeclsFields) {}
1061 void Enter(CodeGenFunction &CGF) override {
1062 auto &Rt =
1063 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
1064 if (GlobalizedRD) {
1065 auto I = Rt.FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;
1066 I->getSecond().MappedParams =
1067 std::make_unique<CodeGenFunction::OMPMapVars>();
1068 DeclToAddrMapTy &Data = I->getSecond().LocalVarData;
1069 for (const auto &Pair : MappedDeclsFields) {
1070 assert(Pair.getFirst()->isCanonicalDecl() &&
1071 "Expected canonical declaration");
1072 Data.try_emplace(Pair.getFirst());
1073 }
1074 }
1075 Rt.emitGenericVarsProlog(CGF, Loc);
1076 }
1077 void Exit(CodeGenFunction &CGF) override {
1078 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())
1079 .emitGenericVarsEpilog(CGF);
1080 }
1081 } Action(Loc, GlobalizedRD, MappedDeclsFields);
1082 CodeGen.setAction(Action);
1083 llvm::Function *OutlinedFun = CGOpenMPRuntime::emitTeamsOutlinedFunction(
1084 CGF, D, ThreadIDVar, InnermostKind, CodeGen);
1085
1086 return OutlinedFun;
1087}
1088
1089void CGOpenMPRuntimeGPU::emitGenericVarsProlog(CodeGenFunction &CGF,
1090 SourceLocation Loc) {
1091 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
1092 return;
1093
1094 CGBuilderTy &Bld = CGF.Builder;
1095
1096 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1097 if (I == FunctionGlobalizedDecls.end())
1098 return;
1099
1100 for (auto &Rec : I->getSecond().LocalVarData) {
1101 const auto *VD = cast<VarDecl>(Rec.first);
1102 bool EscapedParam = I->getSecond().EscapedParameters.count(Rec.first);
1103 QualType VarTy = VD->getType();
1104
1105 // Get the local allocation of a firstprivate variable before sharing
1106 llvm::Value *ParValue;
1107 if (EscapedParam) {
1108 LValue ParLVal =
1109 CGF.MakeAddrLValue(CGF.GetAddrOfLocalVar(VD), VD->getType());
1110 ParValue = CGF.EmitLoadOfScalar(ParLVal, Loc);
1111 }
1112
1113 // Allocate space for the variable to be globalized
1114 llvm::Value *AllocArgs[] = {CGF.getTypeSize(VD->getType())};
1115 llvm::CallBase *VoidPtr =
1116 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1117 CGM.getModule(), OMPRTL___kmpc_alloc_shared),
1118 AllocArgs, VD->getName());
1119 // FIXME: We should use the variables actual alignment as an argument.
1120 VoidPtr->addRetAttr(llvm::Attribute::get(
1121 CGM.getLLVMContext(), llvm::Attribute::Alignment,
1123
1124 // Cast the void pointer and get the address of the globalized variable.
1125 llvm::Value *CastedVoidPtr = Bld.CreatePointerBitCastOrAddrSpaceCast(
1126 VoidPtr, Bld.getPtrTy(0), VD->getName() + "_on_stack");
1127 LValue VarAddr =
1128 CGF.MakeNaturalAlignPointeeRawAddrLValue(CastedVoidPtr, VarTy);
1129 Rec.second.PrivateAddr = VarAddr.getAddress();
1130 Rec.second.GlobalizedVal = VoidPtr;
1131
1132 // Assign the local allocation to the newly globalized location.
1133 if (EscapedParam) {
1134 CGF.EmitStoreOfScalar(ParValue, VarAddr);
1135 I->getSecond().MappedParams->setVarAddr(CGF, VD, VarAddr.getAddress());
1136 }
1137 if (auto *DI = CGF.getDebugInfo())
1138 VoidPtr->setDebugLoc(DI->SourceLocToDebugLoc(VD->getLocation()));
1139 }
1140
1141 for (const auto *ValueD : I->getSecond().EscapedVariableLengthDecls) {
1142 const auto *VD = cast<VarDecl>(ValueD);
1143 std::pair<llvm::Value *, llvm::Value *> AddrSizePair =
1144 getKmpcAllocShared(CGF, VD);
1145 I->getSecond().EscapedVariableLengthDeclsAddrs.emplace_back(AddrSizePair);
1146 LValue Base = CGF.MakeAddrLValue(AddrSizePair.first, VD->getType(),
1147 CGM.getContext().getDeclAlign(VD),
1149 I->getSecond().MappedParams->setVarAddr(CGF, VD, Base.getAddress());
1150 }
1151 I->getSecond().MappedParams->apply(CGF);
1152}
1153
1155 const VarDecl *VD) const {
1156 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1157 if (I == FunctionGlobalizedDecls.end())
1158 return false;
1159
1160 // Check variable declaration is delayed:
1161 return llvm::is_contained(I->getSecond().DelayedVariableLengthDecls, VD);
1162}
1163
1164std::pair<llvm::Value *, llvm::Value *>
1166 const VarDecl *VD) {
1167 CGBuilderTy &Bld = CGF.Builder;
1168
1169 // Compute size and alignment.
1170 llvm::Value *Size = CGF.getTypeSize(VD->getType());
1171 CharUnits Align = CGM.getContext().getDeclAlign(VD);
1172 Size = Bld.CreateNUWAdd(
1173 Size, llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity() - 1));
1174 llvm::Value *AlignVal =
1175 llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity());
1176 Size = Bld.CreateUDiv(Size, AlignVal);
1177 Size = Bld.CreateNUWMul(Size, AlignVal);
1178
1179 // Allocate space for this VLA object to be globalized.
1180 llvm::Value *AllocArgs[] = {Size};
1181 llvm::CallBase *VoidPtr =
1182 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1183 CGM.getModule(), OMPRTL___kmpc_alloc_shared),
1184 AllocArgs, VD->getName());
1185 VoidPtr->addRetAttr(llvm::Attribute::get(
1186 CGM.getLLVMContext(), llvm::Attribute::Alignment, Align.getQuantity()));
1187
1188 return std::make_pair(VoidPtr, Size);
1189}
1190
1192 CodeGenFunction &CGF,
1193 const std::pair<llvm::Value *, llvm::Value *> &AddrSizePair) {
1194 // Deallocate the memory for each globalized VLA object
1195 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1196 CGM.getModule(), OMPRTL___kmpc_free_shared),
1197 {AddrSizePair.first, AddrSizePair.second});
1198}
1199
1200void CGOpenMPRuntimeGPU::emitGenericVarsEpilog(CodeGenFunction &CGF) {
1201 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
1202 return;
1203
1204 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1205 if (I != FunctionGlobalizedDecls.end()) {
1206 // Deallocate the memory for each globalized VLA object that was
1207 // globalized in the prolog (i.e. emitGenericVarsProlog).
1208 for (const auto &AddrSizePair :
1209 llvm::reverse(I->getSecond().EscapedVariableLengthDeclsAddrs)) {
1210 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1211 CGM.getModule(), OMPRTL___kmpc_free_shared),
1212 {AddrSizePair.first, AddrSizePair.second});
1213 }
1214 // Deallocate the memory for each globalized value
1215 for (auto &Rec : llvm::reverse(I->getSecond().LocalVarData)) {
1216 const auto *VD = cast<VarDecl>(Rec.first);
1217 I->getSecond().MappedParams->restore(CGF);
1218
1219 llvm::Value *FreeArgs[] = {Rec.second.GlobalizedVal,
1220 CGF.getTypeSize(VD->getType())};
1221 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1222 CGM.getModule(), OMPRTL___kmpc_free_shared),
1223 FreeArgs);
1224 }
1225 }
1226}
1227
1229 const OMPExecutableDirective &D,
1230 SourceLocation Loc,
1231 llvm::Function *OutlinedFn,
1232 ArrayRef<llvm::Value *> CapturedVars) {
1233 if (!CGF.HaveInsertPoint())
1234 return;
1235
1236 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
1237
1239 /*Name=*/".zero.addr");
1240 CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);
1242 // We don't emit any thread id function call in bare kernel, but because the
1243 // outlined function has a pointer argument, we emit a nullptr here.
1244 if (IsBareKernel)
1245 OutlinedFnArgs.push_back(llvm::ConstantPointerNull::get(CGM.VoidPtrTy));
1246 else
1247 OutlinedFnArgs.push_back(emitThreadIDAddress(CGF, Loc).emitRawPointer(CGF));
1248 OutlinedFnArgs.push_back(ZeroAddr.getPointer());
1249 OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
1250 emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, OutlinedFnArgs);
1251}
1252
1254 CodeGenFunction &CGF, SourceLocation Loc, llvm::Function *OutlinedFn,
1255 ArrayRef<llvm::Value *> CapturedVars, const Expr *IfCond,
1256 llvm::Value *NumThreads, OpenMPNumThreadsClauseModifier NumThreadsModifier,
1257 OpenMPSeverityClauseKind Severity, const Expr *Message) {
1258 if (!CGF.HaveInsertPoint())
1259 return;
1260
1261 auto &&ParallelGen = [this, Loc, OutlinedFn, CapturedVars, IfCond,
1262 NumThreads](CodeGenFunction &CGF,
1263 PrePostActionTy &Action) {
1264 CGBuilderTy &Bld = CGF.Builder;
1265 llvm::Value *NumThreadsVal = NumThreads;
1266 llvm::Function *WFn = WrapperFunctionsMap[OutlinedFn];
1267 llvm::PointerType *FnPtrTy = llvm::PointerType::get(
1268 CGF.getLLVMContext(), CGM.getDataLayout().getProgramAddressSpace());
1269
1270 llvm::Value *ID = llvm::ConstantPointerNull::get(FnPtrTy);
1271 if (WFn)
1272 ID = Bld.CreateBitOrPointerCast(WFn, FnPtrTy);
1273
1274 llvm::Value *FnPtr = Bld.CreateBitOrPointerCast(OutlinedFn, FnPtrTy);
1275
1276 // Create a private scope that will globalize the arguments
1277 // passed from the outside of the target region.
1278 // TODO: Is that needed?
1279 CodeGenFunction::OMPPrivateScope PrivateArgScope(CGF);
1280
1281 Address CapturedVarsAddrs = CGF.CreateDefaultAlignTempAlloca(
1282 llvm::ArrayType::get(CGM.VoidPtrTy, CapturedVars.size()),
1283 "captured_vars_addrs");
1284 // There's something to share.
1285 if (!CapturedVars.empty()) {
1286 // Prepare for parallel region. Indicate the outlined function.
1287 ASTContext &Ctx = CGF.getContext();
1288 unsigned Idx = 0;
1289 for (llvm::Value *V : CapturedVars) {
1290 Address Dst = Bld.CreateConstArrayGEP(CapturedVarsAddrs, Idx);
1291 llvm::Value *PtrV;
1292 if (V->getType()->isIntegerTy())
1293 PtrV = Bld.CreateIntToPtr(V, CGF.VoidPtrTy);
1294 else
1296 CGF.EmitStoreOfScalar(PtrV, Dst, /*Volatile=*/false,
1297 Ctx.getPointerType(Ctx.VoidPtrTy));
1298 ++Idx;
1299 }
1300 }
1301
1302 llvm::Value *IfCondVal = nullptr;
1303 if (IfCond)
1304 IfCondVal = Bld.CreateIntCast(CGF.EvaluateExprAsBool(IfCond), CGF.Int32Ty,
1305 /* isSigned */ false);
1306 else
1307 IfCondVal = llvm::ConstantInt::get(CGF.Int32Ty, 1);
1308
1309 if (!NumThreadsVal)
1310 NumThreadsVal = llvm::ConstantInt::getAllOnesValue(CGF.Int32Ty);
1311 else
1312 NumThreadsVal = Bld.CreateZExtOrTrunc(NumThreadsVal, CGF.Int32Ty);
1313
1314 // No strict prescriptiveness for the number of threads.
1315 llvm::Value *StrictNumThreadsVal = llvm::ConstantInt::get(CGF.Int32Ty, 0);
1316
1317 assert(IfCondVal && "Expected a value");
1318 llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);
1319 llvm::Value *Args[] = {
1320 RTLoc,
1321 getThreadID(CGF, Loc),
1322 IfCondVal,
1323 NumThreadsVal,
1324 llvm::ConstantInt::getAllOnesValue(CGF.Int32Ty),
1325 FnPtr,
1326 ID,
1327 Bld.CreateBitOrPointerCast(CapturedVarsAddrs.emitRawPointer(CGF),
1328 CGF.VoidPtrPtrTy),
1329 llvm::ConstantInt::get(CGM.SizeTy, CapturedVars.size()),
1330 StrictNumThreadsVal};
1331
1332 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1333 CGM.getModule(), OMPRTL___kmpc_parallel_60),
1334 Args);
1335 };
1336
1337 RegionCodeGenTy RCG(ParallelGen);
1338 RCG(CGF);
1339}
1340
1341void CGOpenMPRuntimeGPU::syncCTAThreads(CodeGenFunction &CGF) {
1342 // Always emit simple barriers!
1343 if (!CGF.HaveInsertPoint())
1344 return;
1345 // Build call __kmpc_barrier_simple_spmd(nullptr, 0);
1346 // This function does not use parameters, so we can emit just default values.
1347 llvm::Value *Args[] = {
1348 llvm::ConstantPointerNull::get(
1350 llvm::ConstantInt::get(CGF.Int32Ty, /*V=*/0, /*isSigned=*/true)};
1351 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1352 CGM.getModule(), OMPRTL___kmpc_barrier_simple_spmd),
1353 Args);
1354}
1355
1357 SourceLocation Loc,
1358 OpenMPDirectiveKind Kind, bool,
1359 bool) {
1360 // Always emit simple barriers!
1361 if (!CGF.HaveInsertPoint())
1362 return;
1363 // Build call __kmpc_cancel_barrier(loc, thread_id);
1364 unsigned Flags = getDefaultFlagsForBarriers(Kind);
1365 llvm::Value *Args[] = {emitUpdateLocation(CGF, Loc, Flags),
1366 getThreadID(CGF, Loc)};
1367
1368 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1369 CGM.getModule(), OMPRTL___kmpc_barrier),
1370 Args);
1371}
1372
1374 CodeGenFunction &CGF, StringRef CriticalName,
1375 const RegionCodeGenTy &CriticalOpGen, SourceLocation Loc,
1376 const Expr *Hint) {
1377 llvm::BasicBlock *LoopBB = CGF.createBasicBlock("omp.critical.loop");
1378 llvm::BasicBlock *TestBB = CGF.createBasicBlock("omp.critical.test");
1379 llvm::BasicBlock *SyncBB = CGF.createBasicBlock("omp.critical.sync");
1380 llvm::BasicBlock *BodyBB = CGF.createBasicBlock("omp.critical.body");
1381 llvm::BasicBlock *ExitBB = CGF.createBasicBlock("omp.critical.exit");
1382
1383 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
1384
1385 // Get the mask of active threads in the warp.
1386 llvm::Value *Mask = CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1387 CGM.getModule(), OMPRTL___kmpc_warp_active_thread_mask));
1388 // Fetch team-local id of the thread.
1389 llvm::Value *ThreadID = RT.getGPUThreadID(CGF);
1390
1391 // Get the width of the team.
1392 llvm::Value *TeamWidth = RT.getGPUNumThreads(CGF);
1393
1394 // Initialize the counter variable for the loop.
1395 QualType Int32Ty =
1396 CGF.getContext().getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/0);
1397 Address Counter = CGF.CreateMemTempWithoutCast(Int32Ty, "critical_counter");
1398 LValue CounterLVal = CGF.MakeAddrLValue(Counter, Int32Ty);
1399 CGF.EmitStoreOfScalar(llvm::Constant::getNullValue(CGM.Int32Ty), CounterLVal,
1400 /*isInit=*/true);
1401
1402 // Block checks if loop counter exceeds upper bound.
1403 CGF.EmitBlock(LoopBB);
1404 llvm::Value *CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);
1405 llvm::Value *CmpLoopBound = CGF.Builder.CreateICmpSLT(CounterVal, TeamWidth);
1406 CGF.Builder.CreateCondBr(CmpLoopBound, TestBB, ExitBB);
1407
1408 // Block tests which single thread should execute region, and which threads
1409 // should go straight to synchronisation point.
1410 CGF.EmitBlock(TestBB);
1411 CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);
1412 llvm::Value *CmpThreadToCounter =
1413 CGF.Builder.CreateICmpEQ(ThreadID, CounterVal);
1414 CGF.Builder.CreateCondBr(CmpThreadToCounter, BodyBB, SyncBB);
1415
1416 // Block emits the body of the critical region.
1417 CGF.EmitBlock(BodyBB);
1418
1419 // Output the critical statement.
1420 CGOpenMPRuntime::emitCriticalRegion(CGF, CriticalName, CriticalOpGen, Loc,
1421 Hint);
1422
1423 // After the body surrounded by the critical region, the single executing
1424 // thread will jump to the synchronisation point.
1425 // Block waits for all threads in current team to finish then increments the
1426 // counter variable and returns to the loop.
1427 CGF.EmitBlock(SyncBB);
1428 // Reconverge active threads in the warp.
1429 (void)CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1430 CGM.getModule(), OMPRTL___kmpc_syncwarp),
1431 Mask);
1432
1433 llvm::Value *IncCounterVal =
1434 CGF.Builder.CreateNSWAdd(CounterVal, CGF.Builder.getInt32(1));
1435 CGF.EmitStoreOfScalar(IncCounterVal, CounterLVal);
1436 CGF.EmitBranch(LoopBB);
1437
1438 // Block that is reached when all threads in the team complete the region.
1439 CGF.EmitBlock(ExitBB, /*IsFinished=*/true);
1440}
1441
1442/// Cast value to the specified type.
1443static llvm::Value *castValueToType(CodeGenFunction &CGF, llvm::Value *Val,
1444 QualType ValTy, QualType CastTy,
1445 SourceLocation Loc) {
1446 assert(!CGF.getContext().getTypeSizeInChars(CastTy).isZero() &&
1447 "Cast type must sized.");
1448 assert(!CGF.getContext().getTypeSizeInChars(ValTy).isZero() &&
1449 "Val type must sized.");
1450 llvm::Type *LLVMCastTy = CGF.ConvertTypeForMem(CastTy);
1451 if (ValTy == CastTy)
1452 return Val;
1453 if (CGF.getContext().getTypeSizeInChars(ValTy) ==
1454 CGF.getContext().getTypeSizeInChars(CastTy))
1455 return CGF.Builder.CreateBitCast(Val, LLVMCastTy);
1456 if (CastTy->isIntegerType() && ValTy->isIntegerType())
1457 return CGF.Builder.CreateIntCast(Val, LLVMCastTy,
1459 Address CastItem = CGF.CreateMemTempWithoutCast(CastTy);
1460 Address ValCastItem = CastItem.withElementType(Val->getType());
1461 CGF.EmitStoreOfScalar(Val, ValCastItem, /*Volatile=*/false, ValTy,
1463 TBAAAccessInfo());
1464 return CGF.EmitLoadOfScalar(CastItem, /*Volatile=*/false, CastTy, Loc,
1466 TBAAAccessInfo());
1467}
1468
1469/// Extracts the built-in reduction operator from a combiner of the form `x = x
1470/// <op> rhs` (or the min/max conditional), or nullopt if the shape is not
1471/// recognized (e.g. user-defined reductions).
1472static std::optional<BinaryOperatorKind>
1473getReductionBinOpKind(const Expr *ReductionOp) {
1474 const auto *Assign = dyn_cast<BinaryOperator>(ReductionOp);
1475 if (!Assign || Assign->getOpcode() != BO_Assign)
1476 return std::nullopt;
1477 const Expr *RHS = Assign->getRHS();
1478 // min/max are lowered as `x <cmp> rhs ? x : rhs`; the comparison identifies
1479 // it.
1480 if (const auto *ACO =
1481 dyn_cast<AbstractConditionalOperator>(RHS->IgnoreParenImpCasts()))
1482 RHS = ACO->getCond();
1483 if (const auto *BO = dyn_cast<BinaryOperator>(RHS->IgnoreParenImpCasts()))
1484 return BO->getOpcode();
1485 return std::nullopt;
1486}
1487
1488/// Maps a built-in reduction operator to an atomicrmw opcode for the atomic
1489/// cross-team reduction fast path, or nullopt if there is no direct atomicrmw
1490/// (e.g. user-defined, complex, fp min/max) so the buffer path is used instead.
1491static std::optional<llvm::AtomicRMWInst::BinOp>
1493 bool IsInt = Ty->isIntegerType();
1494 bool IsSigned = Ty->hasSignedIntegerRepresentation();
1495 switch (BOK) {
1496 case BO_Add:
1497 case BO_Sub: // A `-` reduction sums the partials, so it accumulates with add.
1498 if (IsInt)
1499 return llvm::AtomicRMWInst::Add;
1500 if (Ty->isFloatingType())
1501 return llvm::AtomicRMWInst::FAdd;
1502 return std::nullopt;
1503 case BO_And:
1504 return IsInt ? std::optional(llvm::AtomicRMWInst::And) : std::nullopt;
1505 case BO_Or:
1506 return IsInt ? std::optional(llvm::AtomicRMWInst::Or) : std::nullopt;
1507 case BO_Xor:
1508 return IsInt ? std::optional(llvm::AtomicRMWInst::Xor) : std::nullopt;
1509 case BO_LT: // min
1510 if (IsInt)
1511 return IsSigned ? llvm::AtomicRMWInst::Min : llvm::AtomicRMWInst::UMin;
1512 return std::nullopt;
1513 case BO_GT: // max
1514 if (IsInt)
1515 return IsSigned ? llvm::AtomicRMWInst::Max : llvm::AtomicRMWInst::UMax;
1516 return std::nullopt;
1517 default:
1518 return std::nullopt;
1519 }
1520}
1521
1522///
1523/// Design of OpenMP reductions on the GPU
1524///
1525/// Consider a typical OpenMP program with one or more reduction
1526/// clauses:
1527///
1528/// float foo;
1529/// double bar;
1530/// #pragma omp target teams distribute parallel for \
1531/// reduction(+:foo) reduction(*:bar)
1532/// for (int i = 0; i < N; i++) {
1533/// foo += A[i]; bar *= B[i];
1534/// }
1535///
1536/// where 'foo' and 'bar' are reduced across all OpenMP threads in
1537/// all teams. In our OpenMP implementation on the NVPTX device an
1538/// OpenMP team is mapped to a CUDA threadblock and OpenMP threads
1539/// within a team are mapped to CUDA threads within a threadblock.
1540/// Our goal is to efficiently aggregate values across all OpenMP
1541/// threads such that:
1542///
1543/// - the compiler and runtime are logically concise, and
1544/// - the reduction is performed efficiently in a hierarchical
1545/// manner as follows: within OpenMP threads in the same warp,
1546/// across warps in a threadblock, and finally across teams on
1547/// the NVPTX device.
1548///
1549/// Introduction to Decoupling
1550///
1551/// We would like to decouple the compiler and the runtime so that the
1552/// latter is ignorant of the reduction variables (number, data types)
1553/// and the reduction operators. This allows a simpler interface
1554/// and implementation while still attaining good performance.
1555///
1556/// Pseudocode for the aforementioned OpenMP program generated by the
1557/// compiler is as follows:
1558///
1559/// 1. Create private copies of reduction variables on each OpenMP
1560/// thread: 'foo_private', 'bar_private'
1561/// 2. Each OpenMP thread reduces the chunk of 'A' and 'B' assigned
1562/// to it and writes the result in 'foo_private' and 'bar_private'
1563/// respectively.
1564/// 3. Call the OpenMP runtime on the GPU to reduce within a team
1565/// and store the result on the team master:
1566///
1567/// __kmpc_nvptx_parallel_reduce_nowait_v2(...,
1568/// reduceData, shuffleReduceFn, interWarpCpyFn)
1569///
1570/// where:
1571/// struct ReduceData {
1572/// double *foo;
1573/// double *bar;
1574/// } reduceData
1575/// reduceData.foo = &foo_private
1576/// reduceData.bar = &bar_private
1577///
1578/// 'shuffleReduceFn' and 'interWarpCpyFn' are pointers to two
1579/// auxiliary functions generated by the compiler that operate on
1580/// variables of type 'ReduceData'. They aid the runtime perform
1581/// algorithmic steps in a data agnostic manner.
1582///
1583/// 'shuffleReduceFn' is a pointer to a function that reduces data
1584/// of type 'ReduceData' across two OpenMP threads (lanes) in the
1585/// same warp. It takes the following arguments as input:
1586///
1587/// a. variable of type 'ReduceData' on the calling lane,
1588/// b. its lane_id,
1589/// c. an offset relative to the current lane_id to generate a
1590/// remote_lane_id. The remote lane contains the second
1591/// variable of type 'ReduceData' that is to be reduced.
1592/// d. an algorithm version parameter determining which reduction
1593/// algorithm to use.
1594///
1595/// 'shuffleReduceFn' retrieves data from the remote lane using
1596/// efficient GPU shuffle intrinsics and reduces, using the
1597/// algorithm specified by the 4th parameter, the two operands
1598/// element-wise. The result is written to the first operand.
1599///
1600/// Different reduction algorithms are implemented in different
1601/// runtime functions, all calling 'shuffleReduceFn' to perform
1602/// the essential reduction step. Therefore, based on the 4th
1603/// parameter, this function behaves slightly differently to
1604/// cooperate with the runtime to ensure correctness under
1605/// different circumstances.
1606///
1607/// 'InterWarpCpyFn' is a pointer to a function that transfers
1608/// reduced variables across warps. It tunnels, through CUDA
1609/// shared memory, the thread-private data of type 'ReduceData'
1610/// from lane 0 of each warp to a lane in the first warp.
1611/// 4. Call the OpenMP runtime on the GPU to reduce across teams.
1612/// The last team writes the global reduced value to memory.
1613///
1614/// ret = __kmpc_nvptx_teams_reduce_nowait(...,
1615/// reduceData, shuffleReduceFn, interWarpCpyFn,
1616/// scratchpadCopyFn, loadAndReduceFn)
1617///
1618/// 'scratchpadCopyFn' is a helper that stores reduced
1619/// data from the team master to a scratchpad array in
1620/// global memory.
1621///
1622/// 'loadAndReduceFn' is a helper that loads data from
1623/// the scratchpad array and reduces it with the input
1624/// operand.
1625///
1626/// These compiler generated functions hide address
1627/// calculation and alignment information from the runtime.
1628/// 5. if ret == 1:
1629/// The team master of the last team stores the reduced
1630/// result to the globals in memory.
1631/// foo += reduceData.foo; bar *= reduceData.bar
1632///
1633///
1634/// Warp Reduction Algorithms
1635///
1636/// On the warp level, we have three algorithms implemented in the
1637/// OpenMP runtime depending on the number of active lanes:
1638///
1639/// Full Warp Reduction
1640///
1641/// The reduce algorithm within a warp where all lanes are active
1642/// is implemented in the runtime as follows:
1643///
1644/// full_warp_reduce(void *reduce_data,
1645/// kmp_ShuffleReductFctPtr ShuffleReduceFn) {
1646/// for (int offset = WARPSIZE/2; offset > 0; offset /= 2)
1647/// ShuffleReduceFn(reduce_data, 0, offset, 0);
1648/// }
1649///
1650/// The algorithm completes in log(2, WARPSIZE) steps.
1651///
1652/// 'ShuffleReduceFn' is used here with lane_id set to 0 because it is
1653/// not used therefore we save instructions by not retrieving lane_id
1654/// from the corresponding special registers. The 4th parameter, which
1655/// represents the version of the algorithm being used, is set to 0 to
1656/// signify full warp reduction.
1657///
1658/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1659///
1660/// #reduce_elem refers to an element in the local lane's data structure
1661/// #remote_elem is retrieved from a remote lane
1662/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1663/// reduce_elem = reduce_elem REDUCE_OP remote_elem;
1664///
1665/// Contiguous Partial Warp Reduction
1666///
1667/// This reduce algorithm is used within a warp where only the first
1668/// 'n' (n <= WARPSIZE) lanes are active. It is typically used when the
1669/// number of OpenMP threads in a parallel region is not a multiple of
1670/// WARPSIZE. The algorithm is implemented in the runtime as follows:
1671///
1672/// void
1673/// contiguous_partial_reduce(void *reduce_data,
1674/// kmp_ShuffleReductFctPtr ShuffleReduceFn,
1675/// int size, int lane_id) {
1676/// int curr_size;
1677/// int offset;
1678/// curr_size = size;
1679/// mask = curr_size/2;
1680/// while (offset>0) {
1681/// ShuffleReduceFn(reduce_data, lane_id, offset, 1);
1682/// curr_size = (curr_size+1)/2;
1683/// offset = curr_size/2;
1684/// }
1685/// }
1686///
1687/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1688///
1689/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1690/// if (lane_id < offset)
1691/// reduce_elem = reduce_elem REDUCE_OP remote_elem
1692/// else
1693/// reduce_elem = remote_elem
1694///
1695/// This algorithm assumes that the data to be reduced are located in a
1696/// contiguous subset of lanes starting from the first. When there is
1697/// an odd number of active lanes, the data in the last lane is not
1698/// aggregated with any other lane's dat but is instead copied over.
1699///
1700/// Dispersed Partial Warp Reduction
1701///
1702/// This algorithm is used within a warp when any discontiguous subset of
1703/// lanes are active. It is used to implement the reduction operation
1704/// across lanes in an OpenMP simd region or in a nested parallel region.
1705///
1706/// void
1707/// dispersed_partial_reduce(void *reduce_data,
1708/// kmp_ShuffleReductFctPtr ShuffleReduceFn) {
1709/// int size, remote_id;
1710/// int logical_lane_id = number_of_active_lanes_before_me() * 2;
1711/// do {
1712/// remote_id = next_active_lane_id_right_after_me();
1713/// # the above function returns 0 of no active lane
1714/// # is present right after the current lane.
1715/// size = number_of_active_lanes_in_this_warp();
1716/// logical_lane_id /= 2;
1717/// ShuffleReduceFn(reduce_data, logical_lane_id,
1718/// remote_id-1-threadIdx.x, 2);
1719/// } while (logical_lane_id % 2 == 0 && size > 1);
1720/// }
1721///
1722/// There is no assumption made about the initial state of the reduction.
1723/// Any number of lanes (>=1) could be active at any position. The reduction
1724/// result is returned in the first active lane.
1725///
1726/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1727///
1728/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1729/// if (lane_id % 2 == 0 && offset > 0)
1730/// reduce_elem = reduce_elem REDUCE_OP remote_elem
1731/// else
1732/// reduce_elem = remote_elem
1733///
1734///
1735/// Intra-Team Reduction
1736///
1737/// This function, as implemented in the runtime call
1738/// '__kmpc_nvptx_parallel_reduce_nowait_v2', aggregates data across OpenMP
1739/// threads in a team. It first reduces within a warp using the
1740/// aforementioned algorithms. We then proceed to gather all such
1741/// reduced values at the first warp.
1742///
1743/// The runtime makes use of the function 'InterWarpCpyFn', which copies
1744/// data from each of the "warp master" (zeroth lane of each warp, where
1745/// warp-reduced data is held) to the zeroth warp. This step reduces (in
1746/// a mathematical sense) the problem of reduction across warp masters in
1747/// a block to the problem of warp reduction.
1748///
1749///
1750/// Inter-Team Reduction
1751///
1752/// Once a team has reduced its data to a single value, it is stored in
1753/// a global scratchpad array. Since each team has a distinct slot, this
1754/// can be done without locking.
1755///
1756/// The last team to write to the scratchpad array proceeds to reduce the
1757/// scratchpad array. One or more workers in the last team use the helper
1758/// 'loadAndReduceDataFn' to load and reduce values from the array, i.e.,
1759/// the k'th worker reduces every k'th element.
1760///
1761/// Finally, a call is made to '__kmpc_nvptx_parallel_reduce_nowait_v2' to
1762/// reduce across workers and compute a globally reduced value.
1763///
1767 ArrayRef<const Expr *> ReductionOps, ReductionOptionsTy Options) {
1768 if (!CGF.HaveInsertPoint())
1769 return;
1770
1771 bool ParallelReduction = isOpenMPParallelDirective(Options.ReductionKind);
1772 bool TeamsReduction = isOpenMPTeamsDirective(Options.ReductionKind);
1773
1774 if (Options.SimpleReduction) {
1775 assert(!TeamsReduction && !ParallelReduction &&
1776 "Invalid reduction selection in emitReduction.");
1777 (void)ParallelReduction;
1778 CGOpenMPRuntime::emitReduction(CGF, Loc, Privates, LHSExprs, RHSExprs,
1779 ReductionOps, Options);
1780 return;
1781 }
1782
1783 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> VarFieldMap;
1784 llvm::SmallVector<const ValueDecl *, 4> PrivatesReductions(Privates.size());
1785 int Cnt = 0;
1786 for (const Expr *DRE : Privates) {
1787 PrivatesReductions[Cnt] = cast<DeclRefExpr>(DRE)->getDecl();
1788 ++Cnt;
1789 }
1790 const RecordDecl *ReductionRec = ::buildRecordForGlobalizedVars(
1791 CGM.getContext(), PrivatesReductions, {}, VarFieldMap, 1);
1792
1793 // The atomic cross-team reduction fast path is opt-in. Hand each eligible
1794 // scalar reduction an atomic combiner; createReductionsGPU uses the atomic
1795 // path only if every reduction in the set has one. Track whether that holds
1796 // so we can skip the (then unused) per-team buffer registration.
1797 bool UseAtomicReduction =
1798 TeamsReduction && CGM.getLangOpts().OpenMPTargetAtomicReduction;
1799 bool AllAtomicable = UseAtomicReduction;
1800
1801 // Source location for the ident struct
1802 llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);
1803
1804 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;
1805 InsertPointTy AllocaIP(CGF.AllocaInsertPt->getIterator());
1806 InsertPointTy CodeGenIP(CGF.Builder.GetInsertPoint());
1807 llvm::OpenMPIRBuilder::LocationDescription OmpLoc(
1808 CodeGenIP, CGF.SourceLocToDebugLoc(Loc));
1810
1812 unsigned Idx = 0;
1813 for (const Expr *Private : Privates) {
1814 llvm::Type *ElementType;
1815 llvm::Value *Variable;
1816 llvm::Value *PrivateVariable;
1817 llvm::OpenMPIRBuilder::ReductionGenAtomicCBTy AtomicReductionGen = nullptr;
1818 ElementType = CGF.ConvertTypeForMem(Private->getType());
1819 const auto *RHSVar =
1820 cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[Idx])->getDecl());
1821 PrivateVariable = CGF.GetAddrOfLocalVar(RHSVar).emitRawPointer(CGF);
1822 const auto *LHSVar =
1823 cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[Idx])->getDecl());
1824 Variable = CGF.GetAddrOfLocalVar(LHSVar).emitRawPointer(CGF);
1825 llvm::OpenMPIRBuilder::EvalKind EvalKind;
1826 switch (CGF.getEvaluationKind(Private->getType())) {
1827 case TEK_Scalar:
1828 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Scalar;
1829 break;
1830 case TEK_Complex:
1831 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Complex;
1832 break;
1833 case TEK_Aggregate:
1834 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Aggregate;
1835 break;
1836 }
1837 auto ReductionGen = [&](InsertPointTy CodeGenIP, unsigned I,
1838 llvm::Value **LHSPtr, llvm::Value **RHSPtr,
1839 llvm::Function *NewFunc) {
1840 CGF.Builder.restoreIP(CodeGenIP);
1841 auto *CurFn = CGF.CurFn;
1842 CGF.CurFn = NewFunc;
1843
1844 const bool DisableDebugInfo = !NewFunc->getSubprogram();
1845 llvm::DebugLoc SavedDebugLoc;
1846 if (DisableDebugInfo) {
1847 // The helper has no DISubprogram of its own, so a debug location here
1848 // would name the enclosing function's scope, which is invalid IR.
1849 // Suppress them, as the other OpenMPIRBuilder-generated helpers do.
1850 SavedDebugLoc = CGF.Builder.getCurrentDebugLocation();
1851 CGF.Builder.SetCurrentDebugLocation({});
1852 CGF.disableDebugInfo();
1853 }
1854
1855 *LHSPtr = CGF.GetAddrOfLocalVar(
1856 cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[I])->getDecl()))
1857 .emitRawPointer(CGF);
1858 *RHSPtr = CGF.GetAddrOfLocalVar(
1859 cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[I])->getDecl()))
1860 .emitRawPointer(CGF);
1861
1862 emitSingleReductionCombiner(CGF, ReductionOps[I], Privates[I],
1863 cast<DeclRefExpr>(LHSExprs[I]),
1864 cast<DeclRefExpr>(RHSExprs[I]));
1865
1866 if (DisableDebugInfo) {
1867 CGF.enableDebugInfo();
1868 CGF.Builder.SetCurrentDebugLocation(SavedDebugLoc);
1869 }
1870
1871 CGF.CurFn = CurFn;
1872 return CGF.Builder.GetInsertPoint();
1873 };
1874
1875 // For the atomic fast path, hand this reduction an atomic combiner if it is
1876 // a scalar with a direct atomicrmw; otherwise the set is not fully
1877 // atomicable and falls back to the buffer path.
1878 if (UseAtomicReduction) {
1879 std::optional<llvm::AtomicRMWInst::BinOp> AtomicOp;
1880 if (EvalKind == llvm::OpenMPIRBuilder::EvalKind::Scalar) {
1881 if (std::optional<BinaryOperatorKind> BOK =
1882 getReductionBinOpKind(ReductionOps[Idx]))
1883 AtomicOp = getReductionAtomicRMWOp(*BOK, Private->getType());
1884 }
1885 if (!AtomicOp) {
1886 AllAtomicable = false;
1887 } else {
1888 llvm::AtomicRMWInst::BinOp Op = *AtomicOp;
1889 llvm::Align Alignment =
1890 CGM.getModule().getDataLayout().getPrefTypeAlign(ElementType);
1891 // Device (agent) scope suffices: all teams accumulate on-device and the
1892 // host reads the result only after the kernel (via map-back), so the
1893 // far costlier system scope is unnecessary. The
1894 // no.fine.grained/no.remote memory metadata is omitted so the atomic
1895 // stays correct under USM.
1896 llvm::SyncScope::ID SSID = CGF.getTargetHooks().getLLVMSyncScopeID(
1898 llvm::AtomicOrdering::Monotonic, CGF.getLLVMContext());
1899 AtomicReductionGen = [Op, Alignment,
1900 SSID](InsertPointTy IP, llvm::Type *EltTy,
1901 llvm::Value *LHS, llvm::Value *RHS)
1902 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {
1903 llvm::IRBuilder<> Builder(IP);
1904 llvm::Value *Val = Builder.CreateLoad(EltTy, RHS);
1905 Builder.CreateAtomicRMW(Op, LHS, Val, Alignment,
1906 llvm::AtomicOrdering::Monotonic, SSID);
1907 return Builder.GetInsertPoint();
1908 };
1909 }
1910 }
1911
1912 ReductionInfos.emplace_back(llvm::OpenMPIRBuilder::ReductionInfo(
1913 ElementType, Variable, PrivateVariable, EvalKind,
1914 /*ReductionGen=*/nullptr, ReductionGen, AtomicReductionGen,
1915 /*DataPtrPtrGen=*/nullptr));
1916 Idx++;
1917 }
1918
1919 // The atomic path folds directly into the mapped variable and needs no
1920 // per-team buffer; register the record for buffer allocation otherwise.
1921 if (TeamsReduction && !AllAtomicable)
1922 TeamsReductions.push_back(ReductionRec);
1923
1924 bool IsSPMD = getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD;
1925 llvm::OpenMPIRBuilder::InsertPointTy AfterIP =
1926 cantFail(OMPBuilder.createReductionsGPU(
1927 OmpLoc, AllocaIP, CodeGenIP, ReductionInfos, /*IsByRef=*/{}, false,
1928 TeamsReduction, IsSPMD,
1929 llvm::OpenMPIRBuilder::ReductionGenCBKind::Clang,
1930 CGF.getTarget().getGridValue(), RTLoc));
1931 CGF.Builder.restoreIP(AfterIP);
1932}
1933
1934const VarDecl *
1936 const VarDecl *NativeParam) const {
1937 if (!NativeParam->getType()->isReferenceType())
1938 return NativeParam;
1939 QualType ArgType = NativeParam->getType();
1941 const Type *NonQualTy = QC.strip(ArgType);
1942 QualType PointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
1943 if (const auto *Attr = FD->getAttr<OMPCaptureKindAttr>()) {
1944 if (Attr->getCaptureKind() == OMPC_map) {
1945 PointeeTy = CGM.getContext().getAddrSpaceQualType(PointeeTy,
1947 }
1948 }
1949 ArgType = CGM.getContext().getPointerType(PointeeTy);
1950 QC.addRestrict();
1951 ArgType = QC.apply(CGM.getContext(), ArgType);
1952 if (isa<ImplicitParamDecl>(NativeParam))
1954 CGM.getContext(), /*DC=*/nullptr, NativeParam->getLocation(),
1956 return ParmVarDecl::Create(
1957 CGM.getContext(),
1958 const_cast<DeclContext *>(NativeParam->getDeclContext()),
1959 NativeParam->getBeginLoc(), NativeParam->getLocation(),
1960 NativeParam->getIdentifier(), ArgType,
1961 /*TInfo=*/nullptr, SC_None, /*DefArg=*/nullptr);
1962}
1963
1964Address
1966 const VarDecl *NativeParam,
1967 const VarDecl *TargetParam) const {
1968 assert(NativeParam != TargetParam &&
1969 NativeParam->getType()->isReferenceType() &&
1970 "Native arg must not be the same as target arg.");
1971 Address LocalAddr = CGF.GetAddrOfLocalVar(TargetParam);
1972 QualType NativeParamType = NativeParam->getType();
1974 const Type *NonQualTy = QC.strip(NativeParamType);
1975 QualType NativePointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
1976 unsigned NativePointeeAddrSpace =
1977 CGF.getTypes().getTargetAddressSpace(NativePointeeTy);
1978 QualType TargetTy = TargetParam->getType();
1979 llvm::Value *TargetAddr = CGF.EmitLoadOfScalar(LocalAddr, /*Volatile=*/false,
1980 TargetTy, SourceLocation());
1981 // Cast to native address space.
1983 TargetAddr,
1984 llvm::PointerType::get(CGF.getLLVMContext(), NativePointeeAddrSpace));
1985 Address NativeParamAddr = CGF.CreateMemTemp(NativeParamType);
1986 CGF.EmitStoreOfScalar(TargetAddr, NativeParamAddr, /*Volatile=*/false,
1987 NativeParamType);
1988 return NativeParamAddr;
1989}
1990
1992 CodeGenFunction &CGF, SourceLocation Loc, llvm::FunctionCallee OutlinedFn,
1993 ArrayRef<llvm::Value *> Args) const {
1995 TargetArgs.reserve(Args.size());
1996 auto *FnType = OutlinedFn.getFunctionType();
1997 for (unsigned I = 0, E = Args.size(); I < E; ++I) {
1998 if (FnType->isVarArg() && FnType->getNumParams() <= I) {
1999 TargetArgs.append(std::next(Args.begin(), I), Args.end());
2000 break;
2001 }
2002 llvm::Type *TargetType = FnType->getParamType(I);
2003 llvm::Value *NativeArg = Args[I];
2004 if (!TargetType->isPointerTy()) {
2005 TargetArgs.emplace_back(NativeArg);
2006 continue;
2007 }
2008 TargetArgs.emplace_back(
2009 CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(NativeArg, TargetType));
2010 }
2011 CGOpenMPRuntime::emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, TargetArgs);
2012}
2013
2014/// Emit function which wraps the outline parallel region
2015/// and controls the arguments which are passed to this function.
2016/// The wrapper ensures that the outlined function is called
2017/// with the correct arguments when data is shared.
2018llvm::Function *CGOpenMPRuntimeGPU::createParallelDataSharingWrapper(
2019 llvm::Function *OutlinedParallelFn, const OMPExecutableDirective &D) {
2020 ASTContext &Ctx = CGM.getContext();
2021 const auto &CS = *D.getCapturedStmt(OMPD_parallel);
2022
2023 // Create a function that takes as argument the source thread.
2024 FunctionArgList WrapperArgs;
2025 QualType Int16QTy =
2026 Ctx.getIntTypeForBitwidth(/*DestWidth=*/16, /*Signed=*/false);
2027 QualType Int32QTy =
2028 Ctx.getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/false);
2029 auto *ParallelLevelArg = ImplicitParamDecl::Create(
2030 Ctx, /*DC=*/nullptr, D.getBeginLoc(),
2031 /*Id=*/nullptr, Int16QTy, ImplicitParamKind::Other);
2032 auto *WrapperArg = ImplicitParamDecl::Create(
2033 Ctx, /*DC=*/nullptr, D.getBeginLoc(),
2034 /*Id=*/nullptr, Int32QTy, ImplicitParamKind::Other);
2035 WrapperArgs.emplace_back(ParallelLevelArg);
2036 WrapperArgs.emplace_back(WrapperArg);
2037
2038 const CGFunctionInfo &CGFI =
2040
2041 auto *Fn = llvm::Function::Create(
2042 CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage,
2043 Twine(OutlinedParallelFn->getName(), "_wrapper"), &CGM.getModule());
2044
2045 // Ensure we do not inline the function. This is trivially true for the ones
2046 // passed to __kmpc_fork_call but the ones calles in serialized regions
2047 // could be inlined. This is not a perfect but it is closer to the invariant
2048 // we want, namely, every data environment starts with a new function.
2049 // TODO: We should pass the if condition to the runtime function and do the
2050 // handling there. Much cleaner code.
2051 Fn->addFnAttr(llvm::Attribute::NoInline);
2052
2054 Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
2055
2056 CodeGenFunction CGF(CGM, /*suppressNewContext=*/true);
2057 CGF.StartFunction(GlobalDecl(), Ctx.VoidTy, Fn, CGFI, WrapperArgs,
2058 D.getBeginLoc(), D.getBeginLoc());
2059
2060 const auto *RD = CS.getCapturedRecordDecl();
2061 auto CurField = RD->field_begin();
2062
2063 Address ZeroAddr = CGF.CreateDefaultAlignTempAlloca(CGF.Int32Ty,
2064 /*Name=*/".zero.addr");
2065 CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);
2066 // Get the array of arguments.
2068
2069 Args.emplace_back(CGF.GetAddrOfLocalVar(WrapperArg).emitRawPointer(CGF));
2070 Args.emplace_back(ZeroAddr.emitRawPointer(CGF));
2071
2072 CGBuilderTy &Bld = CGF.Builder;
2073 auto CI = CS.capture_begin();
2074
2075 // Use global memory for data sharing.
2076 // Handle passing of global args to workers.
2077 RawAddress GlobalArgs =
2078 CGF.CreateDefaultAlignTempAlloca(CGF.VoidPtrPtrTy, "global_args");
2079 llvm::Value *GlobalArgsPtr = GlobalArgs.getPointer();
2080 llvm::Value *DataSharingArgs[] = {GlobalArgsPtr};
2081 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
2082 CGM.getModule(), OMPRTL___kmpc_get_shared_variables),
2083 DataSharingArgs);
2084
2085 // Retrieve the shared variables from the list of references returned
2086 // by the runtime. Pass the variables to the outlined function.
2087 Address SharedArgListAddress = Address::invalid();
2088 if (CS.capture_size() > 0 ||
2089 isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {
2090 SharedArgListAddress = CGF.EmitLoadOfPointer(
2091 GlobalArgs, CGF.getContext()
2093 .castAs<PointerType>());
2094 }
2095 unsigned Idx = 0;
2096 if (isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {
2097 Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);
2099 Src, Bld.getPtrTy(0), CGF.SizeTy);
2100 llvm::Value *LB = CGF.EmitLoadOfScalar(
2101 TypedAddress,
2102 /*Volatile=*/false,
2104 cast<OMPLoopDirective>(D).getLowerBoundVariable()->getExprLoc());
2105 Args.emplace_back(LB);
2106 ++Idx;
2107 Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);
2108 TypedAddress = Bld.CreatePointerBitCastOrAddrSpaceCast(Src, Bld.getPtrTy(0),
2109 CGF.SizeTy);
2110 llvm::Value *UB = CGF.EmitLoadOfScalar(
2111 TypedAddress,
2112 /*Volatile=*/false,
2114 cast<OMPLoopDirective>(D).getUpperBoundVariable()->getExprLoc());
2115 Args.emplace_back(UB);
2116 ++Idx;
2117 }
2118 if (CS.capture_size() > 0) {
2119 ASTContext &CGFContext = CGF.getContext();
2120 for (unsigned I = 0, E = CS.capture_size(); I < E; ++I, ++CI, ++CurField) {
2121 QualType ElemTy = CurField->getType();
2122 Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, I + Idx);
2124 Src, CGF.ConvertTypeForMem(CGFContext.getPointerType(ElemTy)),
2125 CGF.ConvertTypeForMem(ElemTy));
2126 llvm::Value *Arg = CGF.EmitLoadOfScalar(TypedAddress,
2127 /*Volatile=*/false,
2128 CGFContext.getPointerType(ElemTy),
2129 CI->getLocation());
2130 if (CI->capturesVariableByCopy() &&
2131 !CI->getCapturedVar()->getType()->isAnyPointerType()) {
2132 Arg = castValueToType(CGF, Arg, ElemTy, CGFContext.getUIntPtrType(),
2133 CI->getLocation());
2134 }
2135 Args.emplace_back(Arg);
2136 }
2137 }
2138
2139 emitOutlinedFunctionCall(CGF, D.getBeginLoc(), OutlinedParallelFn, Args);
2140 CGF.FinishFunction();
2141 return Fn;
2142}
2143
2145 const Decl *D) {
2146 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
2147 return;
2148
2149 assert(D && "Expected function or captured|block decl.");
2150 assert(FunctionGlobalizedDecls.count(CGF.CurFn) == 0 &&
2151 "Function is registered already.");
2152 assert((!TeamAndReductions.first || TeamAndReductions.first == D) &&
2153 "Team is set but not processed.");
2154 const Stmt *Body = nullptr;
2155 bool NeedToDelayGlobalization = false;
2156 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
2157 Body = FD->getBody();
2158 } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
2159 Body = BD->getBody();
2160 } else if (const auto *CD = dyn_cast<CapturedDecl>(D)) {
2161 Body = CD->getBody();
2162 NeedToDelayGlobalization = CGF.CapturedStmtInfo->getKind() == CR_OpenMP;
2163 if (NeedToDelayGlobalization &&
2164 getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD)
2165 return;
2166 }
2167 if (!Body)
2168 return;
2169 CheckVarsEscapingDeclContext VarChecker(CGF, TeamAndReductions.second);
2170 VarChecker.Visit(Body);
2171 const RecordDecl *GlobalizedVarsRecord =
2172 VarChecker.getGlobalizedRecord(IsInTTDRegion);
2173 TeamAndReductions.first = nullptr;
2174 TeamAndReductions.second.clear();
2175 ArrayRef<const ValueDecl *> EscapedVariableLengthDecls =
2176 VarChecker.getEscapedVariableLengthDecls();
2177 ArrayRef<const ValueDecl *> DelayedVariableLengthDecls =
2178 VarChecker.getDelayedVariableLengthDecls();
2179 if (!GlobalizedVarsRecord && EscapedVariableLengthDecls.empty() &&
2180 DelayedVariableLengthDecls.empty())
2181 return;
2182 auto I = FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;
2183 I->getSecond().MappedParams =
2184 std::make_unique<CodeGenFunction::OMPMapVars>();
2185 I->getSecond().EscapedParameters.insert(
2186 VarChecker.getEscapedParameters().begin(),
2187 VarChecker.getEscapedParameters().end());
2188 I->getSecond().EscapedVariableLengthDecls.append(
2189 EscapedVariableLengthDecls.begin(), EscapedVariableLengthDecls.end());
2190 I->getSecond().DelayedVariableLengthDecls.append(
2191 DelayedVariableLengthDecls.begin(), DelayedVariableLengthDecls.end());
2192 DeclToAddrMapTy &Data = I->getSecond().LocalVarData;
2193 for (const ValueDecl *VD : VarChecker.getEscapedDecls()) {
2194 assert(VD->isCanonicalDecl() && "Expected canonical declaration");
2195 Data.try_emplace(VD);
2196 }
2197 if (!NeedToDelayGlobalization) {
2198 emitGenericVarsProlog(CGF, D->getBeginLoc());
2199 struct GlobalizationScope final : EHScopeStack::Cleanup {
2200 GlobalizationScope() = default;
2201
2202 void Emit(CodeGenFunction &CGF, Flags flags) override {
2203 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())
2204 .emitGenericVarsEpilog(CGF);
2205 }
2206 };
2207 CGF.EHStack.pushCleanup<GlobalizationScope>(NormalAndEHCleanup);
2208 }
2209}
2210
2212 const VarDecl *VD) {
2213 if (VD && VD->hasAttr<OMPAllocateDeclAttr>()) {
2214 const auto *A = VD->getAttr<OMPAllocateDeclAttr>();
2215 auto AS = LangAS::Default;
2216 switch (A->getAllocatorType()) {
2217 case OMPAllocateDeclAttr::OMPNullMemAlloc:
2218 case OMPAllocateDeclAttr::OMPDefaultMemAlloc:
2219 case OMPAllocateDeclAttr::OMPHighBWMemAlloc:
2220 case OMPAllocateDeclAttr::OMPLowLatMemAlloc:
2221 break;
2222 case OMPAllocateDeclAttr::OMPThreadMemAlloc:
2223 return Address::invalid();
2224 case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:
2225 // TODO: implement aupport for user-defined allocators.
2226 return Address::invalid();
2227 case OMPAllocateDeclAttr::OMPConstMemAlloc:
2229 break;
2230 case OMPAllocateDeclAttr::OMPPTeamMemAlloc:
2232 break;
2233 case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:
2234 case OMPAllocateDeclAttr::OMPCGroupMemAlloc:
2235 break;
2236 }
2237 llvm::Type *VarTy = CGF.ConvertTypeForMem(VD->getType());
2238 auto *GV = new llvm::GlobalVariable(
2239 CGM.getModule(), VarTy, /*isConstant=*/false,
2240 llvm::GlobalValue::InternalLinkage, llvm::PoisonValue::get(VarTy),
2241 VD->getName(),
2242 /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal,
2243 CGM.getContext().getTargetAddressSpace(AS));
2244 CharUnits Align = CGM.getContext().getDeclAlign(VD);
2245 GV->setAlignment(Align.getAsAlign());
2246 return Address(
2248 GV, CGF.Builder.getPtrTy(CGM.getContext().getTargetAddressSpace(
2249 VD->getType().getAddressSpace()))),
2250 VarTy, Align);
2251 }
2252
2253 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
2254 return Address::invalid();
2255
2256 VD = VD->getCanonicalDecl();
2257 auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
2258 if (I == FunctionGlobalizedDecls.end())
2259 return Address::invalid();
2260 auto VDI = I->getSecond().LocalVarData.find(VD);
2261 if (VDI != I->getSecond().LocalVarData.end())
2262 return VDI->second.PrivateAddr;
2263 if (VD->hasAttrs()) {
2265 E(VD->attr_end());
2266 IT != E; ++IT) {
2267 auto VDI = I->getSecond().LocalVarData.find(
2268 cast<VarDecl>(cast<DeclRefExpr>(IT->getRef())->getDecl())
2269 ->getCanonicalDecl());
2270 if (VDI != I->getSecond().LocalVarData.end())
2271 return VDI->second.PrivateAddr;
2272 }
2273 }
2274
2275 return Address::invalid();
2276}
2277
2279 FunctionGlobalizedDecls.erase(CGF.CurFn);
2281}
2282
2284 CodeGenFunction &CGF, const OMPLoopDirective &S,
2285 OpenMPDistScheduleClauseKind &ScheduleKind,
2286 llvm::Value *&Chunk) const {
2287 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
2288 if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {
2289 ScheduleKind = OMPC_DIST_SCHEDULE_static;
2290 Chunk = CGF.EmitScalarConversion(
2291 RT.getGPUNumThreads(CGF),
2292 CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),
2293 S.getIterationVariable()->getType(), S.getBeginLoc());
2294 return;
2295 }
2297 CGF, S, ScheduleKind, Chunk);
2298}
2299
2301 CodeGenFunction &CGF, const OMPLoopDirective &S,
2302 OpenMPScheduleClauseKind &ScheduleKind,
2303 const Expr *&ChunkExpr) const {
2304 ScheduleKind = OMPC_SCHEDULE_static;
2305 // Chunk size is 1 in this case.
2306 llvm::APInt ChunkSize(32, 1);
2307 ChunkExpr = IntegerLiteral::Create(CGF.getContext(), ChunkSize,
2308 CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),
2309 SourceLocation());
2310}
2311
2313 CodeGenFunction &CGF, const OMPExecutableDirective &D) const {
2314 assert(isOpenMPTargetExecutionDirective(D.getDirectiveKind()) &&
2315 " Expected target-based directive.");
2316 const CapturedStmt *CS = D.getCapturedStmt(OMPD_target);
2317 for (const CapturedStmt::Capture &C : CS->captures()) {
2318 // Capture variables captured by reference in lambdas for target-based
2319 // directives.
2320 if (!C.capturesVariable())
2321 continue;
2322 const VarDecl *VD = C.getCapturedVar();
2323 const auto *RD = VD->getType()
2327 if (!RD || !RD->isLambda())
2328 continue;
2329 Address VDAddr = CGF.GetAddrOfLocalVar(VD);
2330 LValue VDLVal;
2332 VDLVal = CGF.EmitLoadOfReferenceLValue(VDAddr, VD->getType());
2333 else
2334 VDLVal = CGF.MakeAddrLValue(
2335 VDAddr, VD->getType().getCanonicalType().getNonReferenceType());
2336 llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;
2337 FieldDecl *ThisCapture = nullptr;
2338 RD->getCaptureFields(Captures, ThisCapture);
2339 if (ThisCapture && CGF.CapturedStmtInfo->isCXXThisExprCaptured()) {
2340 LValue ThisLVal =
2341 CGF.EmitLValueForFieldInitialization(VDLVal, ThisCapture);
2342 llvm::Value *CXXThis = CGF.LoadCXXThis();
2343 CGF.EmitStoreOfScalar(CXXThis, ThisLVal);
2344 }
2345 for (const LambdaCapture &LC : RD->captures()) {
2346 if (LC.getCaptureKind() != LCK_ByRef)
2347 continue;
2348 const ValueDecl *VD = LC.getCapturedVar();
2349 // FIXME: For now VD is always a VarDecl because OpenMP does not support
2350 // capturing structured bindings in lambdas yet.
2351 if (!CS->capturesVariable(cast<VarDecl>(VD)))
2352 continue;
2353 auto It = Captures.find(VD);
2354 assert(It != Captures.end() && "Found lambda capture without field.");
2355 LValue VarLVal = CGF.EmitLValueForFieldInitialization(VDLVal, It->second);
2356 Address VDAddr = CGF.GetAddrOfLocalVar(cast<VarDecl>(VD));
2358 VDAddr = CGF.EmitLoadOfReferenceLValue(VDAddr,
2359 VD->getType().getCanonicalType())
2360 .getAddress();
2361 CGF.EmitStoreOfScalar(VDAddr.emitRawPointer(CGF), VarLVal);
2362 }
2363 }
2364}
2365
2367 LangAS &AS) {
2368 if (!VD || !VD->hasAttr<OMPAllocateDeclAttr>())
2369 return false;
2370 const auto *A = VD->getAttr<OMPAllocateDeclAttr>();
2371 switch(A->getAllocatorType()) {
2372 case OMPAllocateDeclAttr::OMPNullMemAlloc:
2373 case OMPAllocateDeclAttr::OMPDefaultMemAlloc:
2374 // Not supported, fallback to the default mem space.
2375 case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:
2376 case OMPAllocateDeclAttr::OMPCGroupMemAlloc:
2377 case OMPAllocateDeclAttr::OMPHighBWMemAlloc:
2378 case OMPAllocateDeclAttr::OMPLowLatMemAlloc:
2379 case OMPAllocateDeclAttr::OMPThreadMemAlloc:
2380 AS = LangAS::Default;
2381 return true;
2382 case OMPAllocateDeclAttr::OMPConstMemAlloc:
2384 return true;
2385 case OMPAllocateDeclAttr::OMPPTeamMemAlloc:
2387 return true;
2388 case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:
2389 llvm_unreachable("Expected predefined allocator for the variables with the "
2390 "static storage.");
2391 }
2392 return false;
2393}
2394
2395/// Check to see if target architecture supports unified addressing which is
2396/// a restriction for OpenMP requires clause "unified_shared_memory".
2398 StringRef CPU = CGM.getTarget().getTargetOpts().CPU;
2399 if (CGM.getTarget().getTriple().isNVPTX() &&
2400 !llvm::NVPTX::supportsUnifiedAddressing(llvm::NVPTX::parseArch(CPU))) {
2401 for (const OMPClause *Clause : D->clauselists()) {
2402 if (Clause->getClauseKind() == OMPC_unified_shared_memory) {
2403 CGM.getDiags().Report(Clause->getBeginLoc(),
2404 diag::err_omp_unified_shared_memory_unsupported)
2405 << CPU;
2406 return;
2407 }
2408 }
2409 }
2410
2412}
2413
2415 CGBuilderTy &Bld = CGF.Builder;
2416 llvm::Module *M = &CGF.CGM.getModule();
2417 const char *LocSize = "__kmpc_get_hardware_num_threads_in_block";
2418 llvm::Function *F = M->getFunction(LocSize);
2419 if (!F) {
2420 F = llvm::Function::Create(llvm::FunctionType::get(CGF.Int32Ty, {}, false),
2421 llvm::GlobalVariable::ExternalLinkage, LocSize,
2422 &CGF.CGM.getModule());
2423 }
2424 return Bld.CreateCall(F, {}, "nvptx_num_threads");
2425}
2426
2429 return CGF.EmitRuntimeCall(
2430 OMPBuilder.getOrCreateRuntimeFunction(
2431 CGM.getModule(), OMPRTL___kmpc_get_hardware_thread_id_in_block),
2432 Args);
2433}
#define V(N, I)
static std::optional< BinaryOperatorKind > getReductionBinOpKind(const Expr *ReductionOp)
Extracts the built-in reduction operator from a combiner of the form x = x / <op> rhs (or the min/max...
static void getTeamsReductionVars(ASTContext &Ctx, const OMPExecutableDirective &D, llvm::SmallVectorImpl< const ValueDecl * > &Vars)
Get list of reduction variables from the teams ... directives.
static llvm::Value * castValueToType(CodeGenFunction &CGF, llvm::Value *Val, QualType ValTy, QualType CastTy, SourceLocation Loc)
Cast value to the specified type.
static void getDistributeLastprivateVars(ASTContext &Ctx, const OMPExecutableDirective &D, llvm::SmallVectorImpl< const ValueDecl * > &Vars)
Get list of lastprivate variables from the teams distribute ... or teams {distribute ....
static bool hasNestedSPMDDirective(ASTContext &Ctx, const OMPExecutableDirective &D)
Check for inner (nested) SPMD construct, if any.
static bool supportsSPMDExecutionMode(ASTContext &Ctx, const OMPExecutableDirective &D)
static std::optional< llvm::AtomicRMWInst::BinOp > getReductionAtomicRMWOp(BinaryOperatorKind BOK, QualType Ty)
Maps a built-in reduction operator to an atomicrmw opcode for the atomic cross-team reduction fast pa...
This file defines OpenMP nodes for declarative directives.
This file defines OpenMP AST classes for clauses.
static std::pair< ValueDecl *, bool > getPrivateItem(Sema &S, Expr *&RefExpr, SourceLocation &ELoc, SourceRange &ERange, bool AllowArraySection=false, bool AllowAssumedSizeArray=false, StringRef DiagType="")
This file defines OpenMP AST classes for executable directives and clauses.
static FieldDecl * Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, const IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable, InClassInitStyle InitStyle)
Definition Decl.cpp:4791
This is a common base class for loop directives ('omp simd', 'omp for', 'omp for simd' etc....
Expr * getIterationVariable() const
This represents 'ompx_bare' clause in the 'pragma omp target teams ...' directive.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:245
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
CanQualType VoidPtrTy
QualType getUIntPtrType() const
Return a type compatible with "uintptr_t" (C99 7.18.1.4), as defined by the target.
QualType getIntTypeForBitwidth(unsigned DestWidth, unsigned Signed) const
getIntTypeForBitwidth - sets integer QualTy according to specified details: bitwidth,...
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
CanQualType VoidTy
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:971
Attr - This represents one attribute.
Definition Attr.h:47
ArrayRef< Capture > captures() const
Definition Decl.h:4953
const BlockDecl * getBlockDecl() const
Definition Expr.h:6770
Expr * getCallee()
Definition Expr.h:3159
arg_range arguments()
Definition Expr.h:3264
Describes the capture of either a variable, or 'this', or variable-length array type.
Definition Stmt.h:3963
This captures a statement into a function.
Definition Stmt.h:3950
bool capturesVariable(const VarDecl *Var) const
True if this variable has been captured.
Definition Stmt.cpp:1517
capture_range captures()
Definition Stmt.h:4088
CastKind getCastKind() const
Definition Expr.h:3789
Expr * getSubExpr()
Definition Expr.h:3795
This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
bool isZero() const
Test whether the quantity equals zero.
Definition CharUnits.h:101
llvm::Align getAsAlign() const
Returns Quantity as a valid llvm::Align, Beware llvm::Align assumes power of two 8-bit bytes.
Definition CharUnits.h:159
QuantityType getQuantity() const
Get the raw integer representation of this quantity.
Definition CharUnits.h:155
Like RawAddress, an abstract representation of an aligned address, but the pointer contained in this ...
Definition Address.h:128
static Address invalid()
Definition Address.h:176
llvm::Value * emitRawPointer(CodeGenFunction &CGF) const
Return the pointer contained in this class after authenticating it and adding offset to it if necessa...
Definition Address.h:253
Address withElementType(llvm::Type *ElemTy) const
Return address with different element type, but same pointer and alignment.
Definition Address.h:276
llvm::StoreInst * CreateStore(llvm::Value *Val, Address Addr, bool IsVolatile=false)
Definition CGBuilder.h:148
Address CreatePointerBitCastOrAddrSpaceCast(Address Addr, llvm::Type *Ty, llvm::Type *ElementTy, const llvm::Twine &Name="")
Definition CGBuilder.h:215
Address CreateConstArrayGEP(Address Addr, uint64_t Index, const llvm::Twine &Name="")
Given addr = [n x T]* ... produce name = getelementptr inbounds addr, i64 0, i64 index where i64 is a...
Definition CGBuilder.h:253
Address CreateConstInBoundsGEP(Address Addr, uint64_t Index, const llvm::Twine &Name="")
Given addr = T* ... produce name = getelementptr inbounds addr, i64 index where i64 is actually the t...
Definition CGBuilder.h:273
CGFunctionInfo - Class to encapsulate the information about a function definition.
llvm::Function * emitTeamsOutlinedFunction(CodeGenFunction &CGF, const OMPExecutableDirective &D, const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind, const RegionCodeGenTy &CodeGen) override
Emits inlined function for the specified OpenMP teams.
void emitProcBindClause(CodeGenFunction &CGF, llvm::omp::ProcBindKind ProcBind, SourceLocation Loc) override
Emit call to void __kmpc_push_proc_bind(ident_t *loc, kmp_int32global_tid, int proc_bind) to generate...
void emitReduction(CodeGenFunction &CGF, SourceLocation Loc, ArrayRef< const Expr * > Privates, ArrayRef< const Expr * > LHSExprs, ArrayRef< const Expr * > RHSExprs, ArrayRef< const Expr * > ReductionOps, ReductionOptionsTy Options) override
Emit a code for reduction clause.
DataSharingMode
Target codegen is specialized based on two data-sharing modes: CUDA, in which the local variables are...
@ DS_Generic
Generic data-sharing mode.
void getDefaultDistScheduleAndChunk(CodeGenFunction &CGF, const OMPLoopDirective &S, OpenMPDistScheduleClauseKind &ScheduleKind, llvm::Value *&Chunk) const override
Choose a default value for the dist_schedule clause.
Address getAddressOfLocalVariable(CodeGenFunction &CGF, const VarDecl *VD) override
Gets the OpenMP-specific address of the local variable.
void emitFunctionProlog(CodeGenFunction &CGF, const Decl *D) override
Emits OpenMP-specific function prolog.
void getDefaultScheduleAndChunk(CodeGenFunction &CGF, const OMPLoopDirective &S, OpenMPScheduleClauseKind &ScheduleKind, const Expr *&ChunkExpr) const override
Choose a default value for the schedule clause.
void emitNumTeamsClause(CodeGenFunction &CGF, const Expr *NumTeams, const Expr *ThreadLimit, SourceLocation Loc) override
This function ought to emit, in the general case, a call to.
void emitCriticalRegion(CodeGenFunction &CGF, StringRef CriticalName, const RegionCodeGenTy &CriticalOpGen, SourceLocation Loc, const Expr *Hint=nullptr) override
Emits a critical region.
void emitTeamsCall(CodeGenFunction &CGF, const OMPExecutableDirective &D, SourceLocation Loc, llvm::Function *OutlinedFn, ArrayRef< llvm::Value * > CapturedVars) override
Emits code for teams call of the OutlinedFn with variables captured in a record which address is stor...
bool hasAllocateAttributeForGlobalVar(const VarDecl *VD, LangAS &AS) override
Checks if the variable has associated OMPAllocateDeclAttr attribute with the predefined allocator and...
void getKmpcFreeShared(CodeGenFunction &CGF, const std::pair< llvm::Value *, llvm::Value * > &AddrSizePair) override
Get call to __kmpc_free_shared.
llvm::Function * emitParallelOutlinedFunction(CodeGenFunction &CGF, const OMPExecutableDirective &D, const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind, const RegionCodeGenTy &CodeGen) override
Emits inlined function for the specified OpenMP parallel.
void functionFinished(CodeGenFunction &CGF) override
Cleans up references to the objects in finished function.
llvm::Value * getGPUThreadID(CodeGenFunction &CGF)
Get the id of the current thread on the GPU.
void processRequiresDirective(const OMPRequiresDecl *D) override
Perform check on requires decl to ensure that target architecture supports unified addressing.
bool isDelayedVariableLengthDecl(CodeGenFunction &CGF, const VarDecl *VD) const override
Declare generalized virtual functions which need to be defined by all specializations of OpenMPGPURun...
void emitOutlinedFunctionCall(CodeGenFunction &CGF, SourceLocation Loc, llvm::FunctionCallee OutlinedFn, ArrayRef< llvm::Value * > Args={}) const override
Emits call of the outlined function with the provided arguments, translating these arguments to corre...
Address getParameterAddress(CodeGenFunction &CGF, const VarDecl *NativeParam, const VarDecl *TargetParam) const override
Gets the address of the native argument basing on the address of the target-specific parameter.
ExecutionMode
Defines the execution mode.
@ EM_NonSPMD
Non-SPMD execution mode (1 master thread, others are workers).
@ EM_Unknown
Unknown execution mode (orphaned directive).
@ EM_SPMD
SPMD execution mode (all threads are worker threads).
void emitBarrierCall(CodeGenFunction &CGF, SourceLocation Loc, OpenMPDirectiveKind Kind, bool EmitChecks=true, bool ForceSimpleCall=false) override
Emit an implicit/explicit barrier for OpenMP threads.
llvm::Value * getGPUNumThreads(CodeGenFunction &CGF)
Get the maximum number of threads in a block of the GPU.
const VarDecl * translateParameter(const FieldDecl *FD, const VarDecl *NativeParam) const override
Translates the native parameter of outlined function if this is required for target.
std::pair< llvm::Value *, llvm::Value * > getKmpcAllocShared(CodeGenFunction &CGF, const VarDecl *VD) override
Get call to __kmpc_alloc_shared.
bool isGPU() const override
Returns true if the current target is a GPU.
llvm::Value * emitSeverityClause(OpenMPSeverityClauseKind Severity, SourceLocation Loc) override
llvm::Value * emitMessageClause(CodeGenFunction &CGF, const Expr *Message, SourceLocation Loc) override
void emitParallelCall(CodeGenFunction &CGF, SourceLocation Loc, llvm::Function *OutlinedFn, ArrayRef< llvm::Value * > CapturedVars, const Expr *IfCond, llvm::Value *NumThreads, OpenMPNumThreadsClauseModifier NumThreadsModifier=OMPC_NUMTHREADS_unknown, OpenMPSeverityClauseKind Severity=OMPC_SEVERITY_fatal, const Expr *Message=nullptr) override
Emits code for parallel or serial call of the OutlinedFn with variables captured in a record which ad...
void emitNumThreadsClause(CodeGenFunction &CGF, llvm::Value *NumThreads, SourceLocation Loc, OpenMPNumThreadsClauseModifier Modifier=OMPC_NUMTHREADS_unknown, OpenMPSeverityClauseKind Severity=OMPC_SEVERITY_fatal, SourceLocation SeverityLoc=SourceLocation(), const Expr *Message=nullptr, SourceLocation MessageLoc=SourceLocation()) override
Emits call to void __kmpc_push_num_threads(ident_t *loc, kmp_int32global_tid, kmp_int32 num_threads) ...
void adjustTargetSpecificDataForLambdas(CodeGenFunction &CGF, const OMPExecutableDirective &D) const override
Adjust some parameters for the target-based directives, like addresses of the variables captured by r...
virtual Address emitThreadIDAddress(CodeGenFunction &CGF, SourceLocation Loc)
Emits address of the word in a memory where current thread id is stored.
static const Stmt * getSingleCompoundChild(ASTContext &Ctx, const Stmt *Body)
Checks if the Body is the CompoundStmt and returns its child statement iff there is only one that is ...
llvm::Value * emitUpdateLocation(CodeGenFunction &CGF, SourceLocation Loc, unsigned Flags=0, bool EmitLoc=false)
Emits object of ident_t type with info for source location.
virtual void functionFinished(CodeGenFunction &CGF)
Cleans up references to the objects in finished function.
virtual llvm::Function * emitTeamsOutlinedFunction(CodeGenFunction &CGF, const OMPExecutableDirective &D, const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind, const RegionCodeGenTy &CodeGen)
Emits outlined function for the specified OpenMP teams directive D.
llvm::OpenMPIRBuilder OMPBuilder
An OpenMP-IR-Builder instance.
virtual void emitTargetOutlinedFunctionHelper(const OMPExecutableDirective &D, StringRef ParentName, llvm::Function *&OutlinedFn, llvm::Constant *&OutlinedFnID, bool IsOffloadEntry, const RegionCodeGenTy &CodeGen)
Helper to emit outlined function for 'target' directive.
bool hasRequiresUnifiedSharedMemory() const
Return whether the unified_shared_memory has been specified.
virtual void processRequiresDirective(const OMPRequiresDecl *D)
Perform check on requires decl to ensure that target architecture supports unified addressing.
llvm::Value * getThreadID(CodeGenFunction &CGF, SourceLocation Loc)
Gets thread id value for the current thread.
void computeMinAndMaxThreadsAndTeams(const OMPExecutableDirective &D, CodeGenFunction &CGF, llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs &Attrs)
Helper to determine the min/max number of threads/teams for D.
static unsigned getDefaultFlagsForBarriers(OpenMPDirectiveKind Kind)
Returns default flags for the barriers depending on the directive, for which this barier is going to ...
virtual llvm::Function * emitParallelOutlinedFunction(CodeGenFunction &CGF, const OMPExecutableDirective &D, const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind, const RegionCodeGenTy &CodeGen)
Emits outlined function for the specified OpenMP parallel directive D.
virtual void getDefaultDistScheduleAndChunk(CodeGenFunction &CGF, const OMPLoopDirective &S, OpenMPDistScheduleClauseKind &ScheduleKind, llvm::Value *&Chunk) const
Choose default schedule type and chunk value for the dist_schedule clause.
llvm::Type * getIdentTyPointerTy()
Returns pointer to ident_t type.
void emitSingleReductionCombiner(CodeGenFunction &CGF, const Expr *ReductionOp, const Expr *PrivateRef, const DeclRefExpr *LHS, const DeclRefExpr *RHS)
Emits single reduction combiner.
llvm::OpenMPIRBuilder & getOMPBuilder()
virtual void emitCriticalRegion(CodeGenFunction &CGF, StringRef CriticalName, const RegionCodeGenTy &CriticalOpGen, SourceLocation Loc, const Expr *Hint=nullptr)
Emits a critical region.
virtual void emitOutlinedFunctionCall(CodeGenFunction &CGF, SourceLocation Loc, llvm::FunctionCallee OutlinedFn, ArrayRef< llvm::Value * > Args={}) const
Emits call of the outlined function with the provided arguments, translating these arguments to corre...
virtual void emitReduction(CodeGenFunction &CGF, SourceLocation Loc, ArrayRef< const Expr * > Privates, ArrayRef< const Expr * > LHSExprs, ArrayRef< const Expr * > RHSExprs, ArrayRef< const Expr * > ReductionOps, ReductionOptionsTy Options)
Emit a code for reduction clause.
The scope used to remap some variables as private in the OpenMP loop body (or other captured region e...
CodeGenFunction - This class organizes the per-function state that is used while generating LLVM code...
LValue EmitLoadOfReferenceLValue(LValue RefLVal)
Definition CGExpr.cpp:3444
CGCapturedStmtInfo * CapturedStmtInfo
llvm::BasicBlock * createBasicBlock(const Twine &name="", llvm::Function *parent=nullptr, llvm::BasicBlock *before=nullptr)
createBasicBlock - Create an LLVM basic block.
const LangOptions & getLangOpts() const
Address EmitLoadOfPointer(Address Ptr, const PointerType *PtrTy, LValueBaseInfo *BaseInfo=nullptr, TBAAAccessInfo *TBAAInfo=nullptr)
Load a pointer with type PtrTy stored at address Ptr.
Definition CGExpr.cpp:3453
LValue MakeNaturalAlignPointeeRawAddrLValue(llvm::Value *V, QualType T)
Same as MakeNaturalAlignPointeeAddrLValue except that the pointer is known to be unsigned.
llvm::AssertingVH< llvm::Instruction > AllocaInsertPt
AllocaInsertPoint - This is an instruction in the entry block before which we prefer to insert alloca...
llvm::DebugLoc SourceLocToDebugLoc(SourceLocation Location)
Converts Location to a DebugLoc, if debug information is enabled.
RawAddress CreateDefaultAlignTempAlloca(llvm::Type *Ty, const Twine &Name="tmp")
CreateDefaultAlignedTempAlloca - This creates an alloca with the default ABI alignment of the given L...
Definition CGExpr.cpp:187
const TargetInfo & getTarget() const
void StartFunction(GlobalDecl GD, QualType RetTy, llvm::Function *Fn, const CGFunctionInfo &FnInfo, const FunctionArgList &Args, SourceLocation Loc=SourceLocation(), SourceLocation StartLoc=SourceLocation())
Emit code for the start of a function.
llvm::Value * EvaluateExprAsBool(const Expr *E)
EvaluateExprAsBool - Perform the usual unary conversions on the specified expression and compare the ...
Definition CGExpr.cpp:244
bool HaveInsertPoint() const
HaveInsertPoint - True if an insertion point is defined.
llvm::Value * getTypeSize(QualType Ty)
Returns calculated size of the specified type.
LValue EmitLValueForFieldInitialization(LValue Base, const FieldDecl *Field)
EmitLValueForFieldInitialization - Like EmitLValueForField, except that if the Field is a reference,...
Definition CGExpr.cpp:6125
const TargetCodeGenInfo & getTargetHooks() const
RawAddress CreateMemTempWithoutCast(QualType T, const Twine &Name="tmp")
CreateMemTemp - Create a temporary memory object of the given type, with appropriate alignmen without...
Definition CGExpr.cpp:236
llvm::Value * EmitLoadOfScalar(Address Addr, bool Volatile, QualType Ty, SourceLocation Loc, AlignmentSource Source=AlignmentSource::Type, bool isNontemporal=false)
EmitLoadOfScalar - Load a scalar value from an address, taking care to appropriately convert from the...
llvm::CallInst * EmitRuntimeCall(llvm::FunctionCallee callee, const Twine &name="")
llvm::Type * ConvertTypeForMem(QualType T)
CodeGenTypes & getTypes() const
static TypeEvaluationKind getEvaluationKind(QualType T)
getEvaluationKind - Return the TypeEvaluationKind of QualType T.
void EmitBranch(llvm::BasicBlock *Block)
EmitBranch - Emit a branch to the specified basic block from the current insert block,...
Definition CGStmt.cpp:675
RawAddress CreateMemTemp(QualType T, const Twine &Name="tmp", RawAddress *Alloca=nullptr)
CreateMemTemp - Create a temporary memory object of the given type, with appropriate alignmen and cas...
Definition CGExpr.cpp:200
LValue MakeAddrLValue(Address Addr, QualType T, AlignmentSource Source=AlignmentSource::Type)
void FinishFunction(SourceLocation EndLoc=SourceLocation())
FinishFunction - Complete IR generation of the current function.
llvm::Value * LoadCXXThis()
LoadCXXThis - Load the value of 'this'.
Address GetAddrOfLocalVar(const VarDecl *VD)
GetAddrOfLocalVar - Return the address of a local variable.
llvm::LLVMContext & getLLVMContext()
llvm::Value * EmitScalarConversion(llvm::Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc)
Emit a conversion from the specified type to the specified destination type, both of which are LLVM s...
void EmitStoreOfScalar(llvm::Value *Value, Address Addr, bool Volatile, QualType Ty, AlignmentSource Source=AlignmentSource::Type, bool isInit=false, bool isNontemporal=false)
EmitStoreOfScalar - Store a scalar value to an address, taking care to appropriately convert from the...
void EmitBlock(llvm::BasicBlock *BB, bool IsFinished=false)
EmitBlock - Emit the given block.
Definition CGStmt.cpp:655
This class organizes the cross-function state that is used while generating LLVM code.
void SetInternalFunctionAttributes(GlobalDecl GD, llvm::Function *F, const CGFunctionInfo &FI)
Set the attributes on the LLVM function for the given decl and function info.
llvm::Module & getModule() const
CGOpenMPRuntime & getOpenMPRuntime()
Return a reference to the configured OpenMP runtime.
ASTContext & getContext() const
llvm::LLVMContext & getLLVMContext()
llvm::FunctionType * GetFunctionType(const CGFunctionInfo &Info)
GetFunctionType - Get the LLVM function type for.
Definition CGCall.cpp:2092
const CGFunctionInfo & arrangeBuiltinFunctionDeclaration(QualType resultType, const FunctionArgList &args)
A builtin function is a freestanding function using the default C conventions.
Definition CGCall.cpp:774
unsigned getTargetAddressSpace(QualType T) const
FunctionArgList - Type for representing both the decl and type of parameters to a function.
Definition CGCall.h:378
LValue - This represents an lvalue references.
Definition CGValue.h:183
Address getAddress() const
Definition CGValue.h:373
A basic class for pre|post-action for advanced codegen sequence for OpenMP region.
An abstract representation of an aligned address.
Definition Address.h:42
llvm::Value * getPointer() const
Definition Address.h:66
Class provides a way to call simple version of codegen for OpenMP region, or an advanced with possibl...
void setAction(PrePostActionTy &Action) const
llvm::SyncScope::ID getLLVMSyncScopeID(const LangOptions &LangOpts, SyncScope Scope, llvm::AtomicOrdering Ordering, llvm::LLVMContext &Ctx) const
Get the syncscope used in LLVM IR as a SyncScope ID.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition DeclBase.h:1467
void addDecl(Decl *D)
Add the declaration D into this context.
ValueDecl * getDecl()
Definition Expr.h:1383
decl_range decls()
Definition Stmt.h:1690
T * getAttr() const
Definition DeclBase.h:582
bool hasAttrs() const
Definition DeclBase.h:527
attr_iterator attr_end() const
Definition DeclBase.h:551
bool isCanonicalDecl() const
Whether this particular Decl is a canonical one.
Definition DeclBase.h:1002
attr_iterator attr_begin() const
Definition DeclBase.h:548
SourceLocation getLocation() const
Definition DeclBase.h:448
DeclContext * getDeclContext()
Definition DeclBase.h:457
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:440
AttrVec & getAttrs()
Definition DeclBase.h:533
bool hasAttr() const
Definition DeclBase.h:586
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
Definition DeclBase.h:996
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:833
This represents one expression.
Definition Expr.h:114
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3129
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3125
bool isLValue() const
isLValue - True if this expression is an "l-value" according to the rules of the current language.
Definition Expr.h:286
QualType getType() const
Definition Expr.h:146
Represents a member of a struct/union/class.
Definition Decl.h:3311
static FieldDecl * Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, const IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable, InClassInitStyle InitStyle)
Definition Decl.cpp:4791
GlobalDecl - represents a global declaration.
Definition GlobalDecl.h:60
static ImplicitParamDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation IdLoc, const IdentifierInfo *Id, QualType T, ImplicitParamKind ParamKind)
Create implicit parameter.
Definition Decl.cpp:5699
static IntegerLiteral * Create(const ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l)
Returns a new integer literal with value 'V' and type 'type'.
Definition Expr.cpp:985
Describes the capture of a variable or of this, or of a C++1y init-capture.
bool isInitCapture(const LambdaCapture *Capture) const
Determine whether one of this lambda's captures is an init-capture.
Definition ExprCXX.cpp:1390
capture_range captures() const
Retrieve this lambda's captures.
Definition ExprCXX.cpp:1403
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:297
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:303
This is a basic class for representing single OpenMP clause.
This represents 'pragma omp requires...' directive.
Definition DeclOpenMP.h:479
clauselist_range clauselists()
Definition DeclOpenMP.h:504
static ParmVarDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, const IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, StorageClass S, Expr *DefArg)
Definition Decl.cpp:2963
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3403
A (possibly-)qualified type.
Definition TypeBase.h:938
LangAS getAddressSpace() const
Return the address space of this type.
Definition TypeBase.h:8704
QualType getNonReferenceType() const
If Type is a reference type (e.g., const int&), returns the type that the reference refers to ("const...
Definition TypeBase.h:8763
QualType getCanonicalType() const
Definition TypeBase.h:8630
A qualifier set is used to build a set of qualifiers.
Definition TypeBase.h:8518
const Type * strip(QualType type)
Collect any qualifiers on the given type and return an unqualified type.
Definition TypeBase.h:8525
QualType apply(const ASTContext &Context, QualType QT) const
Apply the collected qualifiers to the given type.
Definition Type.cpp:4982
Represents a struct/union/class.
Definition Decl.h:4476
virtual void completeDefinition()
Note that the definition of this type is now complete.
Definition Decl.cpp:5388
Scope - A scope is a transient data structure that is used while parsing the program.
Definition Scope.h:41
Encodes a location in the source.
Stmt - This represents one statement.
Definition Stmt.h:85
child_range children()
Definition Stmt.cpp:304
void startDefinition()
Starts the definition of this tag declaration.
Definition Decl.cpp:5003
unsigned getNewAlign() const
Return the largest alignment for which a suitably-sized allocation with 'operator new(size_t)' is gua...
Definition TargetInfo.h:762
virtual const llvm::omp::GV & getGridValue() const
The base class of the type hierarchy.
Definition TypeBase.h:1879
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:9248
bool isReferenceType() const
Definition TypeBase.h:8839
bool isLValueReferenceType() const
Definition TypeBase.h:8843
bool hasSignedIntegerRepresentation() const
Determine whether this type has an signed integer representation of some sort, e.g....
Definition Type.cpp:2436
bool isVariablyModifiedType() const
Whether this type is a variably-modified type (C99 6.7.5).
Definition TypeBase.h:2881
bool isFloatingType() const
Definition Type.cpp:2517
Expr * getSubExpr() const
Definition Expr.h:2354
Opcode getOpcode() const
Definition Expr.h:2349
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:714
QualType getType() const
Definition Decl.h:725
bool isInitCapture() const
Whether this variable is the implicit variable for a lambda init-capture.
Definition Decl.cpp:5684
Represents a variable declaration or definition.
Definition Decl.h:934
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition Decl.cpp:2238
bool isInitCapture() const
Whether this variable is the implicit variable for a lambda init-capture.
Definition Decl.h:1615
specific_attr_iterator - Iterates over a subrange of an AttrVec, only providing attributes that are o...
@ Type
The l-value was considered opaque, so the alignment was determined from a type.
Definition CGValue.h:155
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
Definition CGValue.h:146
@ Address
A pointer to a ValueDecl.
Definition Primitives.h:28
Top level wrappers for InstallAPI frontend operations.
CanQual< Type > CanQualType
Represents a canonical, potentially-qualified type.
Privates[]
This class represents the 'transparent' clause in the 'pragma omp task' directive.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ ICIS_NoInit
No in-class initializer.
Definition Specifiers.h:276
bool isOpenMPDistributeDirective(OpenMPDirectiveKind DKind)
Checks if the specified directive is a distribute directive.
@ LCK_ByRef
Capturing by reference.
Definition Lambda.h:37
@ Private
'private' clause, allowed on 'parallel', 'serial', 'loop', 'parallel loop', and 'serial loop' constru...
@ AS_public
Definition Specifiers.h:128
@ CR_OpenMP
bool isOpenMPParallelDirective(OpenMPDirectiveKind DKind)
Checks if the specified directive is a parallel-kind directive.
bool isOpenMPPrivate(OpenMPClauseKind Kind)
Checks if the specified clause is one of private clauses like 'private', 'firstprivate',...
@ SC_None
Definition Specifiers.h:254
OpenMPDistScheduleClauseKind
OpenMP attributes for 'dist_schedule' clause.
bool isOpenMPTargetExecutionDirective(OpenMPDirectiveKind DKind)
Checks if the specified directive is a target code offload directive.
bool isOpenMPTeamsDirective(OpenMPDirectiveKind DKind)
Checks if the specified directive is a teams-kind directive.
OpenMPSeverityClauseKind
OpenMP attributes for 'severity' clause.
bool isOpenMPLoopBoundSharingDirective(OpenMPDirectiveKind Kind)
Checks if the specified directive kind is one of the composite or combined directives that need loop ...
LangAS
Defines the address space values used by the address space qualifier of QualType.
llvm::omp::Directive OpenMPDirectiveKind
OpenMP directives.
Definition OpenMPKinds.h:25
void getOpenMPCaptureRegions(llvm::SmallVectorImpl< OpenMPDirectiveKind > &CaptureRegions, OpenMPDirectiveKind DKind)
Return the captured regions of an OpenMP directive.
OpenMPNumThreadsClauseModifier
U cast(CodeGen::Address addr)
Definition Address.h:327
@ CXXThis
Parameter for C++ 'this' argument.
Definition Decl.h:1775
@ Other
Other implicit parameter.
Definition Decl.h:1787
OpenMPScheduleClauseKind
OpenMP attributes for 'schedule' clause.
Definition OpenMPKinds.h:31
unsigned long uint64_t