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 void VisitCapturedStmt(const CapturedStmt *S) {
359 if (!S)
360 return;
361 for (const CapturedStmt::Capture &C : S->captures()) {
362 if (C.capturesVariable() && !C.capturesVariableByCopy()) {
363 const ValueDecl *VD = C.getCapturedVar();
364 markAsEscaped(VD);
366 VisitValueDecl(VD);
367 }
368 }
369 }
370 void VisitLambdaExpr(const LambdaExpr *E) {
371 if (!E)
372 return;
373 for (const LambdaCapture &C : E->captures()) {
374 if (C.capturesVariable()) {
375 if (C.getCaptureKind() == LCK_ByRef) {
376 const ValueDecl *VD = C.getCapturedVar();
377 markAsEscaped(VD);
379 VisitValueDecl(VD);
380 }
381 }
382 }
383 }
384 void VisitBlockExpr(const BlockExpr *E) {
385 if (!E)
386 return;
387 for (const BlockDecl::Capture &C : E->getBlockDecl()->captures()) {
388 if (C.isByRef()) {
389 const VarDecl *VD = C.getVariable();
390 markAsEscaped(VD);
392 VisitValueDecl(VD);
393 }
394 }
395 }
396 void VisitCallExpr(const CallExpr *E) {
397 if (!E)
398 return;
399 for (const Expr *Arg : E->arguments()) {
400 if (!Arg)
401 continue;
402 if (Arg->isLValue()) {
403 const bool SavedAllEscaped = AllEscaped;
404 AllEscaped = true;
405 Visit(Arg);
406 AllEscaped = SavedAllEscaped;
407 } else {
408 Visit(Arg);
409 }
410 }
411 Visit(E->getCallee());
412 }
413 void VisitDeclRefExpr(const DeclRefExpr *E) {
414 if (!E)
415 return;
416 const ValueDecl *VD = E->getDecl();
417 if (AllEscaped)
418 markAsEscaped(VD);
420 VisitValueDecl(VD);
421 else if (VD->isInitCapture())
422 VisitValueDecl(VD);
423 }
424 void VisitUnaryOperator(const UnaryOperator *E) {
425 if (!E)
426 return;
427 if (E->getOpcode() == UO_AddrOf) {
428 const bool SavedAllEscaped = AllEscaped;
429 AllEscaped = true;
430 Visit(E->getSubExpr());
431 AllEscaped = SavedAllEscaped;
432 } else {
433 Visit(E->getSubExpr());
434 }
435 }
436 void VisitImplicitCastExpr(const ImplicitCastExpr *E) {
437 if (!E)
438 return;
439 if (E->getCastKind() == CK_ArrayToPointerDecay) {
440 const bool SavedAllEscaped = AllEscaped;
441 AllEscaped = true;
442 Visit(E->getSubExpr());
443 AllEscaped = SavedAllEscaped;
444 } else {
445 Visit(E->getSubExpr());
446 }
447 }
448 void VisitExpr(const Expr *E) {
449 if (!E)
450 return;
451 bool SavedAllEscaped = AllEscaped;
452 if (!E->isLValue())
453 AllEscaped = false;
454 for (const Stmt *Child : E->children())
455 if (Child)
456 Visit(Child);
457 AllEscaped = SavedAllEscaped;
458 }
459 void VisitStmt(const Stmt *S) {
460 if (!S)
461 return;
462 for (const Stmt *Child : S->children())
463 if (Child)
464 Visit(Child);
465 }
466
467 /// Returns the record that handles all the escaped local variables and used
468 /// instead of their original storage.
469 const RecordDecl *getGlobalizedRecord(bool IsInTTDRegion) {
470 if (!GlobalizedRD)
471 buildRecordForGlobalizedVars(IsInTTDRegion);
472 return GlobalizedRD;
473 }
474
475 /// Returns the field in the globalized record for the escaped variable.
476 const FieldDecl *getFieldForGlobalizedVar(const ValueDecl *VD) const {
477 assert(GlobalizedRD &&
478 "Record for globalized variables must be generated already.");
479 return MappedDeclsFields.lookup(VD);
480 }
481
482 /// Returns the list of the escaped local variables/parameters.
483 ArrayRef<const ValueDecl *> getEscapedDecls() const {
484 return EscapedDecls.getArrayRef();
485 }
486
487 /// Checks if the escaped local variable is actually a parameter passed by
488 /// value.
489 const llvm::SmallPtrSetImpl<const Decl *> &getEscapedParameters() const {
490 return EscapedParameters;
491 }
492
493 /// Returns the list of the escaped variables with the variably modified
494 /// types.
495 ArrayRef<const ValueDecl *> getEscapedVariableLengthDecls() const {
496 return EscapedVariableLengthDecls.getArrayRef();
497 }
498
499 /// Returns the list of the delayed variables with the variably modified
500 /// types.
501 ArrayRef<const ValueDecl *> getDelayedVariableLengthDecls() const {
502 return DelayedVariableLengthDecls.getArrayRef();
503 }
504};
505} // anonymous namespace
506
508CGOpenMPRuntimeGPU::getExecutionMode() const {
509 return CurrentExecutionMode;
510}
511
513CGOpenMPRuntimeGPU::getDataSharingMode() const {
514 return CurrentDataSharingMode;
515}
516
517/// Check for inner (nested) SPMD construct, if any
519 const OMPExecutableDirective &D) {
520 const auto *CS = D.getInnermostCapturedStmt();
521 const auto *Body =
522 CS->getCapturedStmt()->IgnoreContainers(/*IgnoreCaptured=*/true);
523 const Stmt *ChildStmt = CGOpenMPRuntime::getSingleCompoundChild(Ctx, Body);
524
525 if (const auto *NestedDir =
526 dyn_cast_or_null<OMPExecutableDirective>(ChildStmt)) {
527 OpenMPDirectiveKind DKind = NestedDir->getDirectiveKind();
528 switch (D.getDirectiveKind()) {
529 case OMPD_target:
530 if (isOpenMPParallelDirective(DKind))
531 return true;
532 if (DKind == OMPD_teams) {
533 Body = NestedDir->getInnermostCapturedStmt()->IgnoreContainers(
534 /*IgnoreCaptured=*/true);
535 if (!Body)
536 return false;
537 ChildStmt = CGOpenMPRuntime::getSingleCompoundChild(Ctx, Body);
538 if (const auto *NND =
539 dyn_cast_or_null<OMPExecutableDirective>(ChildStmt)) {
540 DKind = NND->getDirectiveKind();
541 if (isOpenMPParallelDirective(DKind))
542 return true;
543 }
544 }
545 return false;
546 case OMPD_target_teams:
547 return isOpenMPParallelDirective(DKind);
548 case OMPD_target_simd:
549 case OMPD_target_parallel:
550 case OMPD_target_parallel_for:
551 case OMPD_target_parallel_for_simd:
552 case OMPD_target_teams_distribute:
553 case OMPD_target_teams_distribute_simd:
554 case OMPD_target_teams_distribute_parallel_for:
555 case OMPD_target_teams_distribute_parallel_for_simd:
556 case OMPD_parallel:
557 case OMPD_for:
558 case OMPD_parallel_for:
559 case OMPD_parallel_master:
560 case OMPD_parallel_sections:
561 case OMPD_for_simd:
562 case OMPD_parallel_for_simd:
563 case OMPD_cancel:
564 case OMPD_cancellation_point:
565 case OMPD_ordered_standalone:
566 case OMPD_ordered_blockassoc:
567 case OMPD_threadprivate:
568 case OMPD_allocate:
569 case OMPD_task:
570 case OMPD_simd:
571 case OMPD_sections:
572 case OMPD_section:
573 case OMPD_single:
574 case OMPD_master:
575 case OMPD_critical:
576 case OMPD_taskyield:
577 case OMPD_barrier:
578 case OMPD_taskwait:
579 case OMPD_taskgroup:
580 case OMPD_atomic:
581 case OMPD_flush:
582 case OMPD_depobj:
583 case OMPD_scan:
584 case OMPD_teams:
585 case OMPD_target_data:
586 case OMPD_target_exit_data:
587 case OMPD_target_enter_data:
588 case OMPD_distribute:
589 case OMPD_distribute_simd:
590 case OMPD_distribute_parallel_for:
591 case OMPD_distribute_parallel_for_simd:
592 case OMPD_teams_distribute:
593 case OMPD_teams_distribute_simd:
594 case OMPD_teams_distribute_parallel_for:
595 case OMPD_teams_distribute_parallel_for_simd:
596 case OMPD_target_update:
597 case OMPD_declare_simd:
598 case OMPD_declare_variant:
599 case OMPD_begin_declare_variant:
600 case OMPD_end_declare_variant:
601 case OMPD_declare_target:
602 case OMPD_end_declare_target:
603 case OMPD_declare_reduction:
604 case OMPD_declare_mapper:
605 case OMPD_taskloop:
606 case OMPD_taskloop_simd:
607 case OMPD_master_taskloop:
608 case OMPD_master_taskloop_simd:
609 case OMPD_parallel_master_taskloop:
610 case OMPD_parallel_master_taskloop_simd:
611 case OMPD_requires:
612 case OMPD_unknown:
613 default:
614 llvm_unreachable("Unexpected directive.");
615 }
616 }
617
618 return false;
619}
620
622 const OMPExecutableDirective &D) {
623 OpenMPDirectiveKind DirectiveKind = D.getDirectiveKind();
624 switch (DirectiveKind) {
625 case OMPD_target:
626 case OMPD_target_teams:
627 return hasNestedSPMDDirective(Ctx, D);
628 case OMPD_target_parallel_loop:
629 case OMPD_target_parallel:
630 case OMPD_target_parallel_for:
631 case OMPD_target_parallel_for_simd:
632 case OMPD_target_teams_distribute_parallel_for:
633 case OMPD_target_teams_distribute_parallel_for_simd:
634 case OMPD_target_simd:
635 case OMPD_target_teams_distribute_simd:
636 return true;
637 case OMPD_target_teams_distribute:
638 return false;
639 case OMPD_target_teams_loop:
640 // Whether this is true or not depends on how the directive will
641 // eventually be emitted.
642 if (auto *TTLD = dyn_cast<OMPTargetTeamsGenericLoopDirective>(&D))
643 return TTLD->canBeParallelFor();
644 return false;
645 case OMPD_parallel:
646 case OMPD_for:
647 case OMPD_parallel_for:
648 case OMPD_parallel_master:
649 case OMPD_parallel_sections:
650 case OMPD_for_simd:
651 case OMPD_parallel_for_simd:
652 case OMPD_cancel:
653 case OMPD_cancellation_point:
654 case OMPD_ordered_standalone:
655 case OMPD_ordered_blockassoc:
656 case OMPD_threadprivate:
657 case OMPD_allocate:
658 case OMPD_task:
659 case OMPD_simd:
660 case OMPD_sections:
661 case OMPD_section:
662 case OMPD_single:
663 case OMPD_master:
664 case OMPD_critical:
665 case OMPD_taskyield:
666 case OMPD_barrier:
667 case OMPD_taskwait:
668 case OMPD_taskgroup:
669 case OMPD_atomic:
670 case OMPD_flush:
671 case OMPD_depobj:
672 case OMPD_scan:
673 case OMPD_teams:
674 case OMPD_target_data:
675 case OMPD_target_exit_data:
676 case OMPD_target_enter_data:
677 case OMPD_distribute:
678 case OMPD_distribute_simd:
679 case OMPD_distribute_parallel_for:
680 case OMPD_distribute_parallel_for_simd:
681 case OMPD_teams_distribute:
682 case OMPD_teams_distribute_simd:
683 case OMPD_teams_distribute_parallel_for:
684 case OMPD_teams_distribute_parallel_for_simd:
685 case OMPD_target_update:
686 case OMPD_declare_simd:
687 case OMPD_declare_variant:
688 case OMPD_begin_declare_variant:
689 case OMPD_end_declare_variant:
690 case OMPD_declare_target:
691 case OMPD_end_declare_target:
692 case OMPD_declare_reduction:
693 case OMPD_declare_mapper:
694 case OMPD_taskloop:
695 case OMPD_taskloop_simd:
696 case OMPD_master_taskloop:
697 case OMPD_master_taskloop_simd:
698 case OMPD_parallel_master_taskloop:
699 case OMPD_parallel_master_taskloop_simd:
700 case OMPD_requires:
701 case OMPD_unknown:
702 default:
703 break;
704 }
705 llvm_unreachable(
706 "Unknown programming model for OpenMP directive on NVPTX target.");
707}
708
709void CGOpenMPRuntimeGPU::emitNonSPMDKernel(const OMPExecutableDirective &D,
710 StringRef ParentName,
711 llvm::Function *&OutlinedFn,
712 llvm::Constant *&OutlinedFnID,
713 bool IsOffloadEntry,
714 const RegionCodeGenTy &CodeGen) {
715 ExecutionRuntimeModesRAII ModeRAII(CurrentExecutionMode, EM_NonSPMD);
716 EntryFunctionState EST;
717 WrapperFunctionsMap.clear();
718
719 [[maybe_unused]] bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
720 assert(!IsBareKernel && "bare kernel should not be at generic mode");
721
722 // Emit target region as a standalone region.
723 class NVPTXPrePostActionTy : public PrePostActionTy {
724 CGOpenMPRuntimeGPU::EntryFunctionState &EST;
725 const OMPExecutableDirective &D;
726
727 public:
728 NVPTXPrePostActionTy(CGOpenMPRuntimeGPU::EntryFunctionState &EST,
729 const OMPExecutableDirective &D)
730 : EST(EST), D(D) {}
731 void Enter(CodeGenFunction &CGF) override {
732 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
733 RT.emitKernelInit(D, CGF, EST, /* IsSPMD */ false);
734 // Skip target region initialization.
735 RT.setLocThreadIdInsertPt(CGF, /*AtCurrentPoint=*/true);
736 }
737 void Exit(CodeGenFunction &CGF) override {
738 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
739 RT.clearLocThreadIdInsertPt(CGF);
740 RT.emitKernelDeinit(CGF, EST, /* IsSPMD */ false);
741 }
742 } Action(EST, D);
743 CodeGen.setAction(Action);
744 IsInTTDRegion = true;
745 emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,
746 IsOffloadEntry, CodeGen);
747 IsInTTDRegion = false;
748}
749
750void CGOpenMPRuntimeGPU::emitBareKernelEnvironment(
751 const OMPExecutableDirective &D, CodeGenFunction &CGF) {
752 // Bare kernels manage their own initialization and never call
753 // __kmpc_target_init, but the runtime still needs a
754 // '<kernel>_kernel_environment' global to know how the kernel was
755 // configured, so emit it directly here.
756 llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs Attrs;
757 Attrs.ExecFlags = llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_BARE;
758 CGBuilderTy &Bld = CGF.Builder;
759 OMPBuilder.emitKernelEnvironment(Bld, Attrs);
760}
761
762void CGOpenMPRuntimeGPU::emitKernelInit(const OMPExecutableDirective &D,
763 CodeGenFunction &CGF,
764 EntryFunctionState &EST, bool IsSPMD) {
765 llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs Attrs;
766 Attrs.ExecFlags =
767 IsSPMD ? llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_SPMD
768 : llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_GENERIC;
769 computeMinAndMaxThreadsAndTeams(D, CGF, Attrs);
770
771 CGBuilderTy &Bld = CGF.Builder;
772 Bld.restoreIP(OMPBuilder.createTargetInit(Bld, Attrs));
773 if (!IsSPMD)
774 emitGenericVarsProlog(CGF, EST.Loc);
775}
776
777void CGOpenMPRuntimeGPU::emitKernelDeinit(CodeGenFunction &CGF,
778 EntryFunctionState &EST,
779 bool IsSPMD) {
780 if (!IsSPMD)
781 emitGenericVarsEpilog(CGF);
782
783 // This is temporary until we remove the fixed sized buffer.
784 ASTContext &C = CGM.getContext();
785 RecordDecl *StaticRD = C.buildImplicitRecord(
786 "_openmp_teams_reduction_type_$_", RecordDecl::TagKind::Union);
787 StaticRD->startDefinition();
788 for (const RecordDecl *TeamReductionRec : TeamsReductions) {
789 CanQualType RecTy = C.getCanonicalTagType(TeamReductionRec);
790 auto *Field = FieldDecl::Create(
791 C, StaticRD, SourceLocation(), SourceLocation(), nullptr, RecTy,
792 C.getTrivialTypeSourceInfo(RecTy, SourceLocation()),
793 /*BW=*/nullptr, /*Mutable=*/false,
794 /*InitStyle=*/ICIS_NoInit);
795 Field->setAccess(AS_public);
796 StaticRD->addDecl(Field);
797 }
798 StaticRD->completeDefinition();
799 CanQualType StaticTy = C.getCanonicalTagType(StaticRD);
800 llvm::Type *LLVMReductionsBufferTy =
801 CGM.getTypes().ConvertTypeForMem(StaticTy);
802 const auto &DL = CGM.getModule().getDataLayout();
803 uint64_t ReductionDataSize =
804 TeamsReductions.empty()
805 ? 0
806 : DL.getTypeAllocSize(LLVMReductionsBufferTy).getFixedValue();
807 CGBuilderTy &Bld = CGF.Builder;
808 OMPBuilder.createTargetDeinit(Bld, ReductionDataSize);
809 TeamsReductions.clear();
810}
811
812void CGOpenMPRuntimeGPU::emitSPMDKernel(const OMPExecutableDirective &D,
813 StringRef ParentName,
814 llvm::Function *&OutlinedFn,
815 llvm::Constant *&OutlinedFnID,
816 bool IsOffloadEntry,
817 const RegionCodeGenTy &CodeGen) {
818 ExecutionRuntimeModesRAII ModeRAII(CurrentExecutionMode, EM_SPMD);
819 EntryFunctionState EST;
820
821 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
822
823 // Emit target region as a standalone region.
824 class NVPTXPrePostActionTy : public PrePostActionTy {
825 CGOpenMPRuntimeGPU &RT;
826 CGOpenMPRuntimeGPU::EntryFunctionState &EST;
827 bool IsBareKernel;
828 DataSharingMode Mode;
829 const OMPExecutableDirective &D;
830
831 public:
832 NVPTXPrePostActionTy(CGOpenMPRuntimeGPU &RT,
833 CGOpenMPRuntimeGPU::EntryFunctionState &EST,
834 bool IsBareKernel, const OMPExecutableDirective &D)
835 : RT(RT), EST(EST), IsBareKernel(IsBareKernel),
836 Mode(RT.CurrentDataSharingMode), D(D) {}
837 void Enter(CodeGenFunction &CGF) override {
838 if (IsBareKernel) {
839 RT.CurrentDataSharingMode = DataSharingMode::DS_CUDA;
840 RT.emitBareKernelEnvironment(D, CGF);
841 return;
842 }
843 RT.emitKernelInit(D, CGF, EST, /* IsSPMD */ true);
844 // Skip target region initialization.
845 RT.setLocThreadIdInsertPt(CGF, /*AtCurrentPoint=*/true);
846 }
847 void Exit(CodeGenFunction &CGF) override {
848 if (IsBareKernel) {
849 RT.CurrentDataSharingMode = Mode;
850 return;
851 }
852 RT.clearLocThreadIdInsertPt(CGF);
853 RT.emitKernelDeinit(CGF, EST, /* IsSPMD */ true);
854 }
855 } Action(*this, EST, IsBareKernel, D);
856 CodeGen.setAction(Action);
857 IsInTTDRegion = true;
858 emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,
859 IsOffloadEntry, CodeGen);
860 IsInTTDRegion = false;
861}
862
863void CGOpenMPRuntimeGPU::emitTargetOutlinedFunction(
864 const OMPExecutableDirective &D, StringRef ParentName,
865 llvm::Function *&OutlinedFn, llvm::Constant *&OutlinedFnID,
866 bool IsOffloadEntry, const RegionCodeGenTy &CodeGen) {
867 if (!IsOffloadEntry) // Nothing to do.
868 return;
869
870 assert(!ParentName.empty() && "Invalid target region parent name!");
871
872 bool Mode = supportsSPMDExecutionMode(CGM.getContext(), D);
873 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
874 if (Mode || IsBareKernel)
875 emitSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,
876 CodeGen);
877 else
878 emitNonSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,
879 CodeGen);
880}
881
884 llvm::OpenMPIRBuilderConfig Config(
885 CGM.getLangOpts().OpenMPIsTargetDevice, isGPU(),
886 CGM.getLangOpts().OpenMPOffloadMandatory,
887 /*HasRequiresReverseOffload*/ false, /*HasRequiresUnifiedAddress*/ false,
888 hasRequiresUnifiedSharedMemory(), /*HasRequiresDynamicAllocators*/ false);
889 Config.setDefaultTargetAS(
890 CGM.getContext().getTargetInfo().getTargetAddressSpace(LangAS::Default));
891 Config.setRuntimeCC(CGM.getRuntimeCC());
892
893 OMPBuilder.setConfig(Config);
894
895 if (!CGM.getLangOpts().OpenMPIsTargetDevice)
896 llvm_unreachable("OpenMP can only handle device code.");
897
898 if (CGM.getLangOpts().OpenMPCUDAMode)
899 CurrentDataSharingMode = CGOpenMPRuntimeGPU::DS_CUDA;
900
901 llvm::OpenMPIRBuilder &OMPBuilder = getOMPBuilder();
902 if (CGM.getLangOpts().NoGPULib || CGM.getLangOpts().OMPHostIRFile.empty())
903 return;
904
905 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTargetDebug,
906 "__omp_rtl_debug_kind");
907 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTeamSubscription,
908 "__omp_rtl_assume_teams_oversubscription");
909 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPThreadSubscription,
910 "__omp_rtl_assume_threads_oversubscription");
911 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoThreadState,
912 "__omp_rtl_assume_no_thread_state");
913 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoNestedParallelism,
914 "__omp_rtl_assume_no_nested_parallelism");
915}
916
918 ProcBindKind ProcBind,
919 SourceLocation Loc) {
920 // Nothing to do.
921}
922
924 const Expr *Message,
925 SourceLocation Loc) {
926 CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)
927 << getOpenMPClauseName(OMPC_message);
928 return nullptr;
929}
930
931llvm::Value *
933 SourceLocation Loc) {
934 CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)
935 << getOpenMPClauseName(OMPC_severity);
936 return nullptr;
937}
938
940 CodeGenFunction &CGF, llvm::Value *NumThreads, SourceLocation Loc,
942 SourceLocation SeverityLoc, const Expr *Message,
943 SourceLocation MessageLoc) {
944 if (Modifier == OMPC_NUMTHREADS_strict) {
945 CGM.getDiags().Report(Loc,
946 diag::warn_omp_gpu_unsupported_modifier_for_clause)
947 << "strict" << getOpenMPClauseName(OMPC_num_threads);
948 return;
949 }
950
951 // Nothing to do.
952}
953
955 const Expr *NumTeams,
956 const Expr *ThreadLimit,
957 SourceLocation Loc) {}
958
961 const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,
962 const RegionCodeGenTy &CodeGen) {
963 // Emit target region as a standalone region.
964 bool PrevIsInTTDRegion = IsInTTDRegion;
965 IsInTTDRegion = false;
966 auto *OutlinedFun =
968 CGF, D, ThreadIDVar, InnermostKind, CodeGen));
969 IsInTTDRegion = PrevIsInTTDRegion;
970 if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD) {
971 llvm::Function *WrapperFun =
972 createParallelDataSharingWrapper(OutlinedFun, D);
973 WrapperFunctionsMap[OutlinedFun] = WrapperFun;
974 }
975
976 return OutlinedFun;
977}
978
979/// Get list of lastprivate variables from the teams distribute ... or
980/// teams {distribute ...} directives.
981static void
984 assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&
985 "expected teams directive.");
986 const OMPExecutableDirective *Dir = &D;
987 if (!isOpenMPDistributeDirective(D.getDirectiveKind())) {
989 Ctx,
990 D.getInnermostCapturedStmt()->getCapturedStmt()->IgnoreContainers(
991 /*IgnoreCaptured=*/true))) {
992 Dir = dyn_cast_or_null<OMPExecutableDirective>(S);
993 if (Dir && !isOpenMPDistributeDirective(Dir->getDirectiveKind()))
994 Dir = nullptr;
995 }
996 }
997 if (!Dir)
998 return;
999 for (const auto *C : Dir->getClausesOfKind<OMPLastprivateClause>()) {
1000 for (const Expr *E : C->getVarRefs())
1001 Vars.push_back(getPrivateItem(E));
1002 }
1003}
1004
1005/// Get list of reduction variables from the teams ... directives.
1006static void
1009 assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&
1010 "expected teams directive.");
1011 for (const auto *C : D.getClausesOfKind<OMPReductionClause>()) {
1012 for (const Expr *E : C->privates())
1013 Vars.push_back(getPrivateItem(E));
1014 }
1015}
1016
1019 const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,
1020 const RegionCodeGenTy &CodeGen) {
1021 SourceLocation Loc = D.getBeginLoc();
1022
1023 const RecordDecl *GlobalizedRD = nullptr;
1024 llvm::SmallVector<const ValueDecl *, 4> LastPrivatesReductions;
1025 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> MappedDeclsFields;
1026 unsigned WarpSize = CGM.getTarget().getGridValue().GV_Warp_Size;
1027 // Globalize team reductions variable unconditionally in all modes.
1028 if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD)
1029 getTeamsReductionVars(CGM.getContext(), D, LastPrivatesReductions);
1030 if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {
1031 getDistributeLastprivateVars(CGM.getContext(), D, LastPrivatesReductions);
1032 if (!LastPrivatesReductions.empty()) {
1033 GlobalizedRD = ::buildRecordForGlobalizedVars(
1034 CGM.getContext(), {}, LastPrivatesReductions, MappedDeclsFields,
1035 WarpSize);
1036 }
1037 } else if (!LastPrivatesReductions.empty()) {
1038 assert(!TeamAndReductions.first &&
1039 "Previous team declaration is not expected.");
1040 TeamAndReductions.first = D.getCapturedStmt(OMPD_teams)->getCapturedDecl();
1041 std::swap(TeamAndReductions.second, LastPrivatesReductions);
1042 }
1043
1044 // Emit target region as a standalone region.
1045 class NVPTXPrePostActionTy : public PrePostActionTy {
1046 SourceLocation &Loc;
1047 const RecordDecl *GlobalizedRD;
1048 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>
1049 &MappedDeclsFields;
1050
1051 public:
1052 NVPTXPrePostActionTy(
1053 SourceLocation &Loc, const RecordDecl *GlobalizedRD,
1054 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>
1055 &MappedDeclsFields)
1056 : Loc(Loc), GlobalizedRD(GlobalizedRD),
1057 MappedDeclsFields(MappedDeclsFields) {}
1058 void Enter(CodeGenFunction &CGF) override {
1059 auto &Rt =
1060 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
1061 if (GlobalizedRD) {
1062 auto I = Rt.FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;
1063 I->getSecond().MappedParams =
1064 std::make_unique<CodeGenFunction::OMPMapVars>();
1065 DeclToAddrMapTy &Data = I->getSecond().LocalVarData;
1066 for (const auto &Pair : MappedDeclsFields) {
1067 assert(Pair.getFirst()->isCanonicalDecl() &&
1068 "Expected canonical declaration");
1069 Data.try_emplace(Pair.getFirst());
1070 }
1071 }
1072 Rt.emitGenericVarsProlog(CGF, Loc);
1073 }
1074 void Exit(CodeGenFunction &CGF) override {
1075 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())
1076 .emitGenericVarsEpilog(CGF);
1077 }
1078 } Action(Loc, GlobalizedRD, MappedDeclsFields);
1079 CodeGen.setAction(Action);
1080 llvm::Function *OutlinedFun = CGOpenMPRuntime::emitTeamsOutlinedFunction(
1081 CGF, D, ThreadIDVar, InnermostKind, CodeGen);
1082
1083 return OutlinedFun;
1084}
1085
1086void CGOpenMPRuntimeGPU::emitGenericVarsProlog(CodeGenFunction &CGF,
1087 SourceLocation Loc) {
1088 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
1089 return;
1090
1091 CGBuilderTy &Bld = CGF.Builder;
1092
1093 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1094 if (I == FunctionGlobalizedDecls.end())
1095 return;
1096
1097 for (auto &Rec : I->getSecond().LocalVarData) {
1098 const auto *VD = cast<VarDecl>(Rec.first);
1099 bool EscapedParam = I->getSecond().EscapedParameters.count(Rec.first);
1100 QualType VarTy = VD->getType();
1101
1102 // Get the local allocation of a firstprivate variable before sharing
1103 llvm::Value *ParValue;
1104 if (EscapedParam) {
1105 LValue ParLVal =
1106 CGF.MakeAddrLValue(CGF.GetAddrOfLocalVar(VD), VD->getType());
1107 ParValue = CGF.EmitLoadOfScalar(ParLVal, Loc);
1108 }
1109
1110 // Allocate space for the variable to be globalized
1111 llvm::Value *AllocArgs[] = {CGF.getTypeSize(VD->getType())};
1112 llvm::CallBase *VoidPtr =
1113 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1114 CGM.getModule(), OMPRTL___kmpc_alloc_shared),
1115 AllocArgs, VD->getName());
1116 // FIXME: We should use the variables actual alignment as an argument.
1117 VoidPtr->addRetAttr(llvm::Attribute::get(
1118 CGM.getLLVMContext(), llvm::Attribute::Alignment,
1120
1121 // Cast the void pointer and get the address of the globalized variable.
1122 llvm::Value *CastedVoidPtr = Bld.CreatePointerBitCastOrAddrSpaceCast(
1123 VoidPtr, Bld.getPtrTy(0), VD->getName() + "_on_stack");
1124 LValue VarAddr =
1125 CGF.MakeNaturalAlignPointeeRawAddrLValue(CastedVoidPtr, VarTy);
1126 Rec.second.PrivateAddr = VarAddr.getAddress();
1127 Rec.second.GlobalizedVal = VoidPtr;
1128
1129 // Assign the local allocation to the newly globalized location.
1130 if (EscapedParam) {
1131 CGF.EmitStoreOfScalar(ParValue, VarAddr);
1132 I->getSecond().MappedParams->setVarAddr(CGF, VD, VarAddr.getAddress());
1133 }
1134 if (auto *DI = CGF.getDebugInfo())
1135 VoidPtr->setDebugLoc(DI->SourceLocToDebugLoc(VD->getLocation()));
1136 }
1137
1138 for (const auto *ValueD : I->getSecond().EscapedVariableLengthDecls) {
1139 const auto *VD = cast<VarDecl>(ValueD);
1140 std::pair<llvm::Value *, llvm::Value *> AddrSizePair =
1141 getKmpcAllocShared(CGF, VD);
1142 I->getSecond().EscapedVariableLengthDeclsAddrs.emplace_back(AddrSizePair);
1143 LValue Base = CGF.MakeAddrLValue(AddrSizePair.first, VD->getType(),
1144 CGM.getContext().getDeclAlign(VD),
1146 I->getSecond().MappedParams->setVarAddr(CGF, VD, Base.getAddress());
1147 }
1148 I->getSecond().MappedParams->apply(CGF);
1149}
1150
1152 const VarDecl *VD) const {
1153 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1154 if (I == FunctionGlobalizedDecls.end())
1155 return false;
1156
1157 // Check variable declaration is delayed:
1158 return llvm::is_contained(I->getSecond().DelayedVariableLengthDecls, VD);
1159}
1160
1161std::pair<llvm::Value *, llvm::Value *>
1163 const VarDecl *VD) {
1164 CGBuilderTy &Bld = CGF.Builder;
1165
1166 // Compute size and alignment.
1167 llvm::Value *Size = CGF.getTypeSize(VD->getType());
1168 CharUnits Align = CGM.getContext().getDeclAlign(VD);
1169 Size = Bld.CreateNUWAdd(
1170 Size, llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity() - 1));
1171 llvm::Value *AlignVal =
1172 llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity());
1173 Size = Bld.CreateUDiv(Size, AlignVal);
1174 Size = Bld.CreateNUWMul(Size, AlignVal);
1175
1176 // Allocate space for this VLA object to be globalized.
1177 llvm::Value *AllocArgs[] = {Size};
1178 llvm::CallBase *VoidPtr =
1179 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1180 CGM.getModule(), OMPRTL___kmpc_alloc_shared),
1181 AllocArgs, VD->getName());
1182 VoidPtr->addRetAttr(llvm::Attribute::get(
1183 CGM.getLLVMContext(), llvm::Attribute::Alignment, Align.getQuantity()));
1184
1185 return std::make_pair(VoidPtr, Size);
1186}
1187
1189 CodeGenFunction &CGF,
1190 const std::pair<llvm::Value *, llvm::Value *> &AddrSizePair) {
1191 // Deallocate the memory for each globalized VLA object
1192 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1193 CGM.getModule(), OMPRTL___kmpc_free_shared),
1194 {AddrSizePair.first, AddrSizePair.second});
1195}
1196
1197void CGOpenMPRuntimeGPU::emitGenericVarsEpilog(CodeGenFunction &CGF) {
1198 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
1199 return;
1200
1201 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1202 if (I != FunctionGlobalizedDecls.end()) {
1203 // Deallocate the memory for each globalized VLA object that was
1204 // globalized in the prolog (i.e. emitGenericVarsProlog).
1205 for (const auto &AddrSizePair :
1206 llvm::reverse(I->getSecond().EscapedVariableLengthDeclsAddrs)) {
1207 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1208 CGM.getModule(), OMPRTL___kmpc_free_shared),
1209 {AddrSizePair.first, AddrSizePair.second});
1210 }
1211 // Deallocate the memory for each globalized value
1212 for (auto &Rec : llvm::reverse(I->getSecond().LocalVarData)) {
1213 const auto *VD = cast<VarDecl>(Rec.first);
1214 I->getSecond().MappedParams->restore(CGF);
1215
1216 llvm::Value *FreeArgs[] = {Rec.second.GlobalizedVal,
1217 CGF.getTypeSize(VD->getType())};
1218 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1219 CGM.getModule(), OMPRTL___kmpc_free_shared),
1220 FreeArgs);
1221 }
1222 }
1223}
1224
1226 const OMPExecutableDirective &D,
1227 SourceLocation Loc,
1228 llvm::Function *OutlinedFn,
1229 ArrayRef<llvm::Value *> CapturedVars) {
1230 if (!CGF.HaveInsertPoint())
1231 return;
1232
1233 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
1234
1236 /*Name=*/".zero.addr");
1237 CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);
1239 // We don't emit any thread id function call in bare kernel, but because the
1240 // outlined function has a pointer argument, we emit a nullptr here.
1241 if (IsBareKernel)
1242 OutlinedFnArgs.push_back(llvm::ConstantPointerNull::get(CGM.VoidPtrTy));
1243 else
1244 OutlinedFnArgs.push_back(emitThreadIDAddress(CGF, Loc).emitRawPointer(CGF));
1245 OutlinedFnArgs.push_back(ZeroAddr.getPointer());
1246 OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
1247 emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, OutlinedFnArgs);
1248}
1249
1251 CodeGenFunction &CGF, SourceLocation Loc, llvm::Function *OutlinedFn,
1252 ArrayRef<llvm::Value *> CapturedVars, const Expr *IfCond,
1253 llvm::Value *NumThreads, OpenMPNumThreadsClauseModifier NumThreadsModifier,
1254 OpenMPSeverityClauseKind Severity, const Expr *Message) {
1255 if (!CGF.HaveInsertPoint())
1256 return;
1257
1258 auto &&ParallelGen = [this, Loc, OutlinedFn, CapturedVars, IfCond,
1259 NumThreads](CodeGenFunction &CGF,
1260 PrePostActionTy &Action) {
1261 CGBuilderTy &Bld = CGF.Builder;
1262 llvm::Value *NumThreadsVal = NumThreads;
1263 llvm::Function *WFn = WrapperFunctionsMap[OutlinedFn];
1264 llvm::PointerType *FnPtrTy = llvm::PointerType::get(
1265 CGF.getLLVMContext(), CGM.getDataLayout().getProgramAddressSpace());
1266
1267 llvm::Value *ID = llvm::ConstantPointerNull::get(FnPtrTy);
1268 if (WFn)
1269 ID = Bld.CreateBitOrPointerCast(WFn, FnPtrTy);
1270
1271 llvm::Value *FnPtr = Bld.CreateBitOrPointerCast(OutlinedFn, FnPtrTy);
1272
1273 // Create a private scope that will globalize the arguments
1274 // passed from the outside of the target region.
1275 // TODO: Is that needed?
1276 CodeGenFunction::OMPPrivateScope PrivateArgScope(CGF);
1277
1278 Address CapturedVarsAddrs = CGF.CreateDefaultAlignTempAlloca(
1279 llvm::ArrayType::get(CGM.VoidPtrTy, CapturedVars.size()),
1280 "captured_vars_addrs");
1281 // There's something to share.
1282 if (!CapturedVars.empty()) {
1283 // Prepare for parallel region. Indicate the outlined function.
1284 ASTContext &Ctx = CGF.getContext();
1285 unsigned Idx = 0;
1286 for (llvm::Value *V : CapturedVars) {
1287 Address Dst = Bld.CreateConstArrayGEP(CapturedVarsAddrs, Idx);
1288 llvm::Value *PtrV;
1289 if (V->getType()->isIntegerTy())
1290 PtrV = Bld.CreateIntToPtr(V, CGF.VoidPtrTy);
1291 else
1293 CGF.EmitStoreOfScalar(PtrV, Dst, /*Volatile=*/false,
1294 Ctx.getPointerType(Ctx.VoidPtrTy));
1295 ++Idx;
1296 }
1297 }
1298
1299 llvm::Value *IfCondVal = nullptr;
1300 if (IfCond)
1301 IfCondVal = Bld.CreateIntCast(CGF.EvaluateExprAsBool(IfCond), CGF.Int32Ty,
1302 /* isSigned */ false);
1303 else
1304 IfCondVal = llvm::ConstantInt::get(CGF.Int32Ty, 1);
1305
1306 if (!NumThreadsVal)
1307 NumThreadsVal = llvm::ConstantInt::getAllOnesValue(CGF.Int32Ty);
1308 else
1309 NumThreadsVal = Bld.CreateZExtOrTrunc(NumThreadsVal, CGF.Int32Ty);
1310
1311 // No strict prescriptiveness for the number of threads.
1312 llvm::Value *StrictNumThreadsVal = llvm::ConstantInt::get(CGF.Int32Ty, 0);
1313
1314 assert(IfCondVal && "Expected a value");
1315 llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);
1316 llvm::Value *Args[] = {
1317 RTLoc,
1318 getThreadID(CGF, Loc),
1319 IfCondVal,
1320 NumThreadsVal,
1321 llvm::ConstantInt::getAllOnesValue(CGF.Int32Ty),
1322 FnPtr,
1323 ID,
1324 Bld.CreateBitOrPointerCast(CapturedVarsAddrs.emitRawPointer(CGF),
1325 CGF.VoidPtrPtrTy),
1326 llvm::ConstantInt::get(CGM.SizeTy, CapturedVars.size()),
1327 StrictNumThreadsVal};
1328
1329 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1330 CGM.getModule(), OMPRTL___kmpc_parallel_60),
1331 Args);
1332 };
1333
1334 RegionCodeGenTy RCG(ParallelGen);
1335 RCG(CGF);
1336}
1337
1338void CGOpenMPRuntimeGPU::syncCTAThreads(CodeGenFunction &CGF) {
1339 // Always emit simple barriers!
1340 if (!CGF.HaveInsertPoint())
1341 return;
1342 // Build call __kmpc_barrier_simple_spmd(nullptr, 0);
1343 // This function does not use parameters, so we can emit just default values.
1344 llvm::Value *Args[] = {
1345 llvm::ConstantPointerNull::get(
1347 llvm::ConstantInt::get(CGF.Int32Ty, /*V=*/0, /*isSigned=*/true)};
1348 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1349 CGM.getModule(), OMPRTL___kmpc_barrier_simple_spmd),
1350 Args);
1351}
1352
1354 SourceLocation Loc,
1355 OpenMPDirectiveKind Kind, bool,
1356 bool) {
1357 // Always emit simple barriers!
1358 if (!CGF.HaveInsertPoint())
1359 return;
1360 // Build call __kmpc_cancel_barrier(loc, thread_id);
1361 unsigned Flags = getDefaultFlagsForBarriers(Kind);
1362 llvm::Value *Args[] = {emitUpdateLocation(CGF, Loc, Flags),
1363 getThreadID(CGF, Loc)};
1364
1365 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1366 CGM.getModule(), OMPRTL___kmpc_barrier),
1367 Args);
1368}
1369
1371 CodeGenFunction &CGF, StringRef CriticalName,
1372 const RegionCodeGenTy &CriticalOpGen, SourceLocation Loc,
1373 const Expr *Hint) {
1374 llvm::BasicBlock *LoopBB = CGF.createBasicBlock("omp.critical.loop");
1375 llvm::BasicBlock *TestBB = CGF.createBasicBlock("omp.critical.test");
1376 llvm::BasicBlock *SyncBB = CGF.createBasicBlock("omp.critical.sync");
1377 llvm::BasicBlock *BodyBB = CGF.createBasicBlock("omp.critical.body");
1378 llvm::BasicBlock *ExitBB = CGF.createBasicBlock("omp.critical.exit");
1379
1380 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
1381
1382 // Get the mask of active threads in the warp.
1383 llvm::Value *Mask = CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1384 CGM.getModule(), OMPRTL___kmpc_warp_active_thread_mask));
1385 // Fetch team-local id of the thread.
1386 llvm::Value *ThreadID = RT.getGPUThreadID(CGF);
1387
1388 // Get the width of the team.
1389 llvm::Value *TeamWidth = RT.getGPUNumThreads(CGF);
1390
1391 // Initialize the counter variable for the loop.
1392 QualType Int32Ty =
1393 CGF.getContext().getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/0);
1394 Address Counter = CGF.CreateMemTempWithoutCast(Int32Ty, "critical_counter");
1395 LValue CounterLVal = CGF.MakeAddrLValue(Counter, Int32Ty);
1396 CGF.EmitStoreOfScalar(llvm::Constant::getNullValue(CGM.Int32Ty), CounterLVal,
1397 /*isInit=*/true);
1398
1399 // Block checks if loop counter exceeds upper bound.
1400 CGF.EmitBlock(LoopBB);
1401 llvm::Value *CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);
1402 llvm::Value *CmpLoopBound = CGF.Builder.CreateICmpSLT(CounterVal, TeamWidth);
1403 CGF.Builder.CreateCondBr(CmpLoopBound, TestBB, ExitBB);
1404
1405 // Block tests which single thread should execute region, and which threads
1406 // should go straight to synchronisation point.
1407 CGF.EmitBlock(TestBB);
1408 CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);
1409 llvm::Value *CmpThreadToCounter =
1410 CGF.Builder.CreateICmpEQ(ThreadID, CounterVal);
1411 CGF.Builder.CreateCondBr(CmpThreadToCounter, BodyBB, SyncBB);
1412
1413 // Block emits the body of the critical region.
1414 CGF.EmitBlock(BodyBB);
1415
1416 // Output the critical statement.
1417 CGOpenMPRuntime::emitCriticalRegion(CGF, CriticalName, CriticalOpGen, Loc,
1418 Hint);
1419
1420 // After the body surrounded by the critical region, the single executing
1421 // thread will jump to the synchronisation point.
1422 // Block waits for all threads in current team to finish then increments the
1423 // counter variable and returns to the loop.
1424 CGF.EmitBlock(SyncBB);
1425 // Reconverge active threads in the warp.
1426 (void)CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1427 CGM.getModule(), OMPRTL___kmpc_syncwarp),
1428 Mask);
1429
1430 llvm::Value *IncCounterVal =
1431 CGF.Builder.CreateNSWAdd(CounterVal, CGF.Builder.getInt32(1));
1432 CGF.EmitStoreOfScalar(IncCounterVal, CounterLVal);
1433 CGF.EmitBranch(LoopBB);
1434
1435 // Block that is reached when all threads in the team complete the region.
1436 CGF.EmitBlock(ExitBB, /*IsFinished=*/true);
1437}
1438
1439/// Cast value to the specified type.
1440static llvm::Value *castValueToType(CodeGenFunction &CGF, llvm::Value *Val,
1441 QualType ValTy, QualType CastTy,
1442 SourceLocation Loc) {
1443 assert(!CGF.getContext().getTypeSizeInChars(CastTy).isZero() &&
1444 "Cast type must sized.");
1445 assert(!CGF.getContext().getTypeSizeInChars(ValTy).isZero() &&
1446 "Val type must sized.");
1447 llvm::Type *LLVMCastTy = CGF.ConvertTypeForMem(CastTy);
1448 if (ValTy == CastTy)
1449 return Val;
1450 if (CGF.getContext().getTypeSizeInChars(ValTy) ==
1451 CGF.getContext().getTypeSizeInChars(CastTy))
1452 return CGF.Builder.CreateBitCast(Val, LLVMCastTy);
1453 if (CastTy->isIntegerType() && ValTy->isIntegerType())
1454 return CGF.Builder.CreateIntCast(Val, LLVMCastTy,
1456 Address CastItem = CGF.CreateMemTempWithoutCast(CastTy);
1457 Address ValCastItem = CastItem.withElementType(Val->getType());
1458 CGF.EmitStoreOfScalar(Val, ValCastItem, /*Volatile=*/false, ValTy,
1460 TBAAAccessInfo());
1461 return CGF.EmitLoadOfScalar(CastItem, /*Volatile=*/false, CastTy, Loc,
1463 TBAAAccessInfo());
1464}
1465
1466/// Extracts the built-in reduction operator from a combiner of the form `x = x
1467/// <op> rhs` (or the min/max conditional), or nullopt if the shape is not
1468/// recognized (e.g. user-defined reductions).
1469static std::optional<BinaryOperatorKind>
1470getReductionBinOpKind(const Expr *ReductionOp) {
1471 const auto *Assign = dyn_cast<BinaryOperator>(ReductionOp);
1472 if (!Assign || Assign->getOpcode() != BO_Assign)
1473 return std::nullopt;
1474 const Expr *RHS = Assign->getRHS();
1475 // min/max are lowered as `x <cmp> rhs ? x : rhs`; the comparison identifies
1476 // it.
1477 if (const auto *ACO =
1478 dyn_cast<AbstractConditionalOperator>(RHS->IgnoreParenImpCasts()))
1479 RHS = ACO->getCond();
1480 if (const auto *BO = dyn_cast<BinaryOperator>(RHS->IgnoreParenImpCasts()))
1481 return BO->getOpcode();
1482 return std::nullopt;
1483}
1484
1485/// Maps a built-in reduction operator to an atomicrmw opcode for the atomic
1486/// cross-team reduction fast path, or nullopt if there is no direct atomicrmw
1487/// (e.g. user-defined, complex, fp min/max) so the buffer path is used instead.
1488static std::optional<llvm::AtomicRMWInst::BinOp>
1490 bool IsInt = Ty->isIntegerType();
1491 bool IsSigned = Ty->hasSignedIntegerRepresentation();
1492 switch (BOK) {
1493 case BO_Add:
1494 case BO_Sub: // A `-` reduction sums the partials, so it accumulates with add.
1495 if (IsInt)
1496 return llvm::AtomicRMWInst::Add;
1497 if (Ty->isFloatingType())
1498 return llvm::AtomicRMWInst::FAdd;
1499 return std::nullopt;
1500 case BO_And:
1501 return IsInt ? std::optional(llvm::AtomicRMWInst::And) : std::nullopt;
1502 case BO_Or:
1503 return IsInt ? std::optional(llvm::AtomicRMWInst::Or) : std::nullopt;
1504 case BO_Xor:
1505 return IsInt ? std::optional(llvm::AtomicRMWInst::Xor) : std::nullopt;
1506 case BO_LT: // min
1507 if (IsInt)
1508 return IsSigned ? llvm::AtomicRMWInst::Min : llvm::AtomicRMWInst::UMin;
1509 return std::nullopt;
1510 case BO_GT: // max
1511 if (IsInt)
1512 return IsSigned ? llvm::AtomicRMWInst::Max : llvm::AtomicRMWInst::UMax;
1513 return std::nullopt;
1514 default:
1515 return std::nullopt;
1516 }
1517}
1518
1519///
1520/// Design of OpenMP reductions on the GPU
1521///
1522/// Consider a typical OpenMP program with one or more reduction
1523/// clauses:
1524///
1525/// float foo;
1526/// double bar;
1527/// #pragma omp target teams distribute parallel for \
1528/// reduction(+:foo) reduction(*:bar)
1529/// for (int i = 0; i < N; i++) {
1530/// foo += A[i]; bar *= B[i];
1531/// }
1532///
1533/// where 'foo' and 'bar' are reduced across all OpenMP threads in
1534/// all teams. In our OpenMP implementation on the NVPTX device an
1535/// OpenMP team is mapped to a CUDA threadblock and OpenMP threads
1536/// within a team are mapped to CUDA threads within a threadblock.
1537/// Our goal is to efficiently aggregate values across all OpenMP
1538/// threads such that:
1539///
1540/// - the compiler and runtime are logically concise, and
1541/// - the reduction is performed efficiently in a hierarchical
1542/// manner as follows: within OpenMP threads in the same warp,
1543/// across warps in a threadblock, and finally across teams on
1544/// the NVPTX device.
1545///
1546/// Introduction to Decoupling
1547///
1548/// We would like to decouple the compiler and the runtime so that the
1549/// latter is ignorant of the reduction variables (number, data types)
1550/// and the reduction operators. This allows a simpler interface
1551/// and implementation while still attaining good performance.
1552///
1553/// Pseudocode for the aforementioned OpenMP program generated by the
1554/// compiler is as follows:
1555///
1556/// 1. Create private copies of reduction variables on each OpenMP
1557/// thread: 'foo_private', 'bar_private'
1558/// 2. Each OpenMP thread reduces the chunk of 'A' and 'B' assigned
1559/// to it and writes the result in 'foo_private' and 'bar_private'
1560/// respectively.
1561/// 3. Call the OpenMP runtime on the GPU to reduce within a team
1562/// and store the result on the team master:
1563///
1564/// __kmpc_nvptx_parallel_reduce_nowait_v2(...,
1565/// reduceData, shuffleReduceFn, interWarpCpyFn)
1566///
1567/// where:
1568/// struct ReduceData {
1569/// double *foo;
1570/// double *bar;
1571/// } reduceData
1572/// reduceData.foo = &foo_private
1573/// reduceData.bar = &bar_private
1574///
1575/// 'shuffleReduceFn' and 'interWarpCpyFn' are pointers to two
1576/// auxiliary functions generated by the compiler that operate on
1577/// variables of type 'ReduceData'. They aid the runtime perform
1578/// algorithmic steps in a data agnostic manner.
1579///
1580/// 'shuffleReduceFn' is a pointer to a function that reduces data
1581/// of type 'ReduceData' across two OpenMP threads (lanes) in the
1582/// same warp. It takes the following arguments as input:
1583///
1584/// a. variable of type 'ReduceData' on the calling lane,
1585/// b. its lane_id,
1586/// c. an offset relative to the current lane_id to generate a
1587/// remote_lane_id. The remote lane contains the second
1588/// variable of type 'ReduceData' that is to be reduced.
1589/// d. an algorithm version parameter determining which reduction
1590/// algorithm to use.
1591///
1592/// 'shuffleReduceFn' retrieves data from the remote lane using
1593/// efficient GPU shuffle intrinsics and reduces, using the
1594/// algorithm specified by the 4th parameter, the two operands
1595/// element-wise. The result is written to the first operand.
1596///
1597/// Different reduction algorithms are implemented in different
1598/// runtime functions, all calling 'shuffleReduceFn' to perform
1599/// the essential reduction step. Therefore, based on the 4th
1600/// parameter, this function behaves slightly differently to
1601/// cooperate with the runtime to ensure correctness under
1602/// different circumstances.
1603///
1604/// 'InterWarpCpyFn' is a pointer to a function that transfers
1605/// reduced variables across warps. It tunnels, through CUDA
1606/// shared memory, the thread-private data of type 'ReduceData'
1607/// from lane 0 of each warp to a lane in the first warp.
1608/// 4. Call the OpenMP runtime on the GPU to reduce across teams.
1609/// The last team writes the global reduced value to memory.
1610///
1611/// ret = __kmpc_nvptx_teams_reduce_nowait(...,
1612/// reduceData, shuffleReduceFn, interWarpCpyFn,
1613/// scratchpadCopyFn, loadAndReduceFn)
1614///
1615/// 'scratchpadCopyFn' is a helper that stores reduced
1616/// data from the team master to a scratchpad array in
1617/// global memory.
1618///
1619/// 'loadAndReduceFn' is a helper that loads data from
1620/// the scratchpad array and reduces it with the input
1621/// operand.
1622///
1623/// These compiler generated functions hide address
1624/// calculation and alignment information from the runtime.
1625/// 5. if ret == 1:
1626/// The team master of the last team stores the reduced
1627/// result to the globals in memory.
1628/// foo += reduceData.foo; bar *= reduceData.bar
1629///
1630///
1631/// Warp Reduction Algorithms
1632///
1633/// On the warp level, we have three algorithms implemented in the
1634/// OpenMP runtime depending on the number of active lanes:
1635///
1636/// Full Warp Reduction
1637///
1638/// The reduce algorithm within a warp where all lanes are active
1639/// is implemented in the runtime as follows:
1640///
1641/// full_warp_reduce(void *reduce_data,
1642/// kmp_ShuffleReductFctPtr ShuffleReduceFn) {
1643/// for (int offset = WARPSIZE/2; offset > 0; offset /= 2)
1644/// ShuffleReduceFn(reduce_data, 0, offset, 0);
1645/// }
1646///
1647/// The algorithm completes in log(2, WARPSIZE) steps.
1648///
1649/// 'ShuffleReduceFn' is used here with lane_id set to 0 because it is
1650/// not used therefore we save instructions by not retrieving lane_id
1651/// from the corresponding special registers. The 4th parameter, which
1652/// represents the version of the algorithm being used, is set to 0 to
1653/// signify full warp reduction.
1654///
1655/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1656///
1657/// #reduce_elem refers to an element in the local lane's data structure
1658/// #remote_elem is retrieved from a remote lane
1659/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1660/// reduce_elem = reduce_elem REDUCE_OP remote_elem;
1661///
1662/// Contiguous Partial Warp Reduction
1663///
1664/// This reduce algorithm is used within a warp where only the first
1665/// 'n' (n <= WARPSIZE) lanes are active. It is typically used when the
1666/// number of OpenMP threads in a parallel region is not a multiple of
1667/// WARPSIZE. The algorithm is implemented in the runtime as follows:
1668///
1669/// void
1670/// contiguous_partial_reduce(void *reduce_data,
1671/// kmp_ShuffleReductFctPtr ShuffleReduceFn,
1672/// int size, int lane_id) {
1673/// int curr_size;
1674/// int offset;
1675/// curr_size = size;
1676/// mask = curr_size/2;
1677/// while (offset>0) {
1678/// ShuffleReduceFn(reduce_data, lane_id, offset, 1);
1679/// curr_size = (curr_size+1)/2;
1680/// offset = curr_size/2;
1681/// }
1682/// }
1683///
1684/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1685///
1686/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1687/// if (lane_id < offset)
1688/// reduce_elem = reduce_elem REDUCE_OP remote_elem
1689/// else
1690/// reduce_elem = remote_elem
1691///
1692/// This algorithm assumes that the data to be reduced are located in a
1693/// contiguous subset of lanes starting from the first. When there is
1694/// an odd number of active lanes, the data in the last lane is not
1695/// aggregated with any other lane's dat but is instead copied over.
1696///
1697/// Dispersed Partial Warp Reduction
1698///
1699/// This algorithm is used within a warp when any discontiguous subset of
1700/// lanes are active. It is used to implement the reduction operation
1701/// across lanes in an OpenMP simd region or in a nested parallel region.
1702///
1703/// void
1704/// dispersed_partial_reduce(void *reduce_data,
1705/// kmp_ShuffleReductFctPtr ShuffleReduceFn) {
1706/// int size, remote_id;
1707/// int logical_lane_id = number_of_active_lanes_before_me() * 2;
1708/// do {
1709/// remote_id = next_active_lane_id_right_after_me();
1710/// # the above function returns 0 of no active lane
1711/// # is present right after the current lane.
1712/// size = number_of_active_lanes_in_this_warp();
1713/// logical_lane_id /= 2;
1714/// ShuffleReduceFn(reduce_data, logical_lane_id,
1715/// remote_id-1-threadIdx.x, 2);
1716/// } while (logical_lane_id % 2 == 0 && size > 1);
1717/// }
1718///
1719/// There is no assumption made about the initial state of the reduction.
1720/// Any number of lanes (>=1) could be active at any position. The reduction
1721/// result is returned in the first active lane.
1722///
1723/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1724///
1725/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1726/// if (lane_id % 2 == 0 && offset > 0)
1727/// reduce_elem = reduce_elem REDUCE_OP remote_elem
1728/// else
1729/// reduce_elem = remote_elem
1730///
1731///
1732/// Intra-Team Reduction
1733///
1734/// This function, as implemented in the runtime call
1735/// '__kmpc_nvptx_parallel_reduce_nowait_v2', aggregates data across OpenMP
1736/// threads in a team. It first reduces within a warp using the
1737/// aforementioned algorithms. We then proceed to gather all such
1738/// reduced values at the first warp.
1739///
1740/// The runtime makes use of the function 'InterWarpCpyFn', which copies
1741/// data from each of the "warp master" (zeroth lane of each warp, where
1742/// warp-reduced data is held) to the zeroth warp. This step reduces (in
1743/// a mathematical sense) the problem of reduction across warp masters in
1744/// a block to the problem of warp reduction.
1745///
1746///
1747/// Inter-Team Reduction
1748///
1749/// Once a team has reduced its data to a single value, it is stored in
1750/// a global scratchpad array. Since each team has a distinct slot, this
1751/// can be done without locking.
1752///
1753/// The last team to write to the scratchpad array proceeds to reduce the
1754/// scratchpad array. One or more workers in the last team use the helper
1755/// 'loadAndReduceDataFn' to load and reduce values from the array, i.e.,
1756/// the k'th worker reduces every k'th element.
1757///
1758/// Finally, a call is made to '__kmpc_nvptx_parallel_reduce_nowait_v2' to
1759/// reduce across workers and compute a globally reduced value.
1760///
1764 ArrayRef<const Expr *> ReductionOps, ReductionOptionsTy Options) {
1765 if (!CGF.HaveInsertPoint())
1766 return;
1767
1768 bool ParallelReduction = isOpenMPParallelDirective(Options.ReductionKind);
1769 bool TeamsReduction = isOpenMPTeamsDirective(Options.ReductionKind);
1770
1771 if (Options.SimpleReduction) {
1772 assert(!TeamsReduction && !ParallelReduction &&
1773 "Invalid reduction selection in emitReduction.");
1774 (void)ParallelReduction;
1775 CGOpenMPRuntime::emitReduction(CGF, Loc, Privates, LHSExprs, RHSExprs,
1776 ReductionOps, Options);
1777 return;
1778 }
1779
1780 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> VarFieldMap;
1781 llvm::SmallVector<const ValueDecl *, 4> PrivatesReductions(Privates.size());
1782 int Cnt = 0;
1783 for (const Expr *DRE : Privates) {
1784 PrivatesReductions[Cnt] = cast<DeclRefExpr>(DRE)->getDecl();
1785 ++Cnt;
1786 }
1787 const RecordDecl *ReductionRec = ::buildRecordForGlobalizedVars(
1788 CGM.getContext(), PrivatesReductions, {}, VarFieldMap, 1);
1789
1790 // The atomic cross-team reduction fast path is opt-in. Hand each eligible
1791 // scalar reduction an atomic combiner; createReductionsGPU uses the atomic
1792 // path only if every reduction in the set has one. Track whether that holds
1793 // so we can skip the (then unused) per-team buffer registration.
1794 bool UseAtomicReduction =
1795 TeamsReduction && CGM.getLangOpts().OpenMPTargetAtomicReduction;
1796 bool AllAtomicable = UseAtomicReduction;
1797
1798 // Source location for the ident struct
1799 llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);
1800
1801 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;
1802 InsertPointTy AllocaIP(CGF.AllocaInsertPt->getParent(),
1803 CGF.AllocaInsertPt->getIterator());
1804 InsertPointTy CodeGenIP(CGF.Builder.GetInsertBlock(),
1805 CGF.Builder.GetInsertPoint());
1806 llvm::OpenMPIRBuilder::LocationDescription OmpLoc(
1807 CodeGenIP, CGF.SourceLocToDebugLoc(Loc));
1809
1811 unsigned Idx = 0;
1812 for (const Expr *Private : Privates) {
1813 llvm::Type *ElementType;
1814 llvm::Value *Variable;
1815 llvm::Value *PrivateVariable;
1816 llvm::OpenMPIRBuilder::ReductionGenAtomicCBTy AtomicReductionGen = nullptr;
1817 ElementType = CGF.ConvertTypeForMem(Private->getType());
1818 const auto *RHSVar =
1819 cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[Idx])->getDecl());
1820 PrivateVariable = CGF.GetAddrOfLocalVar(RHSVar).emitRawPointer(CGF);
1821 const auto *LHSVar =
1822 cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[Idx])->getDecl());
1823 Variable = CGF.GetAddrOfLocalVar(LHSVar).emitRawPointer(CGF);
1824 llvm::OpenMPIRBuilder::EvalKind EvalKind;
1825 switch (CGF.getEvaluationKind(Private->getType())) {
1826 case TEK_Scalar:
1827 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Scalar;
1828 break;
1829 case TEK_Complex:
1830 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Complex;
1831 break;
1832 case TEK_Aggregate:
1833 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Aggregate;
1834 break;
1835 }
1836 auto ReductionGen = [&](InsertPointTy CodeGenIP, unsigned I,
1837 llvm::Value **LHSPtr, llvm::Value **RHSPtr,
1838 llvm::Function *NewFunc) {
1839 CGF.Builder.restoreIP(CodeGenIP);
1840 auto *CurFn = CGF.CurFn;
1841 CGF.CurFn = NewFunc;
1842
1843 // The helper has no DISubprogram of its own, so a debug location here
1844 // would name the enclosing function's scope, which is invalid IR.
1845 // Suppress them, as the other OpenMPIRBuilder-generated helpers do.
1846 llvm::DebugLoc SavedDebugLoc = CGF.Builder.getCurrentDebugLocation();
1847 CGF.Builder.SetCurrentDebugLocation(llvm::DebugLoc());
1848 CGF.disableDebugInfo();
1849
1850 *LHSPtr = CGF.GetAddrOfLocalVar(
1851 cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[I])->getDecl()))
1852 .emitRawPointer(CGF);
1853 *RHSPtr = CGF.GetAddrOfLocalVar(
1854 cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[I])->getDecl()))
1855 .emitRawPointer(CGF);
1856
1857 emitSingleReductionCombiner(CGF, ReductionOps[I], Privates[I],
1858 cast<DeclRefExpr>(LHSExprs[I]),
1859 cast<DeclRefExpr>(RHSExprs[I]));
1860
1861 CGF.enableDebugInfo();
1862 CGF.Builder.SetCurrentDebugLocation(SavedDebugLoc);
1863 CGF.CurFn = CurFn;
1864
1865 return InsertPointTy(CGF.Builder.GetInsertBlock(),
1866 CGF.Builder.GetInsertPoint());
1867 };
1868
1869 // For the atomic fast path, hand this reduction an atomic combiner if it is
1870 // a scalar with a direct atomicrmw; otherwise the set is not fully
1871 // atomicable and falls back to the buffer path.
1872 if (UseAtomicReduction) {
1873 std::optional<llvm::AtomicRMWInst::BinOp> AtomicOp;
1874 if (EvalKind == llvm::OpenMPIRBuilder::EvalKind::Scalar) {
1875 if (std::optional<BinaryOperatorKind> BOK =
1876 getReductionBinOpKind(ReductionOps[Idx]))
1877 AtomicOp = getReductionAtomicRMWOp(*BOK, Private->getType());
1878 }
1879 if (!AtomicOp) {
1880 AllAtomicable = false;
1881 } else {
1882 llvm::AtomicRMWInst::BinOp Op = *AtomicOp;
1883 llvm::Align Alignment =
1884 CGM.getModule().getDataLayout().getPrefTypeAlign(ElementType);
1885 // Device (agent) scope suffices: all teams accumulate on-device and the
1886 // host reads the result only after the kernel (via map-back), so the
1887 // far costlier system scope is unnecessary. The
1888 // no.fine.grained/no.remote memory metadata is omitted so the atomic
1889 // stays correct under USM.
1890 llvm::SyncScope::ID SSID = CGF.getTargetHooks().getLLVMSyncScopeID(
1892 llvm::AtomicOrdering::Monotonic, CGF.getLLVMContext());
1893 AtomicReductionGen = [Op, Alignment,
1894 SSID](InsertPointTy IP, llvm::Type *EltTy,
1895 llvm::Value *LHS, llvm::Value *RHS)
1896 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {
1897 llvm::IRBuilder<> Builder(IP.getBlock(), IP.getPoint());
1898 llvm::Value *Val = Builder.CreateLoad(EltTy, RHS);
1899 Builder.CreateAtomicRMW(Op, LHS, Val, Alignment,
1900 llvm::AtomicOrdering::Monotonic, SSID);
1901 return InsertPointTy(Builder.GetInsertBlock(),
1902 Builder.GetInsertPoint());
1903 };
1904 }
1905 }
1906
1907 ReductionInfos.emplace_back(llvm::OpenMPIRBuilder::ReductionInfo(
1908 ElementType, Variable, PrivateVariable, EvalKind,
1909 /*ReductionGen=*/nullptr, ReductionGen, AtomicReductionGen,
1910 /*DataPtrPtrGen=*/nullptr));
1911 Idx++;
1912 }
1913
1914 // The atomic path folds directly into the mapped variable and needs no
1915 // per-team buffer; register the record for buffer allocation otherwise.
1916 if (TeamsReduction && !AllAtomicable)
1917 TeamsReductions.push_back(ReductionRec);
1918
1919 bool IsSPMD = getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD;
1920 llvm::OpenMPIRBuilder::InsertPointTy AfterIP =
1921 cantFail(OMPBuilder.createReductionsGPU(
1922 OmpLoc, AllocaIP, CodeGenIP, ReductionInfos, /*IsByRef=*/{}, false,
1923 TeamsReduction, IsSPMD,
1924 llvm::OpenMPIRBuilder::ReductionGenCBKind::Clang,
1925 CGF.getTarget().getGridValue(), RTLoc));
1926 CGF.Builder.restoreIP(AfterIP);
1927}
1928
1929const VarDecl *
1931 const VarDecl *NativeParam) const {
1932 if (!NativeParam->getType()->isReferenceType())
1933 return NativeParam;
1934 QualType ArgType = NativeParam->getType();
1936 const Type *NonQualTy = QC.strip(ArgType);
1937 QualType PointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
1938 if (const auto *Attr = FD->getAttr<OMPCaptureKindAttr>()) {
1939 if (Attr->getCaptureKind() == OMPC_map) {
1940 PointeeTy = CGM.getContext().getAddrSpaceQualType(PointeeTy,
1942 }
1943 }
1944 ArgType = CGM.getContext().getPointerType(PointeeTy);
1945 QC.addRestrict();
1946 ArgType = QC.apply(CGM.getContext(), ArgType);
1947 if (isa<ImplicitParamDecl>(NativeParam))
1949 CGM.getContext(), /*DC=*/nullptr, NativeParam->getLocation(),
1951 return ParmVarDecl::Create(
1952 CGM.getContext(),
1953 const_cast<DeclContext *>(NativeParam->getDeclContext()),
1954 NativeParam->getBeginLoc(), NativeParam->getLocation(),
1955 NativeParam->getIdentifier(), ArgType,
1956 /*TInfo=*/nullptr, SC_None, /*DefArg=*/nullptr);
1957}
1958
1959Address
1961 const VarDecl *NativeParam,
1962 const VarDecl *TargetParam) const {
1963 assert(NativeParam != TargetParam &&
1964 NativeParam->getType()->isReferenceType() &&
1965 "Native arg must not be the same as target arg.");
1966 Address LocalAddr = CGF.GetAddrOfLocalVar(TargetParam);
1967 QualType NativeParamType = NativeParam->getType();
1969 const Type *NonQualTy = QC.strip(NativeParamType);
1970 QualType NativePointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
1971 unsigned NativePointeeAddrSpace =
1972 CGF.getTypes().getTargetAddressSpace(NativePointeeTy);
1973 QualType TargetTy = TargetParam->getType();
1974 llvm::Value *TargetAddr = CGF.EmitLoadOfScalar(LocalAddr, /*Volatile=*/false,
1975 TargetTy, SourceLocation());
1976 // Cast to native address space.
1978 TargetAddr,
1979 llvm::PointerType::get(CGF.getLLVMContext(), NativePointeeAddrSpace));
1980 Address NativeParamAddr = CGF.CreateMemTemp(NativeParamType);
1981 CGF.EmitStoreOfScalar(TargetAddr, NativeParamAddr, /*Volatile=*/false,
1982 NativeParamType);
1983 return NativeParamAddr;
1984}
1985
1987 CodeGenFunction &CGF, SourceLocation Loc, llvm::FunctionCallee OutlinedFn,
1988 ArrayRef<llvm::Value *> Args) const {
1990 TargetArgs.reserve(Args.size());
1991 auto *FnType = OutlinedFn.getFunctionType();
1992 for (unsigned I = 0, E = Args.size(); I < E; ++I) {
1993 if (FnType->isVarArg() && FnType->getNumParams() <= I) {
1994 TargetArgs.append(std::next(Args.begin(), I), Args.end());
1995 break;
1996 }
1997 llvm::Type *TargetType = FnType->getParamType(I);
1998 llvm::Value *NativeArg = Args[I];
1999 if (!TargetType->isPointerTy()) {
2000 TargetArgs.emplace_back(NativeArg);
2001 continue;
2002 }
2003 TargetArgs.emplace_back(
2004 CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(NativeArg, TargetType));
2005 }
2006 CGOpenMPRuntime::emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, TargetArgs);
2007}
2008
2009/// Emit function which wraps the outline parallel region
2010/// and controls the arguments which are passed to this function.
2011/// The wrapper ensures that the outlined function is called
2012/// with the correct arguments when data is shared.
2013llvm::Function *CGOpenMPRuntimeGPU::createParallelDataSharingWrapper(
2014 llvm::Function *OutlinedParallelFn, const OMPExecutableDirective &D) {
2015 ASTContext &Ctx = CGM.getContext();
2016 const auto &CS = *D.getCapturedStmt(OMPD_parallel);
2017
2018 // Create a function that takes as argument the source thread.
2019 FunctionArgList WrapperArgs;
2020 QualType Int16QTy =
2021 Ctx.getIntTypeForBitwidth(/*DestWidth=*/16, /*Signed=*/false);
2022 QualType Int32QTy =
2023 Ctx.getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/false);
2024 auto *ParallelLevelArg = ImplicitParamDecl::Create(
2025 Ctx, /*DC=*/nullptr, D.getBeginLoc(),
2026 /*Id=*/nullptr, Int16QTy, ImplicitParamKind::Other);
2027 auto *WrapperArg = ImplicitParamDecl::Create(
2028 Ctx, /*DC=*/nullptr, D.getBeginLoc(),
2029 /*Id=*/nullptr, Int32QTy, ImplicitParamKind::Other);
2030 WrapperArgs.emplace_back(ParallelLevelArg);
2031 WrapperArgs.emplace_back(WrapperArg);
2032
2033 const CGFunctionInfo &CGFI =
2035
2036 auto *Fn = llvm::Function::Create(
2037 CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage,
2038 Twine(OutlinedParallelFn->getName(), "_wrapper"), &CGM.getModule());
2039
2040 // Ensure we do not inline the function. This is trivially true for the ones
2041 // passed to __kmpc_fork_call but the ones calles in serialized regions
2042 // could be inlined. This is not a perfect but it is closer to the invariant
2043 // we want, namely, every data environment starts with a new function.
2044 // TODO: We should pass the if condition to the runtime function and do the
2045 // handling there. Much cleaner code.
2046 Fn->addFnAttr(llvm::Attribute::NoInline);
2047
2049 Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
2050
2051 CodeGenFunction CGF(CGM, /*suppressNewContext=*/true);
2052 CGF.StartFunction(GlobalDecl(), Ctx.VoidTy, Fn, CGFI, WrapperArgs,
2053 D.getBeginLoc(), D.getBeginLoc());
2054
2055 const auto *RD = CS.getCapturedRecordDecl();
2056 auto CurField = RD->field_begin();
2057
2058 Address ZeroAddr = CGF.CreateDefaultAlignTempAlloca(CGF.Int32Ty,
2059 /*Name=*/".zero.addr");
2060 CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);
2061 // Get the array of arguments.
2063
2064 Args.emplace_back(CGF.GetAddrOfLocalVar(WrapperArg).emitRawPointer(CGF));
2065 Args.emplace_back(ZeroAddr.emitRawPointer(CGF));
2066
2067 CGBuilderTy &Bld = CGF.Builder;
2068 auto CI = CS.capture_begin();
2069
2070 // Use global memory for data sharing.
2071 // Handle passing of global args to workers.
2072 RawAddress GlobalArgs =
2073 CGF.CreateDefaultAlignTempAlloca(CGF.VoidPtrPtrTy, "global_args");
2074 llvm::Value *GlobalArgsPtr = GlobalArgs.getPointer();
2075 llvm::Value *DataSharingArgs[] = {GlobalArgsPtr};
2076 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
2077 CGM.getModule(), OMPRTL___kmpc_get_shared_variables),
2078 DataSharingArgs);
2079
2080 // Retrieve the shared variables from the list of references returned
2081 // by the runtime. Pass the variables to the outlined function.
2082 Address SharedArgListAddress = Address::invalid();
2083 if (CS.capture_size() > 0 ||
2084 isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {
2085 SharedArgListAddress = CGF.EmitLoadOfPointer(
2086 GlobalArgs, CGF.getContext()
2088 .castAs<PointerType>());
2089 }
2090 unsigned Idx = 0;
2091 if (isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {
2092 Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);
2094 Src, Bld.getPtrTy(0), CGF.SizeTy);
2095 llvm::Value *LB = CGF.EmitLoadOfScalar(
2096 TypedAddress,
2097 /*Volatile=*/false,
2099 cast<OMPLoopDirective>(D).getLowerBoundVariable()->getExprLoc());
2100 Args.emplace_back(LB);
2101 ++Idx;
2102 Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);
2103 TypedAddress = Bld.CreatePointerBitCastOrAddrSpaceCast(Src, Bld.getPtrTy(0),
2104 CGF.SizeTy);
2105 llvm::Value *UB = CGF.EmitLoadOfScalar(
2106 TypedAddress,
2107 /*Volatile=*/false,
2109 cast<OMPLoopDirective>(D).getUpperBoundVariable()->getExprLoc());
2110 Args.emplace_back(UB);
2111 ++Idx;
2112 }
2113 if (CS.capture_size() > 0) {
2114 ASTContext &CGFContext = CGF.getContext();
2115 for (unsigned I = 0, E = CS.capture_size(); I < E; ++I, ++CI, ++CurField) {
2116 QualType ElemTy = CurField->getType();
2117 Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, I + Idx);
2119 Src, CGF.ConvertTypeForMem(CGFContext.getPointerType(ElemTy)),
2120 CGF.ConvertTypeForMem(ElemTy));
2121 llvm::Value *Arg = CGF.EmitLoadOfScalar(TypedAddress,
2122 /*Volatile=*/false,
2123 CGFContext.getPointerType(ElemTy),
2124 CI->getLocation());
2125 if (CI->capturesVariableByCopy() &&
2126 !CI->getCapturedVar()->getType()->isAnyPointerType()) {
2127 Arg = castValueToType(CGF, Arg, ElemTy, CGFContext.getUIntPtrType(),
2128 CI->getLocation());
2129 }
2130 Args.emplace_back(Arg);
2131 }
2132 }
2133
2134 emitOutlinedFunctionCall(CGF, D.getBeginLoc(), OutlinedParallelFn, Args);
2135 CGF.FinishFunction();
2136 return Fn;
2137}
2138
2140 const Decl *D) {
2141 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
2142 return;
2143
2144 assert(D && "Expected function or captured|block decl.");
2145 assert(FunctionGlobalizedDecls.count(CGF.CurFn) == 0 &&
2146 "Function is registered already.");
2147 assert((!TeamAndReductions.first || TeamAndReductions.first == D) &&
2148 "Team is set but not processed.");
2149 const Stmt *Body = nullptr;
2150 bool NeedToDelayGlobalization = false;
2151 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
2152 Body = FD->getBody();
2153 } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
2154 Body = BD->getBody();
2155 } else if (const auto *CD = dyn_cast<CapturedDecl>(D)) {
2156 Body = CD->getBody();
2157 NeedToDelayGlobalization = CGF.CapturedStmtInfo->getKind() == CR_OpenMP;
2158 if (NeedToDelayGlobalization &&
2159 getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD)
2160 return;
2161 }
2162 if (!Body)
2163 return;
2164 CheckVarsEscapingDeclContext VarChecker(CGF, TeamAndReductions.second);
2165 VarChecker.Visit(Body);
2166 const RecordDecl *GlobalizedVarsRecord =
2167 VarChecker.getGlobalizedRecord(IsInTTDRegion);
2168 TeamAndReductions.first = nullptr;
2169 TeamAndReductions.second.clear();
2170 ArrayRef<const ValueDecl *> EscapedVariableLengthDecls =
2171 VarChecker.getEscapedVariableLengthDecls();
2172 ArrayRef<const ValueDecl *> DelayedVariableLengthDecls =
2173 VarChecker.getDelayedVariableLengthDecls();
2174 if (!GlobalizedVarsRecord && EscapedVariableLengthDecls.empty() &&
2175 DelayedVariableLengthDecls.empty())
2176 return;
2177 auto I = FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;
2178 I->getSecond().MappedParams =
2179 std::make_unique<CodeGenFunction::OMPMapVars>();
2180 I->getSecond().EscapedParameters.insert(
2181 VarChecker.getEscapedParameters().begin(),
2182 VarChecker.getEscapedParameters().end());
2183 I->getSecond().EscapedVariableLengthDecls.append(
2184 EscapedVariableLengthDecls.begin(), EscapedVariableLengthDecls.end());
2185 I->getSecond().DelayedVariableLengthDecls.append(
2186 DelayedVariableLengthDecls.begin(), DelayedVariableLengthDecls.end());
2187 DeclToAddrMapTy &Data = I->getSecond().LocalVarData;
2188 for (const ValueDecl *VD : VarChecker.getEscapedDecls()) {
2189 assert(VD->isCanonicalDecl() && "Expected canonical declaration");
2190 Data.try_emplace(VD);
2191 }
2192 if (!NeedToDelayGlobalization) {
2193 emitGenericVarsProlog(CGF, D->getBeginLoc());
2194 struct GlobalizationScope final : EHScopeStack::Cleanup {
2195 GlobalizationScope() = default;
2196
2197 void Emit(CodeGenFunction &CGF, Flags flags) override {
2198 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())
2199 .emitGenericVarsEpilog(CGF);
2200 }
2201 };
2202 CGF.EHStack.pushCleanup<GlobalizationScope>(NormalAndEHCleanup);
2203 }
2204}
2205
2207 const VarDecl *VD) {
2208 if (VD && VD->hasAttr<OMPAllocateDeclAttr>()) {
2209 const auto *A = VD->getAttr<OMPAllocateDeclAttr>();
2210 auto AS = LangAS::Default;
2211 switch (A->getAllocatorType()) {
2212 case OMPAllocateDeclAttr::OMPNullMemAlloc:
2213 case OMPAllocateDeclAttr::OMPDefaultMemAlloc:
2214 case OMPAllocateDeclAttr::OMPHighBWMemAlloc:
2215 case OMPAllocateDeclAttr::OMPLowLatMemAlloc:
2216 break;
2217 case OMPAllocateDeclAttr::OMPThreadMemAlloc:
2218 return Address::invalid();
2219 case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:
2220 // TODO: implement aupport for user-defined allocators.
2221 return Address::invalid();
2222 case OMPAllocateDeclAttr::OMPConstMemAlloc:
2224 break;
2225 case OMPAllocateDeclAttr::OMPPTeamMemAlloc:
2227 break;
2228 case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:
2229 case OMPAllocateDeclAttr::OMPCGroupMemAlloc:
2230 break;
2231 }
2232 llvm::Type *VarTy = CGF.ConvertTypeForMem(VD->getType());
2233 auto *GV = new llvm::GlobalVariable(
2234 CGM.getModule(), VarTy, /*isConstant=*/false,
2235 llvm::GlobalValue::InternalLinkage, llvm::PoisonValue::get(VarTy),
2236 VD->getName(),
2237 /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal,
2238 CGM.getContext().getTargetAddressSpace(AS));
2239 CharUnits Align = CGM.getContext().getDeclAlign(VD);
2240 GV->setAlignment(Align.getAsAlign());
2241 return Address(
2243 GV, CGF.Builder.getPtrTy(CGM.getContext().getTargetAddressSpace(
2244 VD->getType().getAddressSpace()))),
2245 VarTy, Align);
2246 }
2247
2248 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
2249 return Address::invalid();
2250
2251 VD = VD->getCanonicalDecl();
2252 auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
2253 if (I == FunctionGlobalizedDecls.end())
2254 return Address::invalid();
2255 auto VDI = I->getSecond().LocalVarData.find(VD);
2256 if (VDI != I->getSecond().LocalVarData.end())
2257 return VDI->second.PrivateAddr;
2258 if (VD->hasAttrs()) {
2260 E(VD->attr_end());
2261 IT != E; ++IT) {
2262 auto VDI = I->getSecond().LocalVarData.find(
2263 cast<VarDecl>(cast<DeclRefExpr>(IT->getRef())->getDecl())
2264 ->getCanonicalDecl());
2265 if (VDI != I->getSecond().LocalVarData.end())
2266 return VDI->second.PrivateAddr;
2267 }
2268 }
2269
2270 return Address::invalid();
2271}
2272
2274 FunctionGlobalizedDecls.erase(CGF.CurFn);
2276}
2277
2279 CodeGenFunction &CGF, const OMPLoopDirective &S,
2280 OpenMPDistScheduleClauseKind &ScheduleKind,
2281 llvm::Value *&Chunk) const {
2282 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
2283 if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {
2284 ScheduleKind = OMPC_DIST_SCHEDULE_static;
2285 Chunk = CGF.EmitScalarConversion(
2286 RT.getGPUNumThreads(CGF),
2287 CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),
2288 S.getIterationVariable()->getType(), S.getBeginLoc());
2289 return;
2290 }
2292 CGF, S, ScheduleKind, Chunk);
2293}
2294
2296 CodeGenFunction &CGF, const OMPLoopDirective &S,
2297 OpenMPScheduleClauseKind &ScheduleKind,
2298 const Expr *&ChunkExpr) const {
2299 ScheduleKind = OMPC_SCHEDULE_static;
2300 // Chunk size is 1 in this case.
2301 llvm::APInt ChunkSize(32, 1);
2302 ChunkExpr = IntegerLiteral::Create(CGF.getContext(), ChunkSize,
2303 CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),
2304 SourceLocation());
2305}
2306
2308 CodeGenFunction &CGF, const OMPExecutableDirective &D) const {
2309 assert(isOpenMPTargetExecutionDirective(D.getDirectiveKind()) &&
2310 " Expected target-based directive.");
2311 const CapturedStmt *CS = D.getCapturedStmt(OMPD_target);
2312 for (const CapturedStmt::Capture &C : CS->captures()) {
2313 // Capture variables captured by reference in lambdas for target-based
2314 // directives.
2315 if (!C.capturesVariable())
2316 continue;
2317 const VarDecl *VD = C.getCapturedVar();
2318 const auto *RD = VD->getType()
2322 if (!RD || !RD->isLambda())
2323 continue;
2324 Address VDAddr = CGF.GetAddrOfLocalVar(VD);
2325 LValue VDLVal;
2327 VDLVal = CGF.EmitLoadOfReferenceLValue(VDAddr, VD->getType());
2328 else
2329 VDLVal = CGF.MakeAddrLValue(
2330 VDAddr, VD->getType().getCanonicalType().getNonReferenceType());
2331 llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;
2332 FieldDecl *ThisCapture = nullptr;
2333 RD->getCaptureFields(Captures, ThisCapture);
2334 if (ThisCapture && CGF.CapturedStmtInfo->isCXXThisExprCaptured()) {
2335 LValue ThisLVal =
2336 CGF.EmitLValueForFieldInitialization(VDLVal, ThisCapture);
2337 llvm::Value *CXXThis = CGF.LoadCXXThis();
2338 CGF.EmitStoreOfScalar(CXXThis, ThisLVal);
2339 }
2340 for (const LambdaCapture &LC : RD->captures()) {
2341 if (LC.getCaptureKind() != LCK_ByRef)
2342 continue;
2343 const ValueDecl *VD = LC.getCapturedVar();
2344 // FIXME: For now VD is always a VarDecl because OpenMP does not support
2345 // capturing structured bindings in lambdas yet.
2346 if (!CS->capturesVariable(cast<VarDecl>(VD)))
2347 continue;
2348 auto It = Captures.find(VD);
2349 assert(It != Captures.end() && "Found lambda capture without field.");
2350 LValue VarLVal = CGF.EmitLValueForFieldInitialization(VDLVal, It->second);
2351 Address VDAddr = CGF.GetAddrOfLocalVar(cast<VarDecl>(VD));
2353 VDAddr = CGF.EmitLoadOfReferenceLValue(VDAddr,
2354 VD->getType().getCanonicalType())
2355 .getAddress();
2356 CGF.EmitStoreOfScalar(VDAddr.emitRawPointer(CGF), VarLVal);
2357 }
2358 }
2359}
2360
2362 LangAS &AS) {
2363 if (!VD || !VD->hasAttr<OMPAllocateDeclAttr>())
2364 return false;
2365 const auto *A = VD->getAttr<OMPAllocateDeclAttr>();
2366 switch(A->getAllocatorType()) {
2367 case OMPAllocateDeclAttr::OMPNullMemAlloc:
2368 case OMPAllocateDeclAttr::OMPDefaultMemAlloc:
2369 // Not supported, fallback to the default mem space.
2370 case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:
2371 case OMPAllocateDeclAttr::OMPCGroupMemAlloc:
2372 case OMPAllocateDeclAttr::OMPHighBWMemAlloc:
2373 case OMPAllocateDeclAttr::OMPLowLatMemAlloc:
2374 case OMPAllocateDeclAttr::OMPThreadMemAlloc:
2375 AS = LangAS::Default;
2376 return true;
2377 case OMPAllocateDeclAttr::OMPConstMemAlloc:
2379 return true;
2380 case OMPAllocateDeclAttr::OMPPTeamMemAlloc:
2382 return true;
2383 case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:
2384 llvm_unreachable("Expected predefined allocator for the variables with the "
2385 "static storage.");
2386 }
2387 return false;
2388}
2389
2390/// Check to see if target architecture supports unified addressing which is
2391/// a restriction for OpenMP requires clause "unified_shared_memory".
2393 StringRef CPU = CGM.getTarget().getTargetOpts().CPU;
2394 if (CGM.getTarget().getTriple().isNVPTX() &&
2395 !llvm::NVPTX::supportsUnifiedAddressing(llvm::NVPTX::parseArch(CPU))) {
2396 for (const OMPClause *Clause : D->clauselists()) {
2397 if (Clause->getClauseKind() == OMPC_unified_shared_memory) {
2398 CGM.getDiags().Report(Clause->getBeginLoc(),
2399 diag::err_omp_unified_shared_memory_unsupported)
2400 << CPU;
2401 return;
2402 }
2403 }
2404 }
2405
2407}
2408
2410 CGBuilderTy &Bld = CGF.Builder;
2411 llvm::Module *M = &CGF.CGM.getModule();
2412 const char *LocSize = "__kmpc_get_hardware_num_threads_in_block";
2413 llvm::Function *F = M->getFunction(LocSize);
2414 if (!F) {
2415 F = llvm::Function::Create(llvm::FunctionType::get(CGF.Int32Ty, {}, false),
2416 llvm::GlobalVariable::ExternalLinkage, LocSize,
2417 &CGF.CGM.getModule());
2418 }
2419 return Bld.CreateCall(F, {}, "nvptx_num_threads");
2420}
2421
2424 return CGF.EmitRuntimeCall(
2425 OMPBuilder.getOrCreateRuntimeFunction(
2426 CGM.getModule(), OMPRTL___kmpc_get_hardware_thread_id_in_block),
2427 Args);
2428}
#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:4765
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:239
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:965
Attr - This represents one attribute.
Definition Attr.h:46
ArrayRef< Capture > captures() const
Definition Decl.h:4937
const BlockDecl * getBlockDecl() const
Definition Expr.h:6734
Expr * getCallee()
Definition Expr.h:3134
arg_range arguments()
Definition Expr.h:3239
Describes the capture of either a variable, or 'this', or variable-length array type.
Definition Stmt.h:3962
This captures a statement into a function.
Definition Stmt.h:3949
bool capturesVariable(const VarDecl *Var) const
True if this variable has been captured.
Definition Stmt.cpp:1517
capture_range captures()
Definition Stmt.h:4087
CastKind getCastKind() const
Definition Expr.h:3764
Expr * getSubExpr()
Definition Expr.h:3770
CharUnits - This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
bool isZero() const
isZero - Test whether the quantity equals zero.
Definition CharUnits.h:122
llvm::Align getAsAlign() const
getAsAlign - Returns Quantity as a valid llvm::Align, Beware llvm::Align assumes power of two 8-bit b...
Definition CharUnits.h:189
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
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:146
Address CreatePointerBitCastOrAddrSpaceCast(Address Addr, llvm::Type *Ty, llvm::Type *ElementTy, const llvm::Twine &Name="")
Definition CGBuilder.h:213
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:251
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:271
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:3434
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:3443
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:185
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:242
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:6124
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:234
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:674
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:198
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:654
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:2064
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:1466
void addDecl(Decl *D)
Add the declaration D into this context.
ValueDecl * getDecl()
Definition Expr.h:1358
decl_range decls()
Definition Stmt.h:1691
T * getAttr() const
Definition DeclBase.h:581
bool hasAttrs() const
Definition DeclBase.h:526
attr_iterator attr_end() const
Definition DeclBase.h:550
bool isCanonicalDecl() const
Whether this particular Decl is a canonical one.
Definition DeclBase.h:1001
attr_iterator attr_begin() const
Definition DeclBase.h:547
SourceLocation getLocation() const
Definition DeclBase.h:447
DeclContext * getDeclContext()
Definition DeclBase.h:456
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:439
AttrVec & getAttrs()
Definition DeclBase.h:532
bool hasAttr() const
Definition DeclBase.h:585
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
Definition DeclBase.h:995
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:832
This represents one expression.
Definition Expr.h:113
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3123
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3119
bool isLValue() const
isLValue - True if this expression is an "l-value" according to the rules of the current language.
Definition Expr.h:285
QualType getType() const
Definition Expr.h:145
Represents a member of a struct/union/class.
Definition Decl.h:3295
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:4765
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:5667
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:981
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:1391
capture_range captures() const
Retrieve this lambda's captures.
Definition ExprCXX.cpp:1404
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:296
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:302
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:2943
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3396
A (possibly-)qualified type.
Definition TypeBase.h:938
LangAS getAddressSpace() const
Return the address space of this type.
Definition TypeBase.h:8554
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:8613
QualType getCanonicalType() const
Definition TypeBase.h:8480
A qualifier set is used to build a set of qualifiers.
Definition TypeBase.h:8368
const Type * strip(QualType type)
Collect any qualifiers on the given type and return an unqualified type.
Definition TypeBase.h:8375
QualType apply(const ASTContext &Context, QualType QT) const
Apply the collected qualifiers to the given type.
Definition Type.cpp:4967
Represents a struct/union/class.
Definition Decl.h:4460
virtual void completeDefinition()
Note that the definition of this type is now complete.
Definition Decl.cpp:5356
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:4971
unsigned getNewAlign() const
Return the largest alignment for which a suitably-sized allocation with 'operator new(size_t)' is gua...
Definition TargetInfo.h:760
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:9081
bool isReferenceType() const
Definition TypeBase.h:8689
bool isLValueReferenceType() const
Definition TypeBase.h:8693
bool hasSignedIntegerRepresentation() const
Determine whether this type has an signed integer representation of some sort, e.g....
Definition Type.cpp:2432
bool isVariablyModifiedType() const
Whether this type is a variably-modified type (C99 6.7.5).
Definition TypeBase.h:2877
bool isFloatingType() const
Definition Type.cpp:2513
Expr * getSubExpr() const
Definition Expr.h:2329
Opcode getOpcode() const
Definition Expr.h:2324
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:713
QualType getType() const
Definition Decl.h:724
bool isInitCapture() const
Whether this variable is the implicit variable for a lambda init-capture.
Definition Decl.cpp:5652
Represents a variable declaration or definition.
Definition Decl.h:933
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition Decl.cpp:2237
bool isInitCapture() const
Whether this variable is the implicit variable for a lambda init-capture.
Definition Decl.h:1603
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:273
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:125
@ 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:251
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:1763
@ Other
Other implicit parameter.
Definition Decl.h:1775
OpenMPScheduleClauseKind
OpenMP attributes for 'schedule' clause.
Definition OpenMPKinds.h:31
unsigned long uint64_t