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::emitKernelInit(const OMPExecutableDirective &D,
751 CodeGenFunction &CGF,
752 EntryFunctionState &EST, bool IsSPMD) {
753 llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs Attrs;
754 Attrs.ExecFlags =
755 IsSPMD ? llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_SPMD
756 : llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_GENERIC;
757 computeMinAndMaxThreadsAndTeams(D, CGF, Attrs);
758
759 CGBuilderTy &Bld = CGF.Builder;
760 Bld.restoreIP(OMPBuilder.createTargetInit(Bld, Attrs));
761 if (!IsSPMD)
762 emitGenericVarsProlog(CGF, EST.Loc);
763}
764
765void CGOpenMPRuntimeGPU::emitKernelDeinit(CodeGenFunction &CGF,
766 EntryFunctionState &EST,
767 bool IsSPMD) {
768 if (!IsSPMD)
769 emitGenericVarsEpilog(CGF);
770
771 // This is temporary until we remove the fixed sized buffer.
772 ASTContext &C = CGM.getContext();
773 RecordDecl *StaticRD = C.buildImplicitRecord(
774 "_openmp_teams_reduction_type_$_", RecordDecl::TagKind::Union);
775 StaticRD->startDefinition();
776 for (const RecordDecl *TeamReductionRec : TeamsReductions) {
777 CanQualType RecTy = C.getCanonicalTagType(TeamReductionRec);
778 auto *Field = FieldDecl::Create(
779 C, StaticRD, SourceLocation(), SourceLocation(), nullptr, RecTy,
780 C.getTrivialTypeSourceInfo(RecTy, SourceLocation()),
781 /*BW=*/nullptr, /*Mutable=*/false,
782 /*InitStyle=*/ICIS_NoInit);
783 Field->setAccess(AS_public);
784 StaticRD->addDecl(Field);
785 }
786 StaticRD->completeDefinition();
787 CanQualType StaticTy = C.getCanonicalTagType(StaticRD);
788 llvm::Type *LLVMReductionsBufferTy =
789 CGM.getTypes().ConvertTypeForMem(StaticTy);
790 const auto &DL = CGM.getModule().getDataLayout();
791 uint64_t ReductionDataSize =
792 TeamsReductions.empty()
793 ? 0
794 : DL.getTypeAllocSize(LLVMReductionsBufferTy).getFixedValue();
795 CGBuilderTy &Bld = CGF.Builder;
796 OMPBuilder.createTargetDeinit(Bld, ReductionDataSize);
797 TeamsReductions.clear();
798}
799
800void CGOpenMPRuntimeGPU::emitSPMDKernel(const OMPExecutableDirective &D,
801 StringRef ParentName,
802 llvm::Function *&OutlinedFn,
803 llvm::Constant *&OutlinedFnID,
804 bool IsOffloadEntry,
805 const RegionCodeGenTy &CodeGen) {
806 ExecutionRuntimeModesRAII ModeRAII(CurrentExecutionMode, EM_SPMD);
807 EntryFunctionState EST;
808
809 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
810
811 // Emit target region as a standalone region.
812 class NVPTXPrePostActionTy : public PrePostActionTy {
813 CGOpenMPRuntimeGPU &RT;
814 CGOpenMPRuntimeGPU::EntryFunctionState &EST;
815 bool IsBareKernel;
816 DataSharingMode Mode;
817 const OMPExecutableDirective &D;
818
819 public:
820 NVPTXPrePostActionTy(CGOpenMPRuntimeGPU &RT,
821 CGOpenMPRuntimeGPU::EntryFunctionState &EST,
822 bool IsBareKernel, const OMPExecutableDirective &D)
823 : RT(RT), EST(EST), IsBareKernel(IsBareKernel),
824 Mode(RT.CurrentDataSharingMode), D(D) {}
825 void Enter(CodeGenFunction &CGF) override {
826 if (IsBareKernel) {
827 RT.CurrentDataSharingMode = DataSharingMode::DS_CUDA;
828 return;
829 }
830 RT.emitKernelInit(D, CGF, EST, /* IsSPMD */ true);
831 // Skip target region initialization.
832 RT.setLocThreadIdInsertPt(CGF, /*AtCurrentPoint=*/true);
833 }
834 void Exit(CodeGenFunction &CGF) override {
835 if (IsBareKernel) {
836 RT.CurrentDataSharingMode = Mode;
837 return;
838 }
839 RT.clearLocThreadIdInsertPt(CGF);
840 RT.emitKernelDeinit(CGF, EST, /* IsSPMD */ true);
841 }
842 } Action(*this, EST, IsBareKernel, D);
843 CodeGen.setAction(Action);
844 IsInTTDRegion = true;
845 emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,
846 IsOffloadEntry, CodeGen);
847 IsInTTDRegion = false;
848}
849
850void CGOpenMPRuntimeGPU::emitTargetOutlinedFunction(
851 const OMPExecutableDirective &D, StringRef ParentName,
852 llvm::Function *&OutlinedFn, llvm::Constant *&OutlinedFnID,
853 bool IsOffloadEntry, const RegionCodeGenTy &CodeGen) {
854 if (!IsOffloadEntry) // Nothing to do.
855 return;
856
857 assert(!ParentName.empty() && "Invalid target region parent name!");
858
859 bool Mode = supportsSPMDExecutionMode(CGM.getContext(), D);
860 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
861 if (Mode || IsBareKernel)
862 emitSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,
863 CodeGen);
864 else
865 emitNonSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,
866 CodeGen);
867}
868
871 llvm::OpenMPIRBuilderConfig Config(
872 CGM.getLangOpts().OpenMPIsTargetDevice, isGPU(),
873 CGM.getLangOpts().OpenMPOffloadMandatory,
874 /*HasRequiresReverseOffload*/ false, /*HasRequiresUnifiedAddress*/ false,
875 hasRequiresUnifiedSharedMemory(), /*HasRequiresDynamicAllocators*/ false);
876 Config.setDefaultTargetAS(
877 CGM.getContext().getTargetInfo().getTargetAddressSpace(LangAS::Default));
878 Config.setRuntimeCC(CGM.getRuntimeCC());
879
880 OMPBuilder.setConfig(Config);
881
882 if (!CGM.getLangOpts().OpenMPIsTargetDevice)
883 llvm_unreachable("OpenMP can only handle device code.");
884
885 if (CGM.getLangOpts().OpenMPCUDAMode)
886 CurrentDataSharingMode = CGOpenMPRuntimeGPU::DS_CUDA;
887
888 llvm::OpenMPIRBuilder &OMPBuilder = getOMPBuilder();
889 if (CGM.getLangOpts().NoGPULib || CGM.getLangOpts().OMPHostIRFile.empty())
890 return;
891
892 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTargetDebug,
893 "__omp_rtl_debug_kind");
894 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTeamSubscription,
895 "__omp_rtl_assume_teams_oversubscription");
896 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPThreadSubscription,
897 "__omp_rtl_assume_threads_oversubscription");
898 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoThreadState,
899 "__omp_rtl_assume_no_thread_state");
900 OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoNestedParallelism,
901 "__omp_rtl_assume_no_nested_parallelism");
902}
903
905 ProcBindKind ProcBind,
906 SourceLocation Loc) {
907 // Nothing to do.
908}
909
911 const Expr *Message,
912 SourceLocation Loc) {
913 CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)
914 << getOpenMPClauseName(OMPC_message);
915 return nullptr;
916}
917
918llvm::Value *
920 SourceLocation Loc) {
921 CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)
922 << getOpenMPClauseName(OMPC_severity);
923 return nullptr;
924}
925
927 CodeGenFunction &CGF, llvm::Value *NumThreads, SourceLocation Loc,
929 SourceLocation SeverityLoc, const Expr *Message,
930 SourceLocation MessageLoc) {
931 if (Modifier == OMPC_NUMTHREADS_strict) {
932 CGM.getDiags().Report(Loc,
933 diag::warn_omp_gpu_unsupported_modifier_for_clause)
934 << "strict" << getOpenMPClauseName(OMPC_num_threads);
935 return;
936 }
937
938 // Nothing to do.
939}
940
942 const Expr *NumTeams,
943 const Expr *ThreadLimit,
944 SourceLocation Loc) {}
945
948 const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,
949 const RegionCodeGenTy &CodeGen) {
950 // Emit target region as a standalone region.
951 bool PrevIsInTTDRegion = IsInTTDRegion;
952 IsInTTDRegion = false;
953 auto *OutlinedFun =
955 CGF, D, ThreadIDVar, InnermostKind, CodeGen));
956 IsInTTDRegion = PrevIsInTTDRegion;
957 if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD) {
958 llvm::Function *WrapperFun =
959 createParallelDataSharingWrapper(OutlinedFun, D);
960 WrapperFunctionsMap[OutlinedFun] = WrapperFun;
961 }
962
963 return OutlinedFun;
964}
965
966/// Get list of lastprivate variables from the teams distribute ... or
967/// teams {distribute ...} directives.
968static void
971 assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&
972 "expected teams directive.");
973 const OMPExecutableDirective *Dir = &D;
974 if (!isOpenMPDistributeDirective(D.getDirectiveKind())) {
976 Ctx,
977 D.getInnermostCapturedStmt()->getCapturedStmt()->IgnoreContainers(
978 /*IgnoreCaptured=*/true))) {
979 Dir = dyn_cast_or_null<OMPExecutableDirective>(S);
980 if (Dir && !isOpenMPDistributeDirective(Dir->getDirectiveKind()))
981 Dir = nullptr;
982 }
983 }
984 if (!Dir)
985 return;
986 for (const auto *C : Dir->getClausesOfKind<OMPLastprivateClause>()) {
987 for (const Expr *E : C->getVarRefs())
988 Vars.push_back(getPrivateItem(E));
989 }
990}
991
992/// Get list of reduction variables from the teams ... directives.
993static void
996 assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&
997 "expected teams directive.");
998 for (const auto *C : D.getClausesOfKind<OMPReductionClause>()) {
999 for (const Expr *E : C->privates())
1000 Vars.push_back(getPrivateItem(E));
1001 }
1002}
1003
1006 const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,
1007 const RegionCodeGenTy &CodeGen) {
1008 SourceLocation Loc = D.getBeginLoc();
1009
1010 const RecordDecl *GlobalizedRD = nullptr;
1011 llvm::SmallVector<const ValueDecl *, 4> LastPrivatesReductions;
1012 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> MappedDeclsFields;
1013 unsigned WarpSize = CGM.getTarget().getGridValue().GV_Warp_Size;
1014 // Globalize team reductions variable unconditionally in all modes.
1015 if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD)
1016 getTeamsReductionVars(CGM.getContext(), D, LastPrivatesReductions);
1017 if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {
1018 getDistributeLastprivateVars(CGM.getContext(), D, LastPrivatesReductions);
1019 if (!LastPrivatesReductions.empty()) {
1020 GlobalizedRD = ::buildRecordForGlobalizedVars(
1021 CGM.getContext(), {}, LastPrivatesReductions, MappedDeclsFields,
1022 WarpSize);
1023 }
1024 } else if (!LastPrivatesReductions.empty()) {
1025 assert(!TeamAndReductions.first &&
1026 "Previous team declaration is not expected.");
1027 TeamAndReductions.first = D.getCapturedStmt(OMPD_teams)->getCapturedDecl();
1028 std::swap(TeamAndReductions.second, LastPrivatesReductions);
1029 }
1030
1031 // Emit target region as a standalone region.
1032 class NVPTXPrePostActionTy : public PrePostActionTy {
1033 SourceLocation &Loc;
1034 const RecordDecl *GlobalizedRD;
1035 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>
1036 &MappedDeclsFields;
1037
1038 public:
1039 NVPTXPrePostActionTy(
1040 SourceLocation &Loc, const RecordDecl *GlobalizedRD,
1041 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>
1042 &MappedDeclsFields)
1043 : Loc(Loc), GlobalizedRD(GlobalizedRD),
1044 MappedDeclsFields(MappedDeclsFields) {}
1045 void Enter(CodeGenFunction &CGF) override {
1046 auto &Rt =
1047 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
1048 if (GlobalizedRD) {
1049 auto I = Rt.FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;
1050 I->getSecond().MappedParams =
1051 std::make_unique<CodeGenFunction::OMPMapVars>();
1052 DeclToAddrMapTy &Data = I->getSecond().LocalVarData;
1053 for (const auto &Pair : MappedDeclsFields) {
1054 assert(Pair.getFirst()->isCanonicalDecl() &&
1055 "Expected canonical declaration");
1056 Data.try_emplace(Pair.getFirst());
1057 }
1058 }
1059 Rt.emitGenericVarsProlog(CGF, Loc);
1060 }
1061 void Exit(CodeGenFunction &CGF) override {
1062 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())
1063 .emitGenericVarsEpilog(CGF);
1064 }
1065 } Action(Loc, GlobalizedRD, MappedDeclsFields);
1066 CodeGen.setAction(Action);
1067 llvm::Function *OutlinedFun = CGOpenMPRuntime::emitTeamsOutlinedFunction(
1068 CGF, D, ThreadIDVar, InnermostKind, CodeGen);
1069
1070 return OutlinedFun;
1071}
1072
1073void CGOpenMPRuntimeGPU::emitGenericVarsProlog(CodeGenFunction &CGF,
1074 SourceLocation Loc) {
1075 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
1076 return;
1077
1078 CGBuilderTy &Bld = CGF.Builder;
1079
1080 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1081 if (I == FunctionGlobalizedDecls.end())
1082 return;
1083
1084 for (auto &Rec : I->getSecond().LocalVarData) {
1085 const auto *VD = cast<VarDecl>(Rec.first);
1086 bool EscapedParam = I->getSecond().EscapedParameters.count(Rec.first);
1087 QualType VarTy = VD->getType();
1088
1089 // Get the local allocation of a firstprivate variable before sharing
1090 llvm::Value *ParValue;
1091 if (EscapedParam) {
1092 LValue ParLVal =
1093 CGF.MakeAddrLValue(CGF.GetAddrOfLocalVar(VD), VD->getType());
1094 ParValue = CGF.EmitLoadOfScalar(ParLVal, Loc);
1095 }
1096
1097 // Allocate space for the variable to be globalized
1098 llvm::Value *AllocArgs[] = {CGF.getTypeSize(VD->getType())};
1099 llvm::CallBase *VoidPtr =
1100 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1101 CGM.getModule(), OMPRTL___kmpc_alloc_shared),
1102 AllocArgs, VD->getName());
1103 // FIXME: We should use the variables actual alignment as an argument.
1104 VoidPtr->addRetAttr(llvm::Attribute::get(
1105 CGM.getLLVMContext(), llvm::Attribute::Alignment,
1107
1108 // Cast the void pointer and get the address of the globalized variable.
1109 llvm::Value *CastedVoidPtr = Bld.CreatePointerBitCastOrAddrSpaceCast(
1110 VoidPtr, Bld.getPtrTy(0), VD->getName() + "_on_stack");
1111 LValue VarAddr =
1112 CGF.MakeNaturalAlignPointeeRawAddrLValue(CastedVoidPtr, VarTy);
1113 Rec.second.PrivateAddr = VarAddr.getAddress();
1114 Rec.second.GlobalizedVal = VoidPtr;
1115
1116 // Assign the local allocation to the newly globalized location.
1117 if (EscapedParam) {
1118 CGF.EmitStoreOfScalar(ParValue, VarAddr);
1119 I->getSecond().MappedParams->setVarAddr(CGF, VD, VarAddr.getAddress());
1120 }
1121 if (auto *DI = CGF.getDebugInfo())
1122 VoidPtr->setDebugLoc(DI->SourceLocToDebugLoc(VD->getLocation()));
1123 }
1124
1125 for (const auto *ValueD : I->getSecond().EscapedVariableLengthDecls) {
1126 const auto *VD = cast<VarDecl>(ValueD);
1127 std::pair<llvm::Value *, llvm::Value *> AddrSizePair =
1128 getKmpcAllocShared(CGF, VD);
1129 I->getSecond().EscapedVariableLengthDeclsAddrs.emplace_back(AddrSizePair);
1130 LValue Base = CGF.MakeAddrLValue(AddrSizePair.first, VD->getType(),
1131 CGM.getContext().getDeclAlign(VD),
1133 I->getSecond().MappedParams->setVarAddr(CGF, VD, Base.getAddress());
1134 }
1135 I->getSecond().MappedParams->apply(CGF);
1136}
1137
1139 const VarDecl *VD) const {
1140 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1141 if (I == FunctionGlobalizedDecls.end())
1142 return false;
1143
1144 // Check variable declaration is delayed:
1145 return llvm::is_contained(I->getSecond().DelayedVariableLengthDecls, VD);
1146}
1147
1148std::pair<llvm::Value *, llvm::Value *>
1150 const VarDecl *VD) {
1151 CGBuilderTy &Bld = CGF.Builder;
1152
1153 // Compute size and alignment.
1154 llvm::Value *Size = CGF.getTypeSize(VD->getType());
1155 CharUnits Align = CGM.getContext().getDeclAlign(VD);
1156 Size = Bld.CreateNUWAdd(
1157 Size, llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity() - 1));
1158 llvm::Value *AlignVal =
1159 llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity());
1160 Size = Bld.CreateUDiv(Size, AlignVal);
1161 Size = Bld.CreateNUWMul(Size, AlignVal);
1162
1163 // Allocate space for this VLA object to be globalized.
1164 llvm::Value *AllocArgs[] = {Size};
1165 llvm::CallBase *VoidPtr =
1166 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1167 CGM.getModule(), OMPRTL___kmpc_alloc_shared),
1168 AllocArgs, VD->getName());
1169 VoidPtr->addRetAttr(llvm::Attribute::get(
1170 CGM.getLLVMContext(), llvm::Attribute::Alignment, Align.getQuantity()));
1171
1172 return std::make_pair(VoidPtr, Size);
1173}
1174
1176 CodeGenFunction &CGF,
1177 const std::pair<llvm::Value *, llvm::Value *> &AddrSizePair) {
1178 // Deallocate the memory for each globalized VLA object
1179 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1180 CGM.getModule(), OMPRTL___kmpc_free_shared),
1181 {AddrSizePair.first, AddrSizePair.second});
1182}
1183
1184void CGOpenMPRuntimeGPU::emitGenericVarsEpilog(CodeGenFunction &CGF) {
1185 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
1186 return;
1187
1188 const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
1189 if (I != FunctionGlobalizedDecls.end()) {
1190 // Deallocate the memory for each globalized VLA object that was
1191 // globalized in the prolog (i.e. emitGenericVarsProlog).
1192 for (const auto &AddrSizePair :
1193 llvm::reverse(I->getSecond().EscapedVariableLengthDeclsAddrs)) {
1194 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1195 CGM.getModule(), OMPRTL___kmpc_free_shared),
1196 {AddrSizePair.first, AddrSizePair.second});
1197 }
1198 // Deallocate the memory for each globalized value
1199 for (auto &Rec : llvm::reverse(I->getSecond().LocalVarData)) {
1200 const auto *VD = cast<VarDecl>(Rec.first);
1201 I->getSecond().MappedParams->restore(CGF);
1202
1203 llvm::Value *FreeArgs[] = {Rec.second.GlobalizedVal,
1204 CGF.getTypeSize(VD->getType())};
1205 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1206 CGM.getModule(), OMPRTL___kmpc_free_shared),
1207 FreeArgs);
1208 }
1209 }
1210}
1211
1213 const OMPExecutableDirective &D,
1214 SourceLocation Loc,
1215 llvm::Function *OutlinedFn,
1216 ArrayRef<llvm::Value *> CapturedVars) {
1217 if (!CGF.HaveInsertPoint())
1218 return;
1219
1220 bool IsBareKernel = D.getSingleClause<OMPXBareClause>();
1221
1223 /*Name=*/".zero.addr");
1224 CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);
1226 // We don't emit any thread id function call in bare kernel, but because the
1227 // outlined function has a pointer argument, we emit a nullptr here.
1228 if (IsBareKernel)
1229 OutlinedFnArgs.push_back(llvm::ConstantPointerNull::get(CGM.VoidPtrTy));
1230 else
1231 OutlinedFnArgs.push_back(emitThreadIDAddress(CGF, Loc).emitRawPointer(CGF));
1232 OutlinedFnArgs.push_back(ZeroAddr.getPointer());
1233 OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
1234 emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, OutlinedFnArgs);
1235}
1236
1238 CodeGenFunction &CGF, SourceLocation Loc, llvm::Function *OutlinedFn,
1239 ArrayRef<llvm::Value *> CapturedVars, const Expr *IfCond,
1240 llvm::Value *NumThreads, OpenMPNumThreadsClauseModifier NumThreadsModifier,
1241 OpenMPSeverityClauseKind Severity, const Expr *Message) {
1242 if (!CGF.HaveInsertPoint())
1243 return;
1244
1245 auto &&ParallelGen = [this, Loc, OutlinedFn, CapturedVars, IfCond,
1246 NumThreads](CodeGenFunction &CGF,
1247 PrePostActionTy &Action) {
1248 CGBuilderTy &Bld = CGF.Builder;
1249 llvm::Value *NumThreadsVal = NumThreads;
1250 llvm::Function *WFn = WrapperFunctionsMap[OutlinedFn];
1251 llvm::PointerType *FnPtrTy = llvm::PointerType::get(
1252 CGF.getLLVMContext(), CGM.getDataLayout().getProgramAddressSpace());
1253
1254 llvm::Value *ID = llvm::ConstantPointerNull::get(FnPtrTy);
1255 if (WFn)
1256 ID = Bld.CreateBitOrPointerCast(WFn, FnPtrTy);
1257
1258 llvm::Value *FnPtr = Bld.CreateBitOrPointerCast(OutlinedFn, FnPtrTy);
1259
1260 // Create a private scope that will globalize the arguments
1261 // passed from the outside of the target region.
1262 // TODO: Is that needed?
1263 CodeGenFunction::OMPPrivateScope PrivateArgScope(CGF);
1264
1265 Address CapturedVarsAddrs = CGF.CreateDefaultAlignTempAlloca(
1266 llvm::ArrayType::get(CGM.VoidPtrTy, CapturedVars.size()),
1267 "captured_vars_addrs");
1268 // There's something to share.
1269 if (!CapturedVars.empty()) {
1270 // Prepare for parallel region. Indicate the outlined function.
1271 ASTContext &Ctx = CGF.getContext();
1272 unsigned Idx = 0;
1273 for (llvm::Value *V : CapturedVars) {
1274 Address Dst = Bld.CreateConstArrayGEP(CapturedVarsAddrs, Idx);
1275 llvm::Value *PtrV;
1276 if (V->getType()->isIntegerTy())
1277 PtrV = Bld.CreateIntToPtr(V, CGF.VoidPtrTy);
1278 else
1280 CGF.EmitStoreOfScalar(PtrV, Dst, /*Volatile=*/false,
1281 Ctx.getPointerType(Ctx.VoidPtrTy));
1282 ++Idx;
1283 }
1284 }
1285
1286 llvm::Value *IfCondVal = nullptr;
1287 if (IfCond)
1288 IfCondVal = Bld.CreateIntCast(CGF.EvaluateExprAsBool(IfCond), CGF.Int32Ty,
1289 /* isSigned */ false);
1290 else
1291 IfCondVal = llvm::ConstantInt::get(CGF.Int32Ty, 1);
1292
1293 if (!NumThreadsVal)
1294 NumThreadsVal = llvm::ConstantInt::getAllOnesValue(CGF.Int32Ty);
1295 else
1296 NumThreadsVal = Bld.CreateZExtOrTrunc(NumThreadsVal, CGF.Int32Ty);
1297
1298 // No strict prescriptiveness for the number of threads.
1299 llvm::Value *StrictNumThreadsVal = llvm::ConstantInt::get(CGF.Int32Ty, 0);
1300
1301 assert(IfCondVal && "Expected a value");
1302 llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);
1303 llvm::Value *Args[] = {
1304 RTLoc,
1305 getThreadID(CGF, Loc),
1306 IfCondVal,
1307 NumThreadsVal,
1308 llvm::ConstantInt::getAllOnesValue(CGF.Int32Ty),
1309 FnPtr,
1310 ID,
1311 Bld.CreateBitOrPointerCast(CapturedVarsAddrs.emitRawPointer(CGF),
1312 CGF.VoidPtrPtrTy),
1313 llvm::ConstantInt::get(CGM.SizeTy, CapturedVars.size()),
1314 StrictNumThreadsVal};
1315
1316 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1317 CGM.getModule(), OMPRTL___kmpc_parallel_60),
1318 Args);
1319 };
1320
1321 RegionCodeGenTy RCG(ParallelGen);
1322 RCG(CGF);
1323}
1324
1325void CGOpenMPRuntimeGPU::syncCTAThreads(CodeGenFunction &CGF) {
1326 // Always emit simple barriers!
1327 if (!CGF.HaveInsertPoint())
1328 return;
1329 // Build call __kmpc_barrier_simple_spmd(nullptr, 0);
1330 // This function does not use parameters, so we can emit just default values.
1331 llvm::Value *Args[] = {
1332 llvm::ConstantPointerNull::get(
1334 llvm::ConstantInt::get(CGF.Int32Ty, /*V=*/0, /*isSigned=*/true)};
1335 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1336 CGM.getModule(), OMPRTL___kmpc_barrier_simple_spmd),
1337 Args);
1338}
1339
1341 SourceLocation Loc,
1342 OpenMPDirectiveKind Kind, bool,
1343 bool) {
1344 // Always emit simple barriers!
1345 if (!CGF.HaveInsertPoint())
1346 return;
1347 // Build call __kmpc_cancel_barrier(loc, thread_id);
1348 unsigned Flags = getDefaultFlagsForBarriers(Kind);
1349 llvm::Value *Args[] = {emitUpdateLocation(CGF, Loc, Flags),
1350 getThreadID(CGF, Loc)};
1351
1352 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1353 CGM.getModule(), OMPRTL___kmpc_barrier),
1354 Args);
1355}
1356
1358 CodeGenFunction &CGF, StringRef CriticalName,
1359 const RegionCodeGenTy &CriticalOpGen, SourceLocation Loc,
1360 const Expr *Hint) {
1361 llvm::BasicBlock *LoopBB = CGF.createBasicBlock("omp.critical.loop");
1362 llvm::BasicBlock *TestBB = CGF.createBasicBlock("omp.critical.test");
1363 llvm::BasicBlock *SyncBB = CGF.createBasicBlock("omp.critical.sync");
1364 llvm::BasicBlock *BodyBB = CGF.createBasicBlock("omp.critical.body");
1365 llvm::BasicBlock *ExitBB = CGF.createBasicBlock("omp.critical.exit");
1366
1367 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
1368
1369 // Get the mask of active threads in the warp.
1370 llvm::Value *Mask = CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1371 CGM.getModule(), OMPRTL___kmpc_warp_active_thread_mask));
1372 // Fetch team-local id of the thread.
1373 llvm::Value *ThreadID = RT.getGPUThreadID(CGF);
1374
1375 // Get the width of the team.
1376 llvm::Value *TeamWidth = RT.getGPUNumThreads(CGF);
1377
1378 // Initialize the counter variable for the loop.
1379 QualType Int32Ty =
1380 CGF.getContext().getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/0);
1381 Address Counter = CGF.CreateMemTempWithoutCast(Int32Ty, "critical_counter");
1382 LValue CounterLVal = CGF.MakeAddrLValue(Counter, Int32Ty);
1383 CGF.EmitStoreOfScalar(llvm::Constant::getNullValue(CGM.Int32Ty), CounterLVal,
1384 /*isInit=*/true);
1385
1386 // Block checks if loop counter exceeds upper bound.
1387 CGF.EmitBlock(LoopBB);
1388 llvm::Value *CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);
1389 llvm::Value *CmpLoopBound = CGF.Builder.CreateICmpSLT(CounterVal, TeamWidth);
1390 CGF.Builder.CreateCondBr(CmpLoopBound, TestBB, ExitBB);
1391
1392 // Block tests which single thread should execute region, and which threads
1393 // should go straight to synchronisation point.
1394 CGF.EmitBlock(TestBB);
1395 CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);
1396 llvm::Value *CmpThreadToCounter =
1397 CGF.Builder.CreateICmpEQ(ThreadID, CounterVal);
1398 CGF.Builder.CreateCondBr(CmpThreadToCounter, BodyBB, SyncBB);
1399
1400 // Block emits the body of the critical region.
1401 CGF.EmitBlock(BodyBB);
1402
1403 // Output the critical statement.
1404 CGOpenMPRuntime::emitCriticalRegion(CGF, CriticalName, CriticalOpGen, Loc,
1405 Hint);
1406
1407 // After the body surrounded by the critical region, the single executing
1408 // thread will jump to the synchronisation point.
1409 // Block waits for all threads in current team to finish then increments the
1410 // counter variable and returns to the loop.
1411 CGF.EmitBlock(SyncBB);
1412 // Reconverge active threads in the warp.
1413 (void)CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
1414 CGM.getModule(), OMPRTL___kmpc_syncwarp),
1415 Mask);
1416
1417 llvm::Value *IncCounterVal =
1418 CGF.Builder.CreateNSWAdd(CounterVal, CGF.Builder.getInt32(1));
1419 CGF.EmitStoreOfScalar(IncCounterVal, CounterLVal);
1420 CGF.EmitBranch(LoopBB);
1421
1422 // Block that is reached when all threads in the team complete the region.
1423 CGF.EmitBlock(ExitBB, /*IsFinished=*/true);
1424}
1425
1426/// Cast value to the specified type.
1427static llvm::Value *castValueToType(CodeGenFunction &CGF, llvm::Value *Val,
1428 QualType ValTy, QualType CastTy,
1429 SourceLocation Loc) {
1430 assert(!CGF.getContext().getTypeSizeInChars(CastTy).isZero() &&
1431 "Cast type must sized.");
1432 assert(!CGF.getContext().getTypeSizeInChars(ValTy).isZero() &&
1433 "Val type must sized.");
1434 llvm::Type *LLVMCastTy = CGF.ConvertTypeForMem(CastTy);
1435 if (ValTy == CastTy)
1436 return Val;
1437 if (CGF.getContext().getTypeSizeInChars(ValTy) ==
1438 CGF.getContext().getTypeSizeInChars(CastTy))
1439 return CGF.Builder.CreateBitCast(Val, LLVMCastTy);
1440 if (CastTy->isIntegerType() && ValTy->isIntegerType())
1441 return CGF.Builder.CreateIntCast(Val, LLVMCastTy,
1443 Address CastItem = CGF.CreateMemTempWithoutCast(CastTy);
1444 Address ValCastItem = CastItem.withElementType(Val->getType());
1445 CGF.EmitStoreOfScalar(Val, ValCastItem, /*Volatile=*/false, ValTy,
1447 TBAAAccessInfo());
1448 return CGF.EmitLoadOfScalar(CastItem, /*Volatile=*/false, CastTy, Loc,
1450 TBAAAccessInfo());
1451}
1452
1453/// Extracts the built-in reduction operator from a combiner of the form `x = x
1454/// <op> rhs` (or the min/max conditional), or nullopt if the shape is not
1455/// recognized (e.g. user-defined reductions).
1456static std::optional<BinaryOperatorKind>
1457getReductionBinOpKind(const Expr *ReductionOp) {
1458 const auto *Assign = dyn_cast<BinaryOperator>(ReductionOp);
1459 if (!Assign || Assign->getOpcode() != BO_Assign)
1460 return std::nullopt;
1461 const Expr *RHS = Assign->getRHS();
1462 // min/max are lowered as `x <cmp> rhs ? x : rhs`; the comparison identifies
1463 // it.
1464 if (const auto *ACO =
1465 dyn_cast<AbstractConditionalOperator>(RHS->IgnoreParenImpCasts()))
1466 RHS = ACO->getCond();
1467 if (const auto *BO = dyn_cast<BinaryOperator>(RHS->IgnoreParenImpCasts()))
1468 return BO->getOpcode();
1469 return std::nullopt;
1470}
1471
1472/// Maps a built-in reduction operator to an atomicrmw opcode for the atomic
1473/// cross-team reduction fast path, or nullopt if there is no direct atomicrmw
1474/// (e.g. user-defined, complex, fp min/max) so the buffer path is used instead.
1475static std::optional<llvm::AtomicRMWInst::BinOp>
1477 bool IsInt = Ty->isIntegerType();
1478 bool IsSigned = Ty->hasSignedIntegerRepresentation();
1479 switch (BOK) {
1480 case BO_Add:
1481 case BO_Sub: // A `-` reduction sums the partials, so it accumulates with add.
1482 if (IsInt)
1483 return llvm::AtomicRMWInst::Add;
1484 if (Ty->isFloatingType())
1485 return llvm::AtomicRMWInst::FAdd;
1486 return std::nullopt;
1487 case BO_And:
1488 return IsInt ? std::optional(llvm::AtomicRMWInst::And) : std::nullopt;
1489 case BO_Or:
1490 return IsInt ? std::optional(llvm::AtomicRMWInst::Or) : std::nullopt;
1491 case BO_Xor:
1492 return IsInt ? std::optional(llvm::AtomicRMWInst::Xor) : std::nullopt;
1493 case BO_LT: // min
1494 if (IsInt)
1495 return IsSigned ? llvm::AtomicRMWInst::Min : llvm::AtomicRMWInst::UMin;
1496 return std::nullopt;
1497 case BO_GT: // max
1498 if (IsInt)
1499 return IsSigned ? llvm::AtomicRMWInst::Max : llvm::AtomicRMWInst::UMax;
1500 return std::nullopt;
1501 default:
1502 return std::nullopt;
1503 }
1504}
1505
1506///
1507/// Design of OpenMP reductions on the GPU
1508///
1509/// Consider a typical OpenMP program with one or more reduction
1510/// clauses:
1511///
1512/// float foo;
1513/// double bar;
1514/// #pragma omp target teams distribute parallel for \
1515/// reduction(+:foo) reduction(*:bar)
1516/// for (int i = 0; i < N; i++) {
1517/// foo += A[i]; bar *= B[i];
1518/// }
1519///
1520/// where 'foo' and 'bar' are reduced across all OpenMP threads in
1521/// all teams. In our OpenMP implementation on the NVPTX device an
1522/// OpenMP team is mapped to a CUDA threadblock and OpenMP threads
1523/// within a team are mapped to CUDA threads within a threadblock.
1524/// Our goal is to efficiently aggregate values across all OpenMP
1525/// threads such that:
1526///
1527/// - the compiler and runtime are logically concise, and
1528/// - the reduction is performed efficiently in a hierarchical
1529/// manner as follows: within OpenMP threads in the same warp,
1530/// across warps in a threadblock, and finally across teams on
1531/// the NVPTX device.
1532///
1533/// Introduction to Decoupling
1534///
1535/// We would like to decouple the compiler and the runtime so that the
1536/// latter is ignorant of the reduction variables (number, data types)
1537/// and the reduction operators. This allows a simpler interface
1538/// and implementation while still attaining good performance.
1539///
1540/// Pseudocode for the aforementioned OpenMP program generated by the
1541/// compiler is as follows:
1542///
1543/// 1. Create private copies of reduction variables on each OpenMP
1544/// thread: 'foo_private', 'bar_private'
1545/// 2. Each OpenMP thread reduces the chunk of 'A' and 'B' assigned
1546/// to it and writes the result in 'foo_private' and 'bar_private'
1547/// respectively.
1548/// 3. Call the OpenMP runtime on the GPU to reduce within a team
1549/// and store the result on the team master:
1550///
1551/// __kmpc_nvptx_parallel_reduce_nowait_v2(...,
1552/// reduceData, shuffleReduceFn, interWarpCpyFn)
1553///
1554/// where:
1555/// struct ReduceData {
1556/// double *foo;
1557/// double *bar;
1558/// } reduceData
1559/// reduceData.foo = &foo_private
1560/// reduceData.bar = &bar_private
1561///
1562/// 'shuffleReduceFn' and 'interWarpCpyFn' are pointers to two
1563/// auxiliary functions generated by the compiler that operate on
1564/// variables of type 'ReduceData'. They aid the runtime perform
1565/// algorithmic steps in a data agnostic manner.
1566///
1567/// 'shuffleReduceFn' is a pointer to a function that reduces data
1568/// of type 'ReduceData' across two OpenMP threads (lanes) in the
1569/// same warp. It takes the following arguments as input:
1570///
1571/// a. variable of type 'ReduceData' on the calling lane,
1572/// b. its lane_id,
1573/// c. an offset relative to the current lane_id to generate a
1574/// remote_lane_id. The remote lane contains the second
1575/// variable of type 'ReduceData' that is to be reduced.
1576/// d. an algorithm version parameter determining which reduction
1577/// algorithm to use.
1578///
1579/// 'shuffleReduceFn' retrieves data from the remote lane using
1580/// efficient GPU shuffle intrinsics and reduces, using the
1581/// algorithm specified by the 4th parameter, the two operands
1582/// element-wise. The result is written to the first operand.
1583///
1584/// Different reduction algorithms are implemented in different
1585/// runtime functions, all calling 'shuffleReduceFn' to perform
1586/// the essential reduction step. Therefore, based on the 4th
1587/// parameter, this function behaves slightly differently to
1588/// cooperate with the runtime to ensure correctness under
1589/// different circumstances.
1590///
1591/// 'InterWarpCpyFn' is a pointer to a function that transfers
1592/// reduced variables across warps. It tunnels, through CUDA
1593/// shared memory, the thread-private data of type 'ReduceData'
1594/// from lane 0 of each warp to a lane in the first warp.
1595/// 4. Call the OpenMP runtime on the GPU to reduce across teams.
1596/// The last team writes the global reduced value to memory.
1597///
1598/// ret = __kmpc_nvptx_teams_reduce_nowait(...,
1599/// reduceData, shuffleReduceFn, interWarpCpyFn,
1600/// scratchpadCopyFn, loadAndReduceFn)
1601///
1602/// 'scratchpadCopyFn' is a helper that stores reduced
1603/// data from the team master to a scratchpad array in
1604/// global memory.
1605///
1606/// 'loadAndReduceFn' is a helper that loads data from
1607/// the scratchpad array and reduces it with the input
1608/// operand.
1609///
1610/// These compiler generated functions hide address
1611/// calculation and alignment information from the runtime.
1612/// 5. if ret == 1:
1613/// The team master of the last team stores the reduced
1614/// result to the globals in memory.
1615/// foo += reduceData.foo; bar *= reduceData.bar
1616///
1617///
1618/// Warp Reduction Algorithms
1619///
1620/// On the warp level, we have three algorithms implemented in the
1621/// OpenMP runtime depending on the number of active lanes:
1622///
1623/// Full Warp Reduction
1624///
1625/// The reduce algorithm within a warp where all lanes are active
1626/// is implemented in the runtime as follows:
1627///
1628/// full_warp_reduce(void *reduce_data,
1629/// kmp_ShuffleReductFctPtr ShuffleReduceFn) {
1630/// for (int offset = WARPSIZE/2; offset > 0; offset /= 2)
1631/// ShuffleReduceFn(reduce_data, 0, offset, 0);
1632/// }
1633///
1634/// The algorithm completes in log(2, WARPSIZE) steps.
1635///
1636/// 'ShuffleReduceFn' is used here with lane_id set to 0 because it is
1637/// not used therefore we save instructions by not retrieving lane_id
1638/// from the corresponding special registers. The 4th parameter, which
1639/// represents the version of the algorithm being used, is set to 0 to
1640/// signify full warp reduction.
1641///
1642/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1643///
1644/// #reduce_elem refers to an element in the local lane's data structure
1645/// #remote_elem is retrieved from a remote lane
1646/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1647/// reduce_elem = reduce_elem REDUCE_OP remote_elem;
1648///
1649/// Contiguous Partial Warp Reduction
1650///
1651/// This reduce algorithm is used within a warp where only the first
1652/// 'n' (n <= WARPSIZE) lanes are active. It is typically used when the
1653/// number of OpenMP threads in a parallel region is not a multiple of
1654/// WARPSIZE. The algorithm is implemented in the runtime as follows:
1655///
1656/// void
1657/// contiguous_partial_reduce(void *reduce_data,
1658/// kmp_ShuffleReductFctPtr ShuffleReduceFn,
1659/// int size, int lane_id) {
1660/// int curr_size;
1661/// int offset;
1662/// curr_size = size;
1663/// mask = curr_size/2;
1664/// while (offset>0) {
1665/// ShuffleReduceFn(reduce_data, lane_id, offset, 1);
1666/// curr_size = (curr_size+1)/2;
1667/// offset = curr_size/2;
1668/// }
1669/// }
1670///
1671/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1672///
1673/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1674/// if (lane_id < offset)
1675/// reduce_elem = reduce_elem REDUCE_OP remote_elem
1676/// else
1677/// reduce_elem = remote_elem
1678///
1679/// This algorithm assumes that the data to be reduced are located in a
1680/// contiguous subset of lanes starting from the first. When there is
1681/// an odd number of active lanes, the data in the last lane is not
1682/// aggregated with any other lane's dat but is instead copied over.
1683///
1684/// Dispersed Partial Warp Reduction
1685///
1686/// This algorithm is used within a warp when any discontiguous subset of
1687/// lanes are active. It is used to implement the reduction operation
1688/// across lanes in an OpenMP simd region or in a nested parallel region.
1689///
1690/// void
1691/// dispersed_partial_reduce(void *reduce_data,
1692/// kmp_ShuffleReductFctPtr ShuffleReduceFn) {
1693/// int size, remote_id;
1694/// int logical_lane_id = number_of_active_lanes_before_me() * 2;
1695/// do {
1696/// remote_id = next_active_lane_id_right_after_me();
1697/// # the above function returns 0 of no active lane
1698/// # is present right after the current lane.
1699/// size = number_of_active_lanes_in_this_warp();
1700/// logical_lane_id /= 2;
1701/// ShuffleReduceFn(reduce_data, logical_lane_id,
1702/// remote_id-1-threadIdx.x, 2);
1703/// } while (logical_lane_id % 2 == 0 && size > 1);
1704/// }
1705///
1706/// There is no assumption made about the initial state of the reduction.
1707/// Any number of lanes (>=1) could be active at any position. The reduction
1708/// result is returned in the first active lane.
1709///
1710/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:
1711///
1712/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);
1713/// if (lane_id % 2 == 0 && offset > 0)
1714/// reduce_elem = reduce_elem REDUCE_OP remote_elem
1715/// else
1716/// reduce_elem = remote_elem
1717///
1718///
1719/// Intra-Team Reduction
1720///
1721/// This function, as implemented in the runtime call
1722/// '__kmpc_nvptx_parallel_reduce_nowait_v2', aggregates data across OpenMP
1723/// threads in a team. It first reduces within a warp using the
1724/// aforementioned algorithms. We then proceed to gather all such
1725/// reduced values at the first warp.
1726///
1727/// The runtime makes use of the function 'InterWarpCpyFn', which copies
1728/// data from each of the "warp master" (zeroth lane of each warp, where
1729/// warp-reduced data is held) to the zeroth warp. This step reduces (in
1730/// a mathematical sense) the problem of reduction across warp masters in
1731/// a block to the problem of warp reduction.
1732///
1733///
1734/// Inter-Team Reduction
1735///
1736/// Once a team has reduced its data to a single value, it is stored in
1737/// a global scratchpad array. Since each team has a distinct slot, this
1738/// can be done without locking.
1739///
1740/// The last team to write to the scratchpad array proceeds to reduce the
1741/// scratchpad array. One or more workers in the last team use the helper
1742/// 'loadAndReduceDataFn' to load and reduce values from the array, i.e.,
1743/// the k'th worker reduces every k'th element.
1744///
1745/// Finally, a call is made to '__kmpc_nvptx_parallel_reduce_nowait_v2' to
1746/// reduce across workers and compute a globally reduced value.
1747///
1751 ArrayRef<const Expr *> ReductionOps, ReductionOptionsTy Options) {
1752 if (!CGF.HaveInsertPoint())
1753 return;
1754
1755 bool ParallelReduction = isOpenMPParallelDirective(Options.ReductionKind);
1756 bool TeamsReduction = isOpenMPTeamsDirective(Options.ReductionKind);
1757
1758 if (Options.SimpleReduction) {
1759 assert(!TeamsReduction && !ParallelReduction &&
1760 "Invalid reduction selection in emitReduction.");
1761 (void)ParallelReduction;
1762 CGOpenMPRuntime::emitReduction(CGF, Loc, Privates, LHSExprs, RHSExprs,
1763 ReductionOps, Options);
1764 return;
1765 }
1766
1767 llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> VarFieldMap;
1768 llvm::SmallVector<const ValueDecl *, 4> PrivatesReductions(Privates.size());
1769 int Cnt = 0;
1770 for (const Expr *DRE : Privates) {
1771 PrivatesReductions[Cnt] = cast<DeclRefExpr>(DRE)->getDecl();
1772 ++Cnt;
1773 }
1774 const RecordDecl *ReductionRec = ::buildRecordForGlobalizedVars(
1775 CGM.getContext(), PrivatesReductions, {}, VarFieldMap, 1);
1776
1777 // The atomic cross-team reduction fast path is opt-in. Hand each eligible
1778 // scalar reduction an atomic combiner; createReductionsGPU uses the atomic
1779 // path only if every reduction in the set has one. Track whether that holds
1780 // so we can skip the (then unused) per-team buffer registration.
1781 bool UseAtomicReduction =
1782 TeamsReduction && CGM.getLangOpts().OpenMPTargetAtomicReduction;
1783 bool AllAtomicable = UseAtomicReduction;
1784
1785 // Source location for the ident struct
1786 llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);
1787
1788 using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;
1789 InsertPointTy AllocaIP(CGF.AllocaInsertPt->getParent(),
1790 CGF.AllocaInsertPt->getIterator());
1791 InsertPointTy CodeGenIP(CGF.Builder.GetInsertBlock(),
1792 CGF.Builder.GetInsertPoint());
1793 llvm::OpenMPIRBuilder::LocationDescription OmpLoc(
1794 CodeGenIP, CGF.SourceLocToDebugLoc(Loc));
1796
1798 unsigned Idx = 0;
1799 for (const Expr *Private : Privates) {
1800 llvm::Type *ElementType;
1801 llvm::Value *Variable;
1802 llvm::Value *PrivateVariable;
1803 llvm::OpenMPIRBuilder::ReductionGenAtomicCBTy AtomicReductionGen = nullptr;
1804 ElementType = CGF.ConvertTypeForMem(Private->getType());
1805 const auto *RHSVar =
1806 cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[Idx])->getDecl());
1807 PrivateVariable = CGF.GetAddrOfLocalVar(RHSVar).emitRawPointer(CGF);
1808 const auto *LHSVar =
1809 cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[Idx])->getDecl());
1810 Variable = CGF.GetAddrOfLocalVar(LHSVar).emitRawPointer(CGF);
1811 llvm::OpenMPIRBuilder::EvalKind EvalKind;
1812 switch (CGF.getEvaluationKind(Private->getType())) {
1813 case TEK_Scalar:
1814 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Scalar;
1815 break;
1816 case TEK_Complex:
1817 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Complex;
1818 break;
1819 case TEK_Aggregate:
1820 EvalKind = llvm::OpenMPIRBuilder::EvalKind::Aggregate;
1821 break;
1822 }
1823 auto ReductionGen = [&](InsertPointTy CodeGenIP, unsigned I,
1824 llvm::Value **LHSPtr, llvm::Value **RHSPtr,
1825 llvm::Function *NewFunc) {
1826 CGF.Builder.restoreIP(CodeGenIP);
1827 auto *CurFn = CGF.CurFn;
1828 CGF.CurFn = NewFunc;
1829
1830 // The helper has no DISubprogram of its own, so a debug location here
1831 // would name the enclosing function's scope, which is invalid IR.
1832 // Suppress them, as the other OpenMPIRBuilder-generated helpers do.
1833 llvm::DebugLoc SavedDebugLoc = CGF.Builder.getCurrentDebugLocation();
1834 CGF.Builder.SetCurrentDebugLocation(llvm::DebugLoc());
1835 CGF.disableDebugInfo();
1836
1837 *LHSPtr = CGF.GetAddrOfLocalVar(
1838 cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[I])->getDecl()))
1839 .emitRawPointer(CGF);
1840 *RHSPtr = CGF.GetAddrOfLocalVar(
1841 cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[I])->getDecl()))
1842 .emitRawPointer(CGF);
1843
1844 emitSingleReductionCombiner(CGF, ReductionOps[I], Privates[I],
1845 cast<DeclRefExpr>(LHSExprs[I]),
1846 cast<DeclRefExpr>(RHSExprs[I]));
1847
1848 CGF.enableDebugInfo();
1849 CGF.Builder.SetCurrentDebugLocation(SavedDebugLoc);
1850 CGF.CurFn = CurFn;
1851
1852 return InsertPointTy(CGF.Builder.GetInsertBlock(),
1853 CGF.Builder.GetInsertPoint());
1854 };
1855
1856 // For the atomic fast path, hand this reduction an atomic combiner if it is
1857 // a scalar with a direct atomicrmw; otherwise the set is not fully
1858 // atomicable and falls back to the buffer path.
1859 if (UseAtomicReduction) {
1860 std::optional<llvm::AtomicRMWInst::BinOp> AtomicOp;
1861 if (EvalKind == llvm::OpenMPIRBuilder::EvalKind::Scalar) {
1862 if (std::optional<BinaryOperatorKind> BOK =
1863 getReductionBinOpKind(ReductionOps[Idx]))
1864 AtomicOp = getReductionAtomicRMWOp(*BOK, Private->getType());
1865 }
1866 if (!AtomicOp) {
1867 AllAtomicable = false;
1868 } else {
1869 llvm::AtomicRMWInst::BinOp Op = *AtomicOp;
1870 llvm::Align Alignment =
1871 CGM.getModule().getDataLayout().getPrefTypeAlign(ElementType);
1872 // Device (agent) scope suffices: all teams accumulate on-device and the
1873 // host reads the result only after the kernel (via map-back), so the
1874 // far costlier system scope is unnecessary. The
1875 // no.fine.grained/no.remote memory metadata is omitted so the atomic
1876 // stays correct under USM.
1877 llvm::SyncScope::ID SSID = CGF.getTargetHooks().getLLVMSyncScopeID(
1879 llvm::AtomicOrdering::Monotonic, CGF.getLLVMContext());
1880 AtomicReductionGen = [Op, Alignment,
1881 SSID](InsertPointTy IP, llvm::Type *EltTy,
1882 llvm::Value *LHS, llvm::Value *RHS)
1883 -> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {
1884 llvm::IRBuilder<> Builder(IP.getBlock(), IP.getPoint());
1885 llvm::Value *Val = Builder.CreateLoad(EltTy, RHS);
1886 Builder.CreateAtomicRMW(Op, LHS, Val, Alignment,
1887 llvm::AtomicOrdering::Monotonic, SSID);
1888 return InsertPointTy(Builder.GetInsertBlock(),
1889 Builder.GetInsertPoint());
1890 };
1891 }
1892 }
1893
1894 ReductionInfos.emplace_back(llvm::OpenMPIRBuilder::ReductionInfo(
1895 ElementType, Variable, PrivateVariable, EvalKind,
1896 /*ReductionGen=*/nullptr, ReductionGen, AtomicReductionGen,
1897 /*DataPtrPtrGen=*/nullptr));
1898 Idx++;
1899 }
1900
1901 // The atomic path folds directly into the mapped variable and needs no
1902 // per-team buffer; register the record for buffer allocation otherwise.
1903 if (TeamsReduction && !AllAtomicable)
1904 TeamsReductions.push_back(ReductionRec);
1905
1906 bool IsSPMD = getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD;
1907 llvm::OpenMPIRBuilder::InsertPointTy AfterIP =
1908 cantFail(OMPBuilder.createReductionsGPU(
1909 OmpLoc, AllocaIP, CodeGenIP, ReductionInfos, /*IsByRef=*/{}, false,
1910 TeamsReduction, IsSPMD,
1911 llvm::OpenMPIRBuilder::ReductionGenCBKind::Clang,
1912 CGF.getTarget().getGridValue(), RTLoc));
1913 CGF.Builder.restoreIP(AfterIP);
1914}
1915
1916const VarDecl *
1918 const VarDecl *NativeParam) const {
1919 if (!NativeParam->getType()->isReferenceType())
1920 return NativeParam;
1921 QualType ArgType = NativeParam->getType();
1923 const Type *NonQualTy = QC.strip(ArgType);
1924 QualType PointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
1925 if (const auto *Attr = FD->getAttr<OMPCaptureKindAttr>()) {
1926 if (Attr->getCaptureKind() == OMPC_map) {
1927 PointeeTy = CGM.getContext().getAddrSpaceQualType(PointeeTy,
1929 }
1930 }
1931 ArgType = CGM.getContext().getPointerType(PointeeTy);
1932 QC.addRestrict();
1933 ArgType = QC.apply(CGM.getContext(), ArgType);
1934 if (isa<ImplicitParamDecl>(NativeParam))
1936 CGM.getContext(), /*DC=*/nullptr, NativeParam->getLocation(),
1938 return ParmVarDecl::Create(
1939 CGM.getContext(),
1940 const_cast<DeclContext *>(NativeParam->getDeclContext()),
1941 NativeParam->getBeginLoc(), NativeParam->getLocation(),
1942 NativeParam->getIdentifier(), ArgType,
1943 /*TInfo=*/nullptr, SC_None, /*DefArg=*/nullptr);
1944}
1945
1946Address
1948 const VarDecl *NativeParam,
1949 const VarDecl *TargetParam) const {
1950 assert(NativeParam != TargetParam &&
1951 NativeParam->getType()->isReferenceType() &&
1952 "Native arg must not be the same as target arg.");
1953 Address LocalAddr = CGF.GetAddrOfLocalVar(TargetParam);
1954 QualType NativeParamType = NativeParam->getType();
1956 const Type *NonQualTy = QC.strip(NativeParamType);
1957 QualType NativePointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
1958 unsigned NativePointeeAddrSpace =
1959 CGF.getTypes().getTargetAddressSpace(NativePointeeTy);
1960 QualType TargetTy = TargetParam->getType();
1961 llvm::Value *TargetAddr = CGF.EmitLoadOfScalar(LocalAddr, /*Volatile=*/false,
1962 TargetTy, SourceLocation());
1963 // Cast to native address space.
1965 TargetAddr,
1966 llvm::PointerType::get(CGF.getLLVMContext(), NativePointeeAddrSpace));
1967 Address NativeParamAddr = CGF.CreateMemTemp(NativeParamType);
1968 CGF.EmitStoreOfScalar(TargetAddr, NativeParamAddr, /*Volatile=*/false,
1969 NativeParamType);
1970 return NativeParamAddr;
1971}
1972
1974 CodeGenFunction &CGF, SourceLocation Loc, llvm::FunctionCallee OutlinedFn,
1975 ArrayRef<llvm::Value *> Args) const {
1977 TargetArgs.reserve(Args.size());
1978 auto *FnType = OutlinedFn.getFunctionType();
1979 for (unsigned I = 0, E = Args.size(); I < E; ++I) {
1980 if (FnType->isVarArg() && FnType->getNumParams() <= I) {
1981 TargetArgs.append(std::next(Args.begin(), I), Args.end());
1982 break;
1983 }
1984 llvm::Type *TargetType = FnType->getParamType(I);
1985 llvm::Value *NativeArg = Args[I];
1986 if (!TargetType->isPointerTy()) {
1987 TargetArgs.emplace_back(NativeArg);
1988 continue;
1989 }
1990 TargetArgs.emplace_back(
1991 CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(NativeArg, TargetType));
1992 }
1993 CGOpenMPRuntime::emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, TargetArgs);
1994}
1995
1996/// Emit function which wraps the outline parallel region
1997/// and controls the arguments which are passed to this function.
1998/// The wrapper ensures that the outlined function is called
1999/// with the correct arguments when data is shared.
2000llvm::Function *CGOpenMPRuntimeGPU::createParallelDataSharingWrapper(
2001 llvm::Function *OutlinedParallelFn, const OMPExecutableDirective &D) {
2002 ASTContext &Ctx = CGM.getContext();
2003 const auto &CS = *D.getCapturedStmt(OMPD_parallel);
2004
2005 // Create a function that takes as argument the source thread.
2006 FunctionArgList WrapperArgs;
2007 QualType Int16QTy =
2008 Ctx.getIntTypeForBitwidth(/*DestWidth=*/16, /*Signed=*/false);
2009 QualType Int32QTy =
2010 Ctx.getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/false);
2011 auto *ParallelLevelArg = ImplicitParamDecl::Create(
2012 Ctx, /*DC=*/nullptr, D.getBeginLoc(),
2013 /*Id=*/nullptr, Int16QTy, ImplicitParamKind::Other);
2014 auto *WrapperArg = ImplicitParamDecl::Create(
2015 Ctx, /*DC=*/nullptr, D.getBeginLoc(),
2016 /*Id=*/nullptr, Int32QTy, ImplicitParamKind::Other);
2017 WrapperArgs.emplace_back(ParallelLevelArg);
2018 WrapperArgs.emplace_back(WrapperArg);
2019
2020 const CGFunctionInfo &CGFI =
2022
2023 auto *Fn = llvm::Function::Create(
2024 CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage,
2025 Twine(OutlinedParallelFn->getName(), "_wrapper"), &CGM.getModule());
2026
2027 // Ensure we do not inline the function. This is trivially true for the ones
2028 // passed to __kmpc_fork_call but the ones calles in serialized regions
2029 // could be inlined. This is not a perfect but it is closer to the invariant
2030 // we want, namely, every data environment starts with a new function.
2031 // TODO: We should pass the if condition to the runtime function and do the
2032 // handling there. Much cleaner code.
2033 Fn->addFnAttr(llvm::Attribute::NoInline);
2034
2036 Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
2037 Fn->setDoesNotRecurse();
2038
2039 CodeGenFunction CGF(CGM, /*suppressNewContext=*/true);
2040 CGF.StartFunction(GlobalDecl(), Ctx.VoidTy, Fn, CGFI, WrapperArgs,
2041 D.getBeginLoc(), D.getBeginLoc());
2042
2043 const auto *RD = CS.getCapturedRecordDecl();
2044 auto CurField = RD->field_begin();
2045
2046 Address ZeroAddr = CGF.CreateDefaultAlignTempAlloca(CGF.Int32Ty,
2047 /*Name=*/".zero.addr");
2048 CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);
2049 // Get the array of arguments.
2051
2052 Args.emplace_back(CGF.GetAddrOfLocalVar(WrapperArg).emitRawPointer(CGF));
2053 Args.emplace_back(ZeroAddr.emitRawPointer(CGF));
2054
2055 CGBuilderTy &Bld = CGF.Builder;
2056 auto CI = CS.capture_begin();
2057
2058 // Use global memory for data sharing.
2059 // Handle passing of global args to workers.
2060 RawAddress GlobalArgs =
2061 CGF.CreateDefaultAlignTempAlloca(CGF.VoidPtrPtrTy, "global_args");
2062 llvm::Value *GlobalArgsPtr = GlobalArgs.getPointer();
2063 llvm::Value *DataSharingArgs[] = {GlobalArgsPtr};
2064 CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(
2065 CGM.getModule(), OMPRTL___kmpc_get_shared_variables),
2066 DataSharingArgs);
2067
2068 // Retrieve the shared variables from the list of references returned
2069 // by the runtime. Pass the variables to the outlined function.
2070 Address SharedArgListAddress = Address::invalid();
2071 if (CS.capture_size() > 0 ||
2072 isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {
2073 SharedArgListAddress = CGF.EmitLoadOfPointer(
2074 GlobalArgs, CGF.getContext()
2076 .castAs<PointerType>());
2077 }
2078 unsigned Idx = 0;
2079 if (isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {
2080 Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);
2082 Src, Bld.getPtrTy(0), CGF.SizeTy);
2083 llvm::Value *LB = CGF.EmitLoadOfScalar(
2084 TypedAddress,
2085 /*Volatile=*/false,
2087 cast<OMPLoopDirective>(D).getLowerBoundVariable()->getExprLoc());
2088 Args.emplace_back(LB);
2089 ++Idx;
2090 Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);
2091 TypedAddress = Bld.CreatePointerBitCastOrAddrSpaceCast(Src, Bld.getPtrTy(0),
2092 CGF.SizeTy);
2093 llvm::Value *UB = CGF.EmitLoadOfScalar(
2094 TypedAddress,
2095 /*Volatile=*/false,
2097 cast<OMPLoopDirective>(D).getUpperBoundVariable()->getExprLoc());
2098 Args.emplace_back(UB);
2099 ++Idx;
2100 }
2101 if (CS.capture_size() > 0) {
2102 ASTContext &CGFContext = CGF.getContext();
2103 for (unsigned I = 0, E = CS.capture_size(); I < E; ++I, ++CI, ++CurField) {
2104 QualType ElemTy = CurField->getType();
2105 Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, I + Idx);
2107 Src, CGF.ConvertTypeForMem(CGFContext.getPointerType(ElemTy)),
2108 CGF.ConvertTypeForMem(ElemTy));
2109 llvm::Value *Arg = CGF.EmitLoadOfScalar(TypedAddress,
2110 /*Volatile=*/false,
2111 CGFContext.getPointerType(ElemTy),
2112 CI->getLocation());
2113 if (CI->capturesVariableByCopy() &&
2114 !CI->getCapturedVar()->getType()->isAnyPointerType()) {
2115 Arg = castValueToType(CGF, Arg, ElemTy, CGFContext.getUIntPtrType(),
2116 CI->getLocation());
2117 }
2118 Args.emplace_back(Arg);
2119 }
2120 }
2121
2122 emitOutlinedFunctionCall(CGF, D.getBeginLoc(), OutlinedParallelFn, Args);
2123 CGF.FinishFunction();
2124 return Fn;
2125}
2126
2128 const Decl *D) {
2129 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
2130 return;
2131
2132 assert(D && "Expected function or captured|block decl.");
2133 assert(FunctionGlobalizedDecls.count(CGF.CurFn) == 0 &&
2134 "Function is registered already.");
2135 assert((!TeamAndReductions.first || TeamAndReductions.first == D) &&
2136 "Team is set but not processed.");
2137 const Stmt *Body = nullptr;
2138 bool NeedToDelayGlobalization = false;
2139 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
2140 Body = FD->getBody();
2141 } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
2142 Body = BD->getBody();
2143 } else if (const auto *CD = dyn_cast<CapturedDecl>(D)) {
2144 Body = CD->getBody();
2145 NeedToDelayGlobalization = CGF.CapturedStmtInfo->getKind() == CR_OpenMP;
2146 if (NeedToDelayGlobalization &&
2147 getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD)
2148 return;
2149 }
2150 if (!Body)
2151 return;
2152 CheckVarsEscapingDeclContext VarChecker(CGF, TeamAndReductions.second);
2153 VarChecker.Visit(Body);
2154 const RecordDecl *GlobalizedVarsRecord =
2155 VarChecker.getGlobalizedRecord(IsInTTDRegion);
2156 TeamAndReductions.first = nullptr;
2157 TeamAndReductions.second.clear();
2158 ArrayRef<const ValueDecl *> EscapedVariableLengthDecls =
2159 VarChecker.getEscapedVariableLengthDecls();
2160 ArrayRef<const ValueDecl *> DelayedVariableLengthDecls =
2161 VarChecker.getDelayedVariableLengthDecls();
2162 if (!GlobalizedVarsRecord && EscapedVariableLengthDecls.empty() &&
2163 DelayedVariableLengthDecls.empty())
2164 return;
2165 auto I = FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;
2166 I->getSecond().MappedParams =
2167 std::make_unique<CodeGenFunction::OMPMapVars>();
2168 I->getSecond().EscapedParameters.insert(
2169 VarChecker.getEscapedParameters().begin(),
2170 VarChecker.getEscapedParameters().end());
2171 I->getSecond().EscapedVariableLengthDecls.append(
2172 EscapedVariableLengthDecls.begin(), EscapedVariableLengthDecls.end());
2173 I->getSecond().DelayedVariableLengthDecls.append(
2174 DelayedVariableLengthDecls.begin(), DelayedVariableLengthDecls.end());
2175 DeclToAddrMapTy &Data = I->getSecond().LocalVarData;
2176 for (const ValueDecl *VD : VarChecker.getEscapedDecls()) {
2177 assert(VD->isCanonicalDecl() && "Expected canonical declaration");
2178 Data.try_emplace(VD);
2179 }
2180 if (!NeedToDelayGlobalization) {
2181 emitGenericVarsProlog(CGF, D->getBeginLoc());
2182 struct GlobalizationScope final : EHScopeStack::Cleanup {
2183 GlobalizationScope() = default;
2184
2185 void Emit(CodeGenFunction &CGF, Flags flags) override {
2186 static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())
2187 .emitGenericVarsEpilog(CGF);
2188 }
2189 };
2190 CGF.EHStack.pushCleanup<GlobalizationScope>(NormalAndEHCleanup);
2191 }
2192}
2193
2195 const VarDecl *VD) {
2196 if (VD && VD->hasAttr<OMPAllocateDeclAttr>()) {
2197 const auto *A = VD->getAttr<OMPAllocateDeclAttr>();
2198 auto AS = LangAS::Default;
2199 switch (A->getAllocatorType()) {
2200 case OMPAllocateDeclAttr::OMPNullMemAlloc:
2201 case OMPAllocateDeclAttr::OMPDefaultMemAlloc:
2202 case OMPAllocateDeclAttr::OMPHighBWMemAlloc:
2203 case OMPAllocateDeclAttr::OMPLowLatMemAlloc:
2204 break;
2205 case OMPAllocateDeclAttr::OMPThreadMemAlloc:
2206 return Address::invalid();
2207 case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:
2208 // TODO: implement aupport for user-defined allocators.
2209 return Address::invalid();
2210 case OMPAllocateDeclAttr::OMPConstMemAlloc:
2212 break;
2213 case OMPAllocateDeclAttr::OMPPTeamMemAlloc:
2215 break;
2216 case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:
2217 case OMPAllocateDeclAttr::OMPCGroupMemAlloc:
2218 break;
2219 }
2220 llvm::Type *VarTy = CGF.ConvertTypeForMem(VD->getType());
2221 auto *GV = new llvm::GlobalVariable(
2222 CGM.getModule(), VarTy, /*isConstant=*/false,
2223 llvm::GlobalValue::InternalLinkage, llvm::PoisonValue::get(VarTy),
2224 VD->getName(),
2225 /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal,
2226 CGM.getContext().getTargetAddressSpace(AS));
2227 CharUnits Align = CGM.getContext().getDeclAlign(VD);
2228 GV->setAlignment(Align.getAsAlign());
2229 return Address(
2231 GV, CGF.Builder.getPtrTy(CGM.getContext().getTargetAddressSpace(
2232 VD->getType().getAddressSpace()))),
2233 VarTy, Align);
2234 }
2235
2236 if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)
2237 return Address::invalid();
2238
2239 VD = VD->getCanonicalDecl();
2240 auto I = FunctionGlobalizedDecls.find(CGF.CurFn);
2241 if (I == FunctionGlobalizedDecls.end())
2242 return Address::invalid();
2243 auto VDI = I->getSecond().LocalVarData.find(VD);
2244 if (VDI != I->getSecond().LocalVarData.end())
2245 return VDI->second.PrivateAddr;
2246 if (VD->hasAttrs()) {
2248 E(VD->attr_end());
2249 IT != E; ++IT) {
2250 auto VDI = I->getSecond().LocalVarData.find(
2251 cast<VarDecl>(cast<DeclRefExpr>(IT->getRef())->getDecl())
2252 ->getCanonicalDecl());
2253 if (VDI != I->getSecond().LocalVarData.end())
2254 return VDI->second.PrivateAddr;
2255 }
2256 }
2257
2258 return Address::invalid();
2259}
2260
2262 FunctionGlobalizedDecls.erase(CGF.CurFn);
2264}
2265
2267 CodeGenFunction &CGF, const OMPLoopDirective &S,
2268 OpenMPDistScheduleClauseKind &ScheduleKind,
2269 llvm::Value *&Chunk) const {
2270 auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());
2271 if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {
2272 ScheduleKind = OMPC_DIST_SCHEDULE_static;
2273 Chunk = CGF.EmitScalarConversion(
2274 RT.getGPUNumThreads(CGF),
2275 CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),
2276 S.getIterationVariable()->getType(), S.getBeginLoc());
2277 return;
2278 }
2280 CGF, S, ScheduleKind, Chunk);
2281}
2282
2284 CodeGenFunction &CGF, const OMPLoopDirective &S,
2285 OpenMPScheduleClauseKind &ScheduleKind,
2286 const Expr *&ChunkExpr) const {
2287 ScheduleKind = OMPC_SCHEDULE_static;
2288 // Chunk size is 1 in this case.
2289 llvm::APInt ChunkSize(32, 1);
2290 ChunkExpr = IntegerLiteral::Create(CGF.getContext(), ChunkSize,
2291 CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),
2292 SourceLocation());
2293}
2294
2296 CodeGenFunction &CGF, const OMPExecutableDirective &D) const {
2297 assert(isOpenMPTargetExecutionDirective(D.getDirectiveKind()) &&
2298 " Expected target-based directive.");
2299 const CapturedStmt *CS = D.getCapturedStmt(OMPD_target);
2300 for (const CapturedStmt::Capture &C : CS->captures()) {
2301 // Capture variables captured by reference in lambdas for target-based
2302 // directives.
2303 if (!C.capturesVariable())
2304 continue;
2305 const VarDecl *VD = C.getCapturedVar();
2306 const auto *RD = VD->getType()
2310 if (!RD || !RD->isLambda())
2311 continue;
2312 Address VDAddr = CGF.GetAddrOfLocalVar(VD);
2313 LValue VDLVal;
2315 VDLVal = CGF.EmitLoadOfReferenceLValue(VDAddr, VD->getType());
2316 else
2317 VDLVal = CGF.MakeAddrLValue(
2318 VDAddr, VD->getType().getCanonicalType().getNonReferenceType());
2319 llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;
2320 FieldDecl *ThisCapture = nullptr;
2321 RD->getCaptureFields(Captures, ThisCapture);
2322 if (ThisCapture && CGF.CapturedStmtInfo->isCXXThisExprCaptured()) {
2323 LValue ThisLVal =
2324 CGF.EmitLValueForFieldInitialization(VDLVal, ThisCapture);
2325 llvm::Value *CXXThis = CGF.LoadCXXThis();
2326 CGF.EmitStoreOfScalar(CXXThis, ThisLVal);
2327 }
2328 for (const LambdaCapture &LC : RD->captures()) {
2329 if (LC.getCaptureKind() != LCK_ByRef)
2330 continue;
2331 const ValueDecl *VD = LC.getCapturedVar();
2332 // FIXME: For now VD is always a VarDecl because OpenMP does not support
2333 // capturing structured bindings in lambdas yet.
2334 if (!CS->capturesVariable(cast<VarDecl>(VD)))
2335 continue;
2336 auto It = Captures.find(VD);
2337 assert(It != Captures.end() && "Found lambda capture without field.");
2338 LValue VarLVal = CGF.EmitLValueForFieldInitialization(VDLVal, It->second);
2339 Address VDAddr = CGF.GetAddrOfLocalVar(cast<VarDecl>(VD));
2341 VDAddr = CGF.EmitLoadOfReferenceLValue(VDAddr,
2342 VD->getType().getCanonicalType())
2343 .getAddress();
2344 CGF.EmitStoreOfScalar(VDAddr.emitRawPointer(CGF), VarLVal);
2345 }
2346 }
2347}
2348
2350 LangAS &AS) {
2351 if (!VD || !VD->hasAttr<OMPAllocateDeclAttr>())
2352 return false;
2353 const auto *A = VD->getAttr<OMPAllocateDeclAttr>();
2354 switch(A->getAllocatorType()) {
2355 case OMPAllocateDeclAttr::OMPNullMemAlloc:
2356 case OMPAllocateDeclAttr::OMPDefaultMemAlloc:
2357 // Not supported, fallback to the default mem space.
2358 case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:
2359 case OMPAllocateDeclAttr::OMPCGroupMemAlloc:
2360 case OMPAllocateDeclAttr::OMPHighBWMemAlloc:
2361 case OMPAllocateDeclAttr::OMPLowLatMemAlloc:
2362 case OMPAllocateDeclAttr::OMPThreadMemAlloc:
2363 AS = LangAS::Default;
2364 return true;
2365 case OMPAllocateDeclAttr::OMPConstMemAlloc:
2367 return true;
2368 case OMPAllocateDeclAttr::OMPPTeamMemAlloc:
2370 return true;
2371 case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:
2372 llvm_unreachable("Expected predefined allocator for the variables with the "
2373 "static storage.");
2374 }
2375 return false;
2376}
2377
2378/// Check to see if target architecture supports unified addressing which is
2379/// a restriction for OpenMP requires clause "unified_shared_memory".
2381 StringRef CPU = CGM.getTarget().getTargetOpts().CPU;
2382 if (CGM.getTarget().getTriple().isNVPTX() &&
2383 !llvm::NVPTX::supportsUnifiedAddressing(llvm::NVPTX::parseArch(CPU))) {
2384 for (const OMPClause *Clause : D->clauselists()) {
2385 if (Clause->getClauseKind() == OMPC_unified_shared_memory) {
2386 CGM.getDiags().Report(Clause->getBeginLoc(),
2387 diag::err_omp_unified_shared_memory_unsupported)
2388 << CPU;
2389 return;
2390 }
2391 }
2392 }
2393
2395}
2396
2398 CGBuilderTy &Bld = CGF.Builder;
2399 llvm::Module *M = &CGF.CGM.getModule();
2400 const char *LocSize = "__kmpc_get_hardware_num_threads_in_block";
2401 llvm::Function *F = M->getFunction(LocSize);
2402 if (!F) {
2403 F = llvm::Function::Create(llvm::FunctionType::get(CGF.Int32Ty, {}, false),
2404 llvm::GlobalVariable::ExternalLinkage, LocSize,
2405 &CGF.CGM.getModule());
2406 }
2407 return Bld.CreateCall(F, {}, "nvptx_num_threads");
2408}
2409
2412 return CGF.EmitRuntimeCall(
2413 OMPBuilder.getOrCreateRuntimeFunction(
2414 CGM.getModule(), OMPRTL___kmpc_get_hardware_thread_id_in_block),
2415 Args);
2416}
#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:4763
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:223
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:942
Attr - This represents one attribute.
Definition Attr.h:46
ArrayRef< Capture > captures() const
Definition Decl.h:4933
const BlockDecl * getBlockDecl() const
Definition Expr.h:6701
Expr * getCallee()
Definition Expr.h:3101
arg_range arguments()
Definition Expr.h:3206
Describes the capture of either a variable, or 'this', or variable-length array type.
Definition Stmt.h:3959
This captures a statement into a function.
Definition Stmt.h:3946
bool capturesVariable(const VarDecl *Var) const
True if this variable has been captured.
Definition Stmt.cpp:1517
capture_range captures()
Definition Stmt.h:4084
CastKind getCastKind() const
Definition Expr.h:3731
Expr * getSubExpr()
Definition Expr.h:3737
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:3436
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:3445
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:183
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:240
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:5970
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:232
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:671
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:196
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:651
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:2051
const CGFunctionInfo & arrangeBuiltinFunctionDeclaration(QualType resultType, const FunctionArgList &args)
A builtin function is a freestanding function using the default C conventions.
Definition CGCall.cpp:780
unsigned getTargetAddressSpace(QualType T) const
FunctionArgList - Type for representing both the decl and type of parameters to a function.
Definition CGCall.h:377
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:1349
decl_range decls()
Definition Stmt.h:1688
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:831
This represents one expression.
Definition Expr.h:112
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3101
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3097
bool isLValue() const
isLValue - True if this expression is an "l-value" according to the rules of the current language.
Definition Expr.h:284
Represents a member of a struct/union/class.
Definition Decl.h:3294
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:4763
GlobalDecl - represents a global declaration.
Definition GlobalDecl.h:57
static ImplicitParamDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation IdLoc, const IdentifierInfo *Id, QualType T, ImplicitParamKind ParamKind)
Create implicit parameter.
Definition Decl.cpp:5665
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:1365
capture_range captures() const
Retrieve this lambda's captures.
Definition ExprCXX.cpp:1378
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:295
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
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:2944
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3408
A (possibly-)qualified type.
Definition TypeBase.h:938
LangAS getAddressSpace() const
Return the address space of this type.
Definition TypeBase.h:8630
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:8689
QualType getCanonicalType() const
Definition TypeBase.h:8556
A qualifier set is used to build a set of qualifiers.
Definition TypeBase.h:8444
const Type * strip(QualType type)
Collect any qualifiers on the given type and return an unqualified type.
Definition TypeBase.h:8451
QualType apply(const ASTContext &Context, QualType QT) const
Apply the collected qualifiers to the given type.
Definition Type.cpp:4826
Represents a struct/union/class.
Definition Decl.h:4459
virtual void completeDefinition()
Note that the definition of this type is now complete.
Definition Decl.cpp:5354
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:4969
unsigned getNewAlign() const
Return the largest alignment for which a suitably-sized allocation with 'operator new(size_t)' is gua...
Definition TargetInfo.h:773
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:9157
bool isReferenceType() const
Definition TypeBase.h:8765
bool isLValueReferenceType() const
Definition TypeBase.h:8769
bool hasSignedIntegerRepresentation() const
Determine whether this type has an signed integer representation of some sort, e.g....
Definition Type.cpp:2340
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:2419
Expr * getSubExpr() const
Definition Expr.h:2296
Opcode getOpcode() const
Definition Expr.h:2291
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:712
QualType getType() const
Definition Decl.h:723
bool isInitCapture() const
Whether this variable is the implicit variable for a lambda init-capture.
Definition Decl.cpp:5650
Represents a variable declaration or definition.
Definition Decl.h:932
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition Decl.cpp:2238
bool isInitCapture() const
Whether this variable is the implicit variable for a lambda init-capture.
Definition Decl.h:1602
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:1762
@ Other
Other implicit parameter.
Definition Decl.h:1774
OpenMPScheduleClauseKind
OpenMP attributes for 'schedule' clause.
Definition OpenMPKinds.h:31
unsigned long uint64_t