1e5dd7070Spatrick //===--- SemaCUDA.cpp - Semantic Analysis for CUDA constructs -------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick /// \file
9e5dd7070Spatrick /// This file implements semantic analysis for CUDA constructs.
10e5dd7070Spatrick ///
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick
13e5dd7070Spatrick #include "clang/AST/ASTContext.h"
14e5dd7070Spatrick #include "clang/AST/Decl.h"
15e5dd7070Spatrick #include "clang/AST/ExprCXX.h"
16e5dd7070Spatrick #include "clang/Basic/Cuda.h"
17ec727ea7Spatrick #include "clang/Basic/TargetInfo.h"
18e5dd7070Spatrick #include "clang/Lex/Preprocessor.h"
19e5dd7070Spatrick #include "clang/Sema/Lookup.h"
20ec727ea7Spatrick #include "clang/Sema/ScopeInfo.h"
21e5dd7070Spatrick #include "clang/Sema/Sema.h"
22e5dd7070Spatrick #include "clang/Sema/SemaDiagnostic.h"
23e5dd7070Spatrick #include "clang/Sema/SemaInternal.h"
24e5dd7070Spatrick #include "clang/Sema/Template.h"
25e5dd7070Spatrick #include "llvm/ADT/SmallVector.h"
26*12c85518Srobert #include <optional>
27e5dd7070Spatrick using namespace clang;
28e5dd7070Spatrick
hasExplicitAttr(const VarDecl * D)29a9ac8606Spatrick template <typename AttrT> static bool hasExplicitAttr(const VarDecl *D) {
30a9ac8606Spatrick if (!D)
31a9ac8606Spatrick return false;
32a9ac8606Spatrick if (auto *A = D->getAttr<AttrT>())
33a9ac8606Spatrick return !A->isImplicit();
34a9ac8606Spatrick return false;
35a9ac8606Spatrick }
36a9ac8606Spatrick
PushForceCUDAHostDevice()37e5dd7070Spatrick void Sema::PushForceCUDAHostDevice() {
38e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
39e5dd7070Spatrick ForceCUDAHostDeviceDepth++;
40e5dd7070Spatrick }
41e5dd7070Spatrick
PopForceCUDAHostDevice()42e5dd7070Spatrick bool Sema::PopForceCUDAHostDevice() {
43e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
44e5dd7070Spatrick if (ForceCUDAHostDeviceDepth == 0)
45e5dd7070Spatrick return false;
46e5dd7070Spatrick ForceCUDAHostDeviceDepth--;
47e5dd7070Spatrick return true;
48e5dd7070Spatrick }
49e5dd7070Spatrick
ActOnCUDAExecConfigExpr(Scope * S,SourceLocation LLLLoc,MultiExprArg ExecConfig,SourceLocation GGGLoc)50e5dd7070Spatrick ExprResult Sema::ActOnCUDAExecConfigExpr(Scope *S, SourceLocation LLLLoc,
51e5dd7070Spatrick MultiExprArg ExecConfig,
52e5dd7070Spatrick SourceLocation GGGLoc) {
53e5dd7070Spatrick FunctionDecl *ConfigDecl = Context.getcudaConfigureCallDecl();
54e5dd7070Spatrick if (!ConfigDecl)
55e5dd7070Spatrick return ExprError(Diag(LLLLoc, diag::err_undeclared_var_use)
56e5dd7070Spatrick << getCudaConfigureFuncName());
57e5dd7070Spatrick QualType ConfigQTy = ConfigDecl->getType();
58e5dd7070Spatrick
59e5dd7070Spatrick DeclRefExpr *ConfigDR = new (Context)
60e5dd7070Spatrick DeclRefExpr(Context, ConfigDecl, false, ConfigQTy, VK_LValue, LLLLoc);
61e5dd7070Spatrick MarkFunctionReferenced(LLLLoc, ConfigDecl);
62e5dd7070Spatrick
63e5dd7070Spatrick return BuildCallExpr(S, ConfigDR, LLLLoc, ExecConfig, GGGLoc, nullptr,
64e5dd7070Spatrick /*IsExecConfig=*/true);
65e5dd7070Spatrick }
66e5dd7070Spatrick
67e5dd7070Spatrick Sema::CUDAFunctionTarget
IdentifyCUDATarget(const ParsedAttributesView & Attrs)68e5dd7070Spatrick Sema::IdentifyCUDATarget(const ParsedAttributesView &Attrs) {
69e5dd7070Spatrick bool HasHostAttr = false;
70e5dd7070Spatrick bool HasDeviceAttr = false;
71e5dd7070Spatrick bool HasGlobalAttr = false;
72e5dd7070Spatrick bool HasInvalidTargetAttr = false;
73e5dd7070Spatrick for (const ParsedAttr &AL : Attrs) {
74e5dd7070Spatrick switch (AL.getKind()) {
75e5dd7070Spatrick case ParsedAttr::AT_CUDAGlobal:
76e5dd7070Spatrick HasGlobalAttr = true;
77e5dd7070Spatrick break;
78e5dd7070Spatrick case ParsedAttr::AT_CUDAHost:
79e5dd7070Spatrick HasHostAttr = true;
80e5dd7070Spatrick break;
81e5dd7070Spatrick case ParsedAttr::AT_CUDADevice:
82e5dd7070Spatrick HasDeviceAttr = true;
83e5dd7070Spatrick break;
84e5dd7070Spatrick case ParsedAttr::AT_CUDAInvalidTarget:
85e5dd7070Spatrick HasInvalidTargetAttr = true;
86e5dd7070Spatrick break;
87e5dd7070Spatrick default:
88e5dd7070Spatrick break;
89e5dd7070Spatrick }
90e5dd7070Spatrick }
91e5dd7070Spatrick
92e5dd7070Spatrick if (HasInvalidTargetAttr)
93e5dd7070Spatrick return CFT_InvalidTarget;
94e5dd7070Spatrick
95e5dd7070Spatrick if (HasGlobalAttr)
96e5dd7070Spatrick return CFT_Global;
97e5dd7070Spatrick
98e5dd7070Spatrick if (HasHostAttr && HasDeviceAttr)
99e5dd7070Spatrick return CFT_HostDevice;
100e5dd7070Spatrick
101e5dd7070Spatrick if (HasDeviceAttr)
102e5dd7070Spatrick return CFT_Device;
103e5dd7070Spatrick
104e5dd7070Spatrick return CFT_Host;
105e5dd7070Spatrick }
106e5dd7070Spatrick
107e5dd7070Spatrick template <typename A>
hasAttr(const FunctionDecl * D,bool IgnoreImplicitAttr)108e5dd7070Spatrick static bool hasAttr(const FunctionDecl *D, bool IgnoreImplicitAttr) {
109e5dd7070Spatrick return D->hasAttrs() && llvm::any_of(D->getAttrs(), [&](Attr *Attribute) {
110e5dd7070Spatrick return isa<A>(Attribute) &&
111e5dd7070Spatrick !(IgnoreImplicitAttr && Attribute->isImplicit());
112e5dd7070Spatrick });
113e5dd7070Spatrick }
114e5dd7070Spatrick
115e5dd7070Spatrick /// IdentifyCUDATarget - Determine the CUDA compilation target for this function
IdentifyCUDATarget(const FunctionDecl * D,bool IgnoreImplicitHDAttr)116e5dd7070Spatrick Sema::CUDAFunctionTarget Sema::IdentifyCUDATarget(const FunctionDecl *D,
117e5dd7070Spatrick bool IgnoreImplicitHDAttr) {
118e5dd7070Spatrick // Code that lives outside a function is run on the host.
119e5dd7070Spatrick if (D == nullptr)
120e5dd7070Spatrick return CFT_Host;
121e5dd7070Spatrick
122e5dd7070Spatrick if (D->hasAttr<CUDAInvalidTargetAttr>())
123e5dd7070Spatrick return CFT_InvalidTarget;
124e5dd7070Spatrick
125e5dd7070Spatrick if (D->hasAttr<CUDAGlobalAttr>())
126e5dd7070Spatrick return CFT_Global;
127e5dd7070Spatrick
128e5dd7070Spatrick if (hasAttr<CUDADeviceAttr>(D, IgnoreImplicitHDAttr)) {
129e5dd7070Spatrick if (hasAttr<CUDAHostAttr>(D, IgnoreImplicitHDAttr))
130e5dd7070Spatrick return CFT_HostDevice;
131e5dd7070Spatrick return CFT_Device;
132e5dd7070Spatrick } else if (hasAttr<CUDAHostAttr>(D, IgnoreImplicitHDAttr)) {
133e5dd7070Spatrick return CFT_Host;
134a9ac8606Spatrick } else if ((D->isImplicit() || !D->isUserProvided()) &&
135a9ac8606Spatrick !IgnoreImplicitHDAttr) {
136e5dd7070Spatrick // Some implicit declarations (like intrinsic functions) are not marked.
137e5dd7070Spatrick // Set the most lenient target on them for maximal flexibility.
138e5dd7070Spatrick return CFT_HostDevice;
139e5dd7070Spatrick }
140e5dd7070Spatrick
141e5dd7070Spatrick return CFT_Host;
142e5dd7070Spatrick }
143e5dd7070Spatrick
144a9ac8606Spatrick /// IdentifyTarget - Determine the CUDA compilation target for this variable.
IdentifyCUDATarget(const VarDecl * Var)145a9ac8606Spatrick Sema::CUDAVariableTarget Sema::IdentifyCUDATarget(const VarDecl *Var) {
146a9ac8606Spatrick if (Var->hasAttr<HIPManagedAttr>())
147a9ac8606Spatrick return CVT_Unified;
148*12c85518Srobert // Only constexpr and const variabless with implicit constant attribute
149*12c85518Srobert // are emitted on both sides. Such variables are promoted to device side
150*12c85518Srobert // only if they have static constant intializers on device side.
151*12c85518Srobert if ((Var->isConstexpr() || Var->getType().isConstQualified()) &&
152*12c85518Srobert Var->hasAttr<CUDAConstantAttr>() &&
153a9ac8606Spatrick !hasExplicitAttr<CUDAConstantAttr>(Var))
154a9ac8606Spatrick return CVT_Both;
155a9ac8606Spatrick if (Var->hasAttr<CUDADeviceAttr>() || Var->hasAttr<CUDAConstantAttr>() ||
156a9ac8606Spatrick Var->hasAttr<CUDASharedAttr>() ||
157a9ac8606Spatrick Var->getType()->isCUDADeviceBuiltinSurfaceType() ||
158a9ac8606Spatrick Var->getType()->isCUDADeviceBuiltinTextureType())
159a9ac8606Spatrick return CVT_Device;
160a9ac8606Spatrick // Function-scope static variable without explicit device or constant
161a9ac8606Spatrick // attribute are emitted
162a9ac8606Spatrick // - on both sides in host device functions
163a9ac8606Spatrick // - on device side in device or global functions
164a9ac8606Spatrick if (auto *FD = dyn_cast<FunctionDecl>(Var->getDeclContext())) {
165a9ac8606Spatrick switch (IdentifyCUDATarget(FD)) {
166a9ac8606Spatrick case CFT_HostDevice:
167a9ac8606Spatrick return CVT_Both;
168a9ac8606Spatrick case CFT_Device:
169a9ac8606Spatrick case CFT_Global:
170a9ac8606Spatrick return CVT_Device;
171a9ac8606Spatrick default:
172a9ac8606Spatrick return CVT_Host;
173a9ac8606Spatrick }
174a9ac8606Spatrick }
175a9ac8606Spatrick return CVT_Host;
176a9ac8606Spatrick }
177a9ac8606Spatrick
178e5dd7070Spatrick // * CUDA Call preference table
179e5dd7070Spatrick //
180e5dd7070Spatrick // F - from,
181e5dd7070Spatrick // T - to
182e5dd7070Spatrick // Ph - preference in host mode
183e5dd7070Spatrick // Pd - preference in device mode
184e5dd7070Spatrick // H - handled in (x)
185e5dd7070Spatrick // Preferences: N:native, SS:same side, HD:host-device, WS:wrong side, --:never.
186e5dd7070Spatrick //
187e5dd7070Spatrick // | F | T | Ph | Pd | H |
188e5dd7070Spatrick // |----+----+-----+-----+-----+
189e5dd7070Spatrick // | d | d | N | N | (c) |
190e5dd7070Spatrick // | d | g | -- | -- | (a) |
191e5dd7070Spatrick // | d | h | -- | -- | (e) |
192e5dd7070Spatrick // | d | hd | HD | HD | (b) |
193e5dd7070Spatrick // | g | d | N | N | (c) |
194e5dd7070Spatrick // | g | g | -- | -- | (a) |
195e5dd7070Spatrick // | g | h | -- | -- | (e) |
196e5dd7070Spatrick // | g | hd | HD | HD | (b) |
197e5dd7070Spatrick // | h | d | -- | -- | (e) |
198e5dd7070Spatrick // | h | g | N | N | (c) |
199e5dd7070Spatrick // | h | h | N | N | (c) |
200e5dd7070Spatrick // | h | hd | HD | HD | (b) |
201e5dd7070Spatrick // | hd | d | WS | SS | (d) |
202e5dd7070Spatrick // | hd | g | SS | -- |(d/a)|
203e5dd7070Spatrick // | hd | h | SS | WS | (d) |
204e5dd7070Spatrick // | hd | hd | HD | HD | (b) |
205e5dd7070Spatrick
206e5dd7070Spatrick Sema::CUDAFunctionPreference
IdentifyCUDAPreference(const FunctionDecl * Caller,const FunctionDecl * Callee)207e5dd7070Spatrick Sema::IdentifyCUDAPreference(const FunctionDecl *Caller,
208e5dd7070Spatrick const FunctionDecl *Callee) {
209e5dd7070Spatrick assert(Callee && "Callee must be valid.");
210e5dd7070Spatrick CUDAFunctionTarget CallerTarget = IdentifyCUDATarget(Caller);
211e5dd7070Spatrick CUDAFunctionTarget CalleeTarget = IdentifyCUDATarget(Callee);
212e5dd7070Spatrick
213e5dd7070Spatrick // If one of the targets is invalid, the check always fails, no matter what
214e5dd7070Spatrick // the other target is.
215e5dd7070Spatrick if (CallerTarget == CFT_InvalidTarget || CalleeTarget == CFT_InvalidTarget)
216e5dd7070Spatrick return CFP_Never;
217e5dd7070Spatrick
218e5dd7070Spatrick // (a) Can't call global from some contexts until we support CUDA's
219e5dd7070Spatrick // dynamic parallelism.
220e5dd7070Spatrick if (CalleeTarget == CFT_Global &&
221e5dd7070Spatrick (CallerTarget == CFT_Global || CallerTarget == CFT_Device))
222e5dd7070Spatrick return CFP_Never;
223e5dd7070Spatrick
224e5dd7070Spatrick // (b) Calling HostDevice is OK for everyone.
225e5dd7070Spatrick if (CalleeTarget == CFT_HostDevice)
226e5dd7070Spatrick return CFP_HostDevice;
227e5dd7070Spatrick
228e5dd7070Spatrick // (c) Best case scenarios
229e5dd7070Spatrick if (CalleeTarget == CallerTarget ||
230e5dd7070Spatrick (CallerTarget == CFT_Host && CalleeTarget == CFT_Global) ||
231e5dd7070Spatrick (CallerTarget == CFT_Global && CalleeTarget == CFT_Device))
232e5dd7070Spatrick return CFP_Native;
233e5dd7070Spatrick
234e5dd7070Spatrick // (d) HostDevice behavior depends on compilation mode.
235e5dd7070Spatrick if (CallerTarget == CFT_HostDevice) {
236e5dd7070Spatrick // It's OK to call a compilation-mode matching function from an HD one.
237e5dd7070Spatrick if ((getLangOpts().CUDAIsDevice && CalleeTarget == CFT_Device) ||
238e5dd7070Spatrick (!getLangOpts().CUDAIsDevice &&
239e5dd7070Spatrick (CalleeTarget == CFT_Host || CalleeTarget == CFT_Global)))
240e5dd7070Spatrick return CFP_SameSide;
241e5dd7070Spatrick
242e5dd7070Spatrick // Calls from HD to non-mode-matching functions (i.e., to host functions
243e5dd7070Spatrick // when compiling in device mode or to device functions when compiling in
244e5dd7070Spatrick // host mode) are allowed at the sema level, but eventually rejected if
245e5dd7070Spatrick // they're ever codegened. TODO: Reject said calls earlier.
246e5dd7070Spatrick return CFP_WrongSide;
247e5dd7070Spatrick }
248e5dd7070Spatrick
249e5dd7070Spatrick // (e) Calling across device/host boundary is not something you should do.
250e5dd7070Spatrick if ((CallerTarget == CFT_Host && CalleeTarget == CFT_Device) ||
251e5dd7070Spatrick (CallerTarget == CFT_Device && CalleeTarget == CFT_Host) ||
252e5dd7070Spatrick (CallerTarget == CFT_Global && CalleeTarget == CFT_Host))
253e5dd7070Spatrick return CFP_Never;
254e5dd7070Spatrick
255e5dd7070Spatrick llvm_unreachable("All cases should've been handled by now.");
256e5dd7070Spatrick }
257e5dd7070Spatrick
hasImplicitAttr(const FunctionDecl * D)258ec727ea7Spatrick template <typename AttrT> static bool hasImplicitAttr(const FunctionDecl *D) {
259ec727ea7Spatrick if (!D)
260ec727ea7Spatrick return false;
261ec727ea7Spatrick if (auto *A = D->getAttr<AttrT>())
262ec727ea7Spatrick return A->isImplicit();
263ec727ea7Spatrick return D->isImplicit();
264ec727ea7Spatrick }
265ec727ea7Spatrick
isCUDAImplicitHostDeviceFunction(const FunctionDecl * D)266ec727ea7Spatrick bool Sema::isCUDAImplicitHostDeviceFunction(const FunctionDecl *D) {
267ec727ea7Spatrick bool IsImplicitDevAttr = hasImplicitAttr<CUDADeviceAttr>(D);
268ec727ea7Spatrick bool IsImplicitHostAttr = hasImplicitAttr<CUDAHostAttr>(D);
269ec727ea7Spatrick return IsImplicitDevAttr && IsImplicitHostAttr;
270ec727ea7Spatrick }
271ec727ea7Spatrick
EraseUnwantedCUDAMatches(const FunctionDecl * Caller,SmallVectorImpl<std::pair<DeclAccessPair,FunctionDecl * >> & Matches)272e5dd7070Spatrick void Sema::EraseUnwantedCUDAMatches(
273e5dd7070Spatrick const FunctionDecl *Caller,
274e5dd7070Spatrick SmallVectorImpl<std::pair<DeclAccessPair, FunctionDecl *>> &Matches) {
275e5dd7070Spatrick if (Matches.size() <= 1)
276e5dd7070Spatrick return;
277e5dd7070Spatrick
278e5dd7070Spatrick using Pair = std::pair<DeclAccessPair, FunctionDecl*>;
279e5dd7070Spatrick
280e5dd7070Spatrick // Gets the CUDA function preference for a call from Caller to Match.
281e5dd7070Spatrick auto GetCFP = [&](const Pair &Match) {
282e5dd7070Spatrick return IdentifyCUDAPreference(Caller, Match.second);
283e5dd7070Spatrick };
284e5dd7070Spatrick
285e5dd7070Spatrick // Find the best call preference among the functions in Matches.
286e5dd7070Spatrick CUDAFunctionPreference BestCFP = GetCFP(*std::max_element(
287e5dd7070Spatrick Matches.begin(), Matches.end(),
288e5dd7070Spatrick [&](const Pair &M1, const Pair &M2) { return GetCFP(M1) < GetCFP(M2); }));
289e5dd7070Spatrick
290e5dd7070Spatrick // Erase all functions with lower priority.
291e5dd7070Spatrick llvm::erase_if(Matches,
292e5dd7070Spatrick [&](const Pair &Match) { return GetCFP(Match) < BestCFP; });
293e5dd7070Spatrick }
294e5dd7070Spatrick
295e5dd7070Spatrick /// When an implicitly-declared special member has to invoke more than one
296e5dd7070Spatrick /// base/field special member, conflicts may occur in the targets of these
297e5dd7070Spatrick /// members. For example, if one base's member __host__ and another's is
298e5dd7070Spatrick /// __device__, it's a conflict.
299e5dd7070Spatrick /// This function figures out if the given targets \param Target1 and
300e5dd7070Spatrick /// \param Target2 conflict, and if they do not it fills in
301e5dd7070Spatrick /// \param ResolvedTarget with a target that resolves for both calls.
302e5dd7070Spatrick /// \return true if there's a conflict, false otherwise.
303e5dd7070Spatrick static bool
resolveCalleeCUDATargetConflict(Sema::CUDAFunctionTarget Target1,Sema::CUDAFunctionTarget Target2,Sema::CUDAFunctionTarget * ResolvedTarget)304e5dd7070Spatrick resolveCalleeCUDATargetConflict(Sema::CUDAFunctionTarget Target1,
305e5dd7070Spatrick Sema::CUDAFunctionTarget Target2,
306e5dd7070Spatrick Sema::CUDAFunctionTarget *ResolvedTarget) {
307e5dd7070Spatrick // Only free functions and static member functions may be global.
308e5dd7070Spatrick assert(Target1 != Sema::CFT_Global);
309e5dd7070Spatrick assert(Target2 != Sema::CFT_Global);
310e5dd7070Spatrick
311e5dd7070Spatrick if (Target1 == Sema::CFT_HostDevice) {
312e5dd7070Spatrick *ResolvedTarget = Target2;
313e5dd7070Spatrick } else if (Target2 == Sema::CFT_HostDevice) {
314e5dd7070Spatrick *ResolvedTarget = Target1;
315e5dd7070Spatrick } else if (Target1 != Target2) {
316e5dd7070Spatrick return true;
317e5dd7070Spatrick } else {
318e5dd7070Spatrick *ResolvedTarget = Target1;
319e5dd7070Spatrick }
320e5dd7070Spatrick
321e5dd7070Spatrick return false;
322e5dd7070Spatrick }
323e5dd7070Spatrick
inferCUDATargetForImplicitSpecialMember(CXXRecordDecl * ClassDecl,CXXSpecialMember CSM,CXXMethodDecl * MemberDecl,bool ConstRHS,bool Diagnose)324e5dd7070Spatrick bool Sema::inferCUDATargetForImplicitSpecialMember(CXXRecordDecl *ClassDecl,
325e5dd7070Spatrick CXXSpecialMember CSM,
326e5dd7070Spatrick CXXMethodDecl *MemberDecl,
327e5dd7070Spatrick bool ConstRHS,
328e5dd7070Spatrick bool Diagnose) {
329e5dd7070Spatrick // If the defaulted special member is defined lexically outside of its
330e5dd7070Spatrick // owning class, or the special member already has explicit device or host
331e5dd7070Spatrick // attributes, do not infer.
332e5dd7070Spatrick bool InClass = MemberDecl->getLexicalParent() == MemberDecl->getParent();
333e5dd7070Spatrick bool HasH = MemberDecl->hasAttr<CUDAHostAttr>();
334e5dd7070Spatrick bool HasD = MemberDecl->hasAttr<CUDADeviceAttr>();
335e5dd7070Spatrick bool HasExplicitAttr =
336e5dd7070Spatrick (HasD && !MemberDecl->getAttr<CUDADeviceAttr>()->isImplicit()) ||
337e5dd7070Spatrick (HasH && !MemberDecl->getAttr<CUDAHostAttr>()->isImplicit());
338e5dd7070Spatrick if (!InClass || HasExplicitAttr)
339e5dd7070Spatrick return false;
340e5dd7070Spatrick
341*12c85518Srobert std::optional<CUDAFunctionTarget> InferredTarget;
342e5dd7070Spatrick
343e5dd7070Spatrick // We're going to invoke special member lookup; mark that these special
344e5dd7070Spatrick // members are called from this one, and not from its caller.
345e5dd7070Spatrick ContextRAII MethodContext(*this, MemberDecl);
346e5dd7070Spatrick
347e5dd7070Spatrick // Look for special members in base classes that should be invoked from here.
348e5dd7070Spatrick // Infer the target of this member base on the ones it should call.
349e5dd7070Spatrick // Skip direct and indirect virtual bases for abstract classes.
350e5dd7070Spatrick llvm::SmallVector<const CXXBaseSpecifier *, 16> Bases;
351e5dd7070Spatrick for (const auto &B : ClassDecl->bases()) {
352e5dd7070Spatrick if (!B.isVirtual()) {
353e5dd7070Spatrick Bases.push_back(&B);
354e5dd7070Spatrick }
355e5dd7070Spatrick }
356e5dd7070Spatrick
357e5dd7070Spatrick if (!ClassDecl->isAbstract()) {
358*12c85518Srobert llvm::append_range(Bases, llvm::make_pointer_range(ClassDecl->vbases()));
359e5dd7070Spatrick }
360e5dd7070Spatrick
361e5dd7070Spatrick for (const auto *B : Bases) {
362e5dd7070Spatrick const RecordType *BaseType = B->getType()->getAs<RecordType>();
363e5dd7070Spatrick if (!BaseType) {
364e5dd7070Spatrick continue;
365e5dd7070Spatrick }
366e5dd7070Spatrick
367e5dd7070Spatrick CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
368e5dd7070Spatrick Sema::SpecialMemberOverloadResult SMOR =
369e5dd7070Spatrick LookupSpecialMember(BaseClassDecl, CSM,
370e5dd7070Spatrick /* ConstArg */ ConstRHS,
371e5dd7070Spatrick /* VolatileArg */ false,
372e5dd7070Spatrick /* RValueThis */ false,
373e5dd7070Spatrick /* ConstThis */ false,
374e5dd7070Spatrick /* VolatileThis */ false);
375e5dd7070Spatrick
376e5dd7070Spatrick if (!SMOR.getMethod())
377e5dd7070Spatrick continue;
378e5dd7070Spatrick
379e5dd7070Spatrick CUDAFunctionTarget BaseMethodTarget = IdentifyCUDATarget(SMOR.getMethod());
380*12c85518Srobert if (!InferredTarget) {
381e5dd7070Spatrick InferredTarget = BaseMethodTarget;
382e5dd7070Spatrick } else {
383e5dd7070Spatrick bool ResolutionError = resolveCalleeCUDATargetConflict(
384*12c85518Srobert *InferredTarget, BaseMethodTarget, &*InferredTarget);
385e5dd7070Spatrick if (ResolutionError) {
386e5dd7070Spatrick if (Diagnose) {
387e5dd7070Spatrick Diag(ClassDecl->getLocation(),
388e5dd7070Spatrick diag::note_implicit_member_target_infer_collision)
389*12c85518Srobert << (unsigned)CSM << *InferredTarget << BaseMethodTarget;
390e5dd7070Spatrick }
391e5dd7070Spatrick MemberDecl->addAttr(CUDAInvalidTargetAttr::CreateImplicit(Context));
392e5dd7070Spatrick return true;
393e5dd7070Spatrick }
394e5dd7070Spatrick }
395e5dd7070Spatrick }
396e5dd7070Spatrick
397e5dd7070Spatrick // Same as for bases, but now for special members of fields.
398e5dd7070Spatrick for (const auto *F : ClassDecl->fields()) {
399e5dd7070Spatrick if (F->isInvalidDecl()) {
400e5dd7070Spatrick continue;
401e5dd7070Spatrick }
402e5dd7070Spatrick
403e5dd7070Spatrick const RecordType *FieldType =
404e5dd7070Spatrick Context.getBaseElementType(F->getType())->getAs<RecordType>();
405e5dd7070Spatrick if (!FieldType) {
406e5dd7070Spatrick continue;
407e5dd7070Spatrick }
408e5dd7070Spatrick
409e5dd7070Spatrick CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(FieldType->getDecl());
410e5dd7070Spatrick Sema::SpecialMemberOverloadResult SMOR =
411e5dd7070Spatrick LookupSpecialMember(FieldRecDecl, CSM,
412e5dd7070Spatrick /* ConstArg */ ConstRHS && !F->isMutable(),
413e5dd7070Spatrick /* VolatileArg */ false,
414e5dd7070Spatrick /* RValueThis */ false,
415e5dd7070Spatrick /* ConstThis */ false,
416e5dd7070Spatrick /* VolatileThis */ false);
417e5dd7070Spatrick
418e5dd7070Spatrick if (!SMOR.getMethod())
419e5dd7070Spatrick continue;
420e5dd7070Spatrick
421e5dd7070Spatrick CUDAFunctionTarget FieldMethodTarget =
422e5dd7070Spatrick IdentifyCUDATarget(SMOR.getMethod());
423*12c85518Srobert if (!InferredTarget) {
424e5dd7070Spatrick InferredTarget = FieldMethodTarget;
425e5dd7070Spatrick } else {
426e5dd7070Spatrick bool ResolutionError = resolveCalleeCUDATargetConflict(
427*12c85518Srobert *InferredTarget, FieldMethodTarget, &*InferredTarget);
428e5dd7070Spatrick if (ResolutionError) {
429e5dd7070Spatrick if (Diagnose) {
430e5dd7070Spatrick Diag(ClassDecl->getLocation(),
431e5dd7070Spatrick diag::note_implicit_member_target_infer_collision)
432*12c85518Srobert << (unsigned)CSM << *InferredTarget << FieldMethodTarget;
433e5dd7070Spatrick }
434e5dd7070Spatrick MemberDecl->addAttr(CUDAInvalidTargetAttr::CreateImplicit(Context));
435e5dd7070Spatrick return true;
436e5dd7070Spatrick }
437e5dd7070Spatrick }
438e5dd7070Spatrick }
439e5dd7070Spatrick
440e5dd7070Spatrick
441e5dd7070Spatrick // If no target was inferred, mark this member as __host__ __device__;
442e5dd7070Spatrick // it's the least restrictive option that can be invoked from any target.
443e5dd7070Spatrick bool NeedsH = true, NeedsD = true;
444*12c85518Srobert if (InferredTarget) {
445*12c85518Srobert if (*InferredTarget == CFT_Device)
446e5dd7070Spatrick NeedsH = false;
447*12c85518Srobert else if (*InferredTarget == CFT_Host)
448e5dd7070Spatrick NeedsD = false;
449e5dd7070Spatrick }
450e5dd7070Spatrick
451e5dd7070Spatrick // We either setting attributes first time, or the inferred ones must match
452e5dd7070Spatrick // previously set ones.
453e5dd7070Spatrick if (NeedsD && !HasD)
454e5dd7070Spatrick MemberDecl->addAttr(CUDADeviceAttr::CreateImplicit(Context));
455e5dd7070Spatrick if (NeedsH && !HasH)
456e5dd7070Spatrick MemberDecl->addAttr(CUDAHostAttr::CreateImplicit(Context));
457e5dd7070Spatrick
458e5dd7070Spatrick return false;
459e5dd7070Spatrick }
460e5dd7070Spatrick
isEmptyCudaConstructor(SourceLocation Loc,CXXConstructorDecl * CD)461e5dd7070Spatrick bool Sema::isEmptyCudaConstructor(SourceLocation Loc, CXXConstructorDecl *CD) {
462e5dd7070Spatrick if (!CD->isDefined() && CD->isTemplateInstantiation())
463e5dd7070Spatrick InstantiateFunctionDefinition(Loc, CD->getFirstDecl());
464e5dd7070Spatrick
465e5dd7070Spatrick // (E.2.3.1, CUDA 7.5) A constructor for a class type is considered
466e5dd7070Spatrick // empty at a point in the translation unit, if it is either a
467e5dd7070Spatrick // trivial constructor
468e5dd7070Spatrick if (CD->isTrivial())
469e5dd7070Spatrick return true;
470e5dd7070Spatrick
471e5dd7070Spatrick // ... or it satisfies all of the following conditions:
472e5dd7070Spatrick // The constructor function has been defined.
473e5dd7070Spatrick // The constructor function has no parameters,
474e5dd7070Spatrick // and the function body is an empty compound statement.
475e5dd7070Spatrick if (!(CD->hasTrivialBody() && CD->getNumParams() == 0))
476e5dd7070Spatrick return false;
477e5dd7070Spatrick
478e5dd7070Spatrick // Its class has no virtual functions and no virtual base classes.
479e5dd7070Spatrick if (CD->getParent()->isDynamicClass())
480e5dd7070Spatrick return false;
481e5dd7070Spatrick
482ec727ea7Spatrick // Union ctor does not call ctors of its data members.
483ec727ea7Spatrick if (CD->getParent()->isUnion())
484ec727ea7Spatrick return true;
485ec727ea7Spatrick
486e5dd7070Spatrick // The only form of initializer allowed is an empty constructor.
487e5dd7070Spatrick // This will recursively check all base classes and member initializers
488e5dd7070Spatrick if (!llvm::all_of(CD->inits(), [&](const CXXCtorInitializer *CI) {
489e5dd7070Spatrick if (const CXXConstructExpr *CE =
490e5dd7070Spatrick dyn_cast<CXXConstructExpr>(CI->getInit()))
491e5dd7070Spatrick return isEmptyCudaConstructor(Loc, CE->getConstructor());
492e5dd7070Spatrick return false;
493e5dd7070Spatrick }))
494e5dd7070Spatrick return false;
495e5dd7070Spatrick
496e5dd7070Spatrick return true;
497e5dd7070Spatrick }
498e5dd7070Spatrick
isEmptyCudaDestructor(SourceLocation Loc,CXXDestructorDecl * DD)499e5dd7070Spatrick bool Sema::isEmptyCudaDestructor(SourceLocation Loc, CXXDestructorDecl *DD) {
500e5dd7070Spatrick // No destructor -> no problem.
501e5dd7070Spatrick if (!DD)
502e5dd7070Spatrick return true;
503e5dd7070Spatrick
504e5dd7070Spatrick if (!DD->isDefined() && DD->isTemplateInstantiation())
505e5dd7070Spatrick InstantiateFunctionDefinition(Loc, DD->getFirstDecl());
506e5dd7070Spatrick
507e5dd7070Spatrick // (E.2.3.1, CUDA 7.5) A destructor for a class type is considered
508e5dd7070Spatrick // empty at a point in the translation unit, if it is either a
509e5dd7070Spatrick // trivial constructor
510e5dd7070Spatrick if (DD->isTrivial())
511e5dd7070Spatrick return true;
512e5dd7070Spatrick
513e5dd7070Spatrick // ... or it satisfies all of the following conditions:
514e5dd7070Spatrick // The destructor function has been defined.
515e5dd7070Spatrick // and the function body is an empty compound statement.
516e5dd7070Spatrick if (!DD->hasTrivialBody())
517e5dd7070Spatrick return false;
518e5dd7070Spatrick
519e5dd7070Spatrick const CXXRecordDecl *ClassDecl = DD->getParent();
520e5dd7070Spatrick
521e5dd7070Spatrick // Its class has no virtual functions and no virtual base classes.
522e5dd7070Spatrick if (ClassDecl->isDynamicClass())
523e5dd7070Spatrick return false;
524e5dd7070Spatrick
525ec727ea7Spatrick // Union does not have base class and union dtor does not call dtors of its
526ec727ea7Spatrick // data members.
527ec727ea7Spatrick if (DD->getParent()->isUnion())
528ec727ea7Spatrick return true;
529ec727ea7Spatrick
530e5dd7070Spatrick // Only empty destructors are allowed. This will recursively check
531e5dd7070Spatrick // destructors for all base classes...
532e5dd7070Spatrick if (!llvm::all_of(ClassDecl->bases(), [&](const CXXBaseSpecifier &BS) {
533e5dd7070Spatrick if (CXXRecordDecl *RD = BS.getType()->getAsCXXRecordDecl())
534e5dd7070Spatrick return isEmptyCudaDestructor(Loc, RD->getDestructor());
535e5dd7070Spatrick return true;
536e5dd7070Spatrick }))
537e5dd7070Spatrick return false;
538e5dd7070Spatrick
539e5dd7070Spatrick // ... and member fields.
540e5dd7070Spatrick if (!llvm::all_of(ClassDecl->fields(), [&](const FieldDecl *Field) {
541e5dd7070Spatrick if (CXXRecordDecl *RD = Field->getType()
542e5dd7070Spatrick ->getBaseElementTypeUnsafe()
543e5dd7070Spatrick ->getAsCXXRecordDecl())
544e5dd7070Spatrick return isEmptyCudaDestructor(Loc, RD->getDestructor());
545e5dd7070Spatrick return true;
546e5dd7070Spatrick }))
547e5dd7070Spatrick return false;
548e5dd7070Spatrick
549e5dd7070Spatrick return true;
550e5dd7070Spatrick }
551e5dd7070Spatrick
552a9ac8606Spatrick namespace {
553a9ac8606Spatrick enum CUDAInitializerCheckKind {
554a9ac8606Spatrick CICK_DeviceOrConstant, // Check initializer for device/constant variable
555a9ac8606Spatrick CICK_Shared, // Check initializer for shared variable
556a9ac8606Spatrick };
557a9ac8606Spatrick
IsDependentVar(VarDecl * VD)558a9ac8606Spatrick bool IsDependentVar(VarDecl *VD) {
559a9ac8606Spatrick if (VD->getType()->isDependentType())
560a9ac8606Spatrick return true;
561a9ac8606Spatrick if (const auto *Init = VD->getInit())
562a9ac8606Spatrick return Init->isValueDependent();
563a9ac8606Spatrick return false;
564a9ac8606Spatrick }
565a9ac8606Spatrick
566a9ac8606Spatrick // Check whether a variable has an allowed initializer for a CUDA device side
567a9ac8606Spatrick // variable with global storage. \p VD may be a host variable to be checked for
568a9ac8606Spatrick // potential promotion to device side variable.
569a9ac8606Spatrick //
570a9ac8606Spatrick // CUDA/HIP allows only empty constructors as initializers for global
571a9ac8606Spatrick // variables (see E.2.3.1, CUDA 7.5). The same restriction also applies to all
572a9ac8606Spatrick // __shared__ variables whether they are local or not (they all are implicitly
573a9ac8606Spatrick // static in CUDA). One exception is that CUDA allows constant initializers
574a9ac8606Spatrick // for __constant__ and __device__ variables.
HasAllowedCUDADeviceStaticInitializer(Sema & S,VarDecl * VD,CUDAInitializerCheckKind CheckKind)575a9ac8606Spatrick bool HasAllowedCUDADeviceStaticInitializer(Sema &S, VarDecl *VD,
576a9ac8606Spatrick CUDAInitializerCheckKind CheckKind) {
577a9ac8606Spatrick assert(!VD->isInvalidDecl() && VD->hasGlobalStorage());
578a9ac8606Spatrick assert(!IsDependentVar(VD) && "do not check dependent var");
579a9ac8606Spatrick const Expr *Init = VD->getInit();
580a9ac8606Spatrick auto IsEmptyInit = [&](const Expr *Init) {
581a9ac8606Spatrick if (!Init)
582a9ac8606Spatrick return true;
583a9ac8606Spatrick if (const auto *CE = dyn_cast<CXXConstructExpr>(Init)) {
584a9ac8606Spatrick return S.isEmptyCudaConstructor(VD->getLocation(), CE->getConstructor());
585a9ac8606Spatrick }
586a9ac8606Spatrick return false;
587a9ac8606Spatrick };
588a9ac8606Spatrick auto IsConstantInit = [&](const Expr *Init) {
589a9ac8606Spatrick assert(Init);
590*12c85518Srobert ASTContext::CUDAConstantEvalContextRAII EvalCtx(S.Context,
591*12c85518Srobert /*NoWronSidedVars=*/true);
592a9ac8606Spatrick return Init->isConstantInitializer(S.Context,
593a9ac8606Spatrick VD->getType()->isReferenceType());
594a9ac8606Spatrick };
595a9ac8606Spatrick auto HasEmptyDtor = [&](VarDecl *VD) {
596a9ac8606Spatrick if (const auto *RD = VD->getType()->getAsCXXRecordDecl())
597a9ac8606Spatrick return S.isEmptyCudaDestructor(VD->getLocation(), RD->getDestructor());
598a9ac8606Spatrick return true;
599a9ac8606Spatrick };
600a9ac8606Spatrick if (CheckKind == CICK_Shared)
601a9ac8606Spatrick return IsEmptyInit(Init) && HasEmptyDtor(VD);
602a9ac8606Spatrick return S.LangOpts.GPUAllowDeviceInit ||
603a9ac8606Spatrick ((IsEmptyInit(Init) || IsConstantInit(Init)) && HasEmptyDtor(VD));
604a9ac8606Spatrick }
605a9ac8606Spatrick } // namespace
606a9ac8606Spatrick
checkAllowedCUDAInitializer(VarDecl * VD)607e5dd7070Spatrick void Sema::checkAllowedCUDAInitializer(VarDecl *VD) {
608a9ac8606Spatrick // Do not check dependent variables since the ctor/dtor/initializer are not
609a9ac8606Spatrick // determined. Do it after instantiation.
610a9ac8606Spatrick if (VD->isInvalidDecl() || !VD->hasInit() || !VD->hasGlobalStorage() ||
611a9ac8606Spatrick IsDependentVar(VD))
612e5dd7070Spatrick return;
613e5dd7070Spatrick const Expr *Init = VD->getInit();
614a9ac8606Spatrick bool IsSharedVar = VD->hasAttr<CUDASharedAttr>();
615a9ac8606Spatrick bool IsDeviceOrConstantVar =
616a9ac8606Spatrick !IsSharedVar &&
617a9ac8606Spatrick (VD->hasAttr<CUDADeviceAttr>() || VD->hasAttr<CUDAConstantAttr>());
618a9ac8606Spatrick if (IsDeviceOrConstantVar || IsSharedVar) {
619a9ac8606Spatrick if (HasAllowedCUDADeviceStaticInitializer(
620a9ac8606Spatrick *this, VD, IsSharedVar ? CICK_Shared : CICK_DeviceOrConstant))
621e5dd7070Spatrick return;
622a9ac8606Spatrick Diag(VD->getLocation(),
623a9ac8606Spatrick IsSharedVar ? diag::err_shared_var_init : diag::err_dynamic_var_init)
624e5dd7070Spatrick << Init->getSourceRange();
625e5dd7070Spatrick VD->setInvalidDecl();
626e5dd7070Spatrick } else {
627e5dd7070Spatrick // This is a host-side global variable. Check that the initializer is
628e5dd7070Spatrick // callable from the host side.
629e5dd7070Spatrick const FunctionDecl *InitFn = nullptr;
630e5dd7070Spatrick if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(Init)) {
631e5dd7070Spatrick InitFn = CE->getConstructor();
632e5dd7070Spatrick } else if (const CallExpr *CE = dyn_cast<CallExpr>(Init)) {
633e5dd7070Spatrick InitFn = CE->getDirectCallee();
634e5dd7070Spatrick }
635e5dd7070Spatrick if (InitFn) {
636e5dd7070Spatrick CUDAFunctionTarget InitFnTarget = IdentifyCUDATarget(InitFn);
637e5dd7070Spatrick if (InitFnTarget != CFT_Host && InitFnTarget != CFT_HostDevice) {
638e5dd7070Spatrick Diag(VD->getLocation(), diag::err_ref_bad_target_global_initializer)
639e5dd7070Spatrick << InitFnTarget << InitFn;
640e5dd7070Spatrick Diag(InitFn->getLocation(), diag::note_previous_decl) << InitFn;
641e5dd7070Spatrick VD->setInvalidDecl();
642e5dd7070Spatrick }
643e5dd7070Spatrick }
644e5dd7070Spatrick }
645e5dd7070Spatrick }
646e5dd7070Spatrick
647e5dd7070Spatrick // With -fcuda-host-device-constexpr, an unattributed constexpr function is
648e5dd7070Spatrick // treated as implicitly __host__ __device__, unless:
649e5dd7070Spatrick // * it is a variadic function (device-side variadic functions are not
650e5dd7070Spatrick // allowed), or
651e5dd7070Spatrick // * a __device__ function with this signature was already declared, in which
652e5dd7070Spatrick // case in which case we output an error, unless the __device__ decl is in a
653e5dd7070Spatrick // system header, in which case we leave the constexpr function unattributed.
654e5dd7070Spatrick //
655e5dd7070Spatrick // In addition, all function decls are treated as __host__ __device__ when
656e5dd7070Spatrick // ForceCUDAHostDeviceDepth > 0 (corresponding to code within a
657e5dd7070Spatrick // #pragma clang force_cuda_host_device_begin/end
658e5dd7070Spatrick // pair).
maybeAddCUDAHostDeviceAttrs(FunctionDecl * NewD,const LookupResult & Previous)659e5dd7070Spatrick void Sema::maybeAddCUDAHostDeviceAttrs(FunctionDecl *NewD,
660e5dd7070Spatrick const LookupResult &Previous) {
661e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
662e5dd7070Spatrick
663e5dd7070Spatrick if (ForceCUDAHostDeviceDepth > 0) {
664e5dd7070Spatrick if (!NewD->hasAttr<CUDAHostAttr>())
665e5dd7070Spatrick NewD->addAttr(CUDAHostAttr::CreateImplicit(Context));
666e5dd7070Spatrick if (!NewD->hasAttr<CUDADeviceAttr>())
667e5dd7070Spatrick NewD->addAttr(CUDADeviceAttr::CreateImplicit(Context));
668e5dd7070Spatrick return;
669e5dd7070Spatrick }
670e5dd7070Spatrick
671e5dd7070Spatrick if (!getLangOpts().CUDAHostDeviceConstexpr || !NewD->isConstexpr() ||
672e5dd7070Spatrick NewD->isVariadic() || NewD->hasAttr<CUDAHostAttr>() ||
673e5dd7070Spatrick NewD->hasAttr<CUDADeviceAttr>() || NewD->hasAttr<CUDAGlobalAttr>())
674e5dd7070Spatrick return;
675e5dd7070Spatrick
676e5dd7070Spatrick // Is D a __device__ function with the same signature as NewD, ignoring CUDA
677e5dd7070Spatrick // attributes?
678e5dd7070Spatrick auto IsMatchingDeviceFn = [&](NamedDecl *D) {
679e5dd7070Spatrick if (UsingShadowDecl *Using = dyn_cast<UsingShadowDecl>(D))
680e5dd7070Spatrick D = Using->getTargetDecl();
681e5dd7070Spatrick FunctionDecl *OldD = D->getAsFunction();
682e5dd7070Spatrick return OldD && OldD->hasAttr<CUDADeviceAttr>() &&
683e5dd7070Spatrick !OldD->hasAttr<CUDAHostAttr>() &&
684e5dd7070Spatrick !IsOverload(NewD, OldD, /* UseMemberUsingDeclRules = */ false,
685e5dd7070Spatrick /* ConsiderCudaAttrs = */ false);
686e5dd7070Spatrick };
687e5dd7070Spatrick auto It = llvm::find_if(Previous, IsMatchingDeviceFn);
688e5dd7070Spatrick if (It != Previous.end()) {
689e5dd7070Spatrick // We found a __device__ function with the same name and signature as NewD
690e5dd7070Spatrick // (ignoring CUDA attrs). This is an error unless that function is defined
691e5dd7070Spatrick // in a system header, in which case we simply return without making NewD
692e5dd7070Spatrick // host+device.
693e5dd7070Spatrick NamedDecl *Match = *It;
694e5dd7070Spatrick if (!getSourceManager().isInSystemHeader(Match->getLocation())) {
695e5dd7070Spatrick Diag(NewD->getLocation(),
696e5dd7070Spatrick diag::err_cuda_unattributed_constexpr_cannot_overload_device)
697e5dd7070Spatrick << NewD;
698e5dd7070Spatrick Diag(Match->getLocation(),
699e5dd7070Spatrick diag::note_cuda_conflicting_device_function_declared_here);
700e5dd7070Spatrick }
701e5dd7070Spatrick return;
702e5dd7070Spatrick }
703e5dd7070Spatrick
704e5dd7070Spatrick NewD->addAttr(CUDAHostAttr::CreateImplicit(Context));
705e5dd7070Spatrick NewD->addAttr(CUDADeviceAttr::CreateImplicit(Context));
706e5dd7070Spatrick }
707e5dd7070Spatrick
708a9ac8606Spatrick // TODO: `__constant__` memory may be a limited resource for certain targets.
709a9ac8606Spatrick // A safeguard may be needed at the end of compilation pipeline if
710a9ac8606Spatrick // `__constant__` memory usage goes beyond limit.
MaybeAddCUDAConstantAttr(VarDecl * VD)711ec727ea7Spatrick void Sema::MaybeAddCUDAConstantAttr(VarDecl *VD) {
712a9ac8606Spatrick // Do not promote dependent variables since the cotr/dtor/initializer are
713a9ac8606Spatrick // not determined. Do it after instantiation.
714a9ac8606Spatrick if (getLangOpts().CUDAIsDevice && !VD->hasAttr<CUDAConstantAttr>() &&
715*12c85518Srobert !VD->hasAttr<CUDASharedAttr>() &&
716a9ac8606Spatrick (VD->isFileVarDecl() || VD->isStaticDataMember()) &&
717a9ac8606Spatrick !IsDependentVar(VD) &&
718*12c85518Srobert ((VD->isConstexpr() || VD->getType().isConstQualified()) &&
719*12c85518Srobert HasAllowedCUDADeviceStaticInitializer(*this, VD,
720*12c85518Srobert CICK_DeviceOrConstant))) {
721ec727ea7Spatrick VD->addAttr(CUDAConstantAttr::CreateImplicit(getASTContext()));
722ec727ea7Spatrick }
723ec727ea7Spatrick }
724ec727ea7Spatrick
CUDADiagIfDeviceCode(SourceLocation Loc,unsigned DiagID)725a9ac8606Spatrick Sema::SemaDiagnosticBuilder Sema::CUDADiagIfDeviceCode(SourceLocation Loc,
726e5dd7070Spatrick unsigned DiagID) {
727e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
728*12c85518Srobert FunctionDecl *CurFunContext = getCurFunctionDecl(/*AllowLambda=*/true);
729a9ac8606Spatrick SemaDiagnosticBuilder::Kind DiagKind = [&] {
730*12c85518Srobert if (!CurFunContext)
731a9ac8606Spatrick return SemaDiagnosticBuilder::K_Nop;
732e5dd7070Spatrick switch (CurrentCUDATarget()) {
733e5dd7070Spatrick case CFT_Global:
734e5dd7070Spatrick case CFT_Device:
735a9ac8606Spatrick return SemaDiagnosticBuilder::K_Immediate;
736e5dd7070Spatrick case CFT_HostDevice:
737e5dd7070Spatrick // An HD function counts as host code if we're compiling for host, and
738e5dd7070Spatrick // device code if we're compiling for device. Defer any errors in device
739e5dd7070Spatrick // mode until the function is known-emitted.
740a9ac8606Spatrick if (!getLangOpts().CUDAIsDevice)
741a9ac8606Spatrick return SemaDiagnosticBuilder::K_Nop;
742a9ac8606Spatrick if (IsLastErrorImmediate && Diags.getDiagnosticIDs()->isBuiltinNote(DiagID))
743a9ac8606Spatrick return SemaDiagnosticBuilder::K_Immediate;
744*12c85518Srobert return (getEmissionStatus(CurFunContext) ==
745e5dd7070Spatrick FunctionEmissionStatus::Emitted)
746a9ac8606Spatrick ? SemaDiagnosticBuilder::K_ImmediateWithCallStack
747a9ac8606Spatrick : SemaDiagnosticBuilder::K_Deferred;
748e5dd7070Spatrick default:
749a9ac8606Spatrick return SemaDiagnosticBuilder::K_Nop;
750e5dd7070Spatrick }
751e5dd7070Spatrick }();
752*12c85518Srobert return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, CurFunContext, *this);
753e5dd7070Spatrick }
754e5dd7070Spatrick
CUDADiagIfHostCode(SourceLocation Loc,unsigned DiagID)755a9ac8606Spatrick Sema::SemaDiagnosticBuilder Sema::CUDADiagIfHostCode(SourceLocation Loc,
756e5dd7070Spatrick unsigned DiagID) {
757e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
758*12c85518Srobert FunctionDecl *CurFunContext = getCurFunctionDecl(/*AllowLambda=*/true);
759a9ac8606Spatrick SemaDiagnosticBuilder::Kind DiagKind = [&] {
760*12c85518Srobert if (!CurFunContext)
761a9ac8606Spatrick return SemaDiagnosticBuilder::K_Nop;
762e5dd7070Spatrick switch (CurrentCUDATarget()) {
763e5dd7070Spatrick case CFT_Host:
764a9ac8606Spatrick return SemaDiagnosticBuilder::K_Immediate;
765e5dd7070Spatrick case CFT_HostDevice:
766e5dd7070Spatrick // An HD function counts as host code if we're compiling for host, and
767e5dd7070Spatrick // device code if we're compiling for device. Defer any errors in device
768e5dd7070Spatrick // mode until the function is known-emitted.
769e5dd7070Spatrick if (getLangOpts().CUDAIsDevice)
770a9ac8606Spatrick return SemaDiagnosticBuilder::K_Nop;
771a9ac8606Spatrick if (IsLastErrorImmediate && Diags.getDiagnosticIDs()->isBuiltinNote(DiagID))
772a9ac8606Spatrick return SemaDiagnosticBuilder::K_Immediate;
773*12c85518Srobert return (getEmissionStatus(CurFunContext) ==
774e5dd7070Spatrick FunctionEmissionStatus::Emitted)
775a9ac8606Spatrick ? SemaDiagnosticBuilder::K_ImmediateWithCallStack
776a9ac8606Spatrick : SemaDiagnosticBuilder::K_Deferred;
777e5dd7070Spatrick default:
778a9ac8606Spatrick return SemaDiagnosticBuilder::K_Nop;
779e5dd7070Spatrick }
780e5dd7070Spatrick }();
781*12c85518Srobert return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, CurFunContext, *this);
782e5dd7070Spatrick }
783e5dd7070Spatrick
CheckCUDACall(SourceLocation Loc,FunctionDecl * Callee)784e5dd7070Spatrick bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) {
785e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
786e5dd7070Spatrick assert(Callee && "Callee may not be null.");
787e5dd7070Spatrick
788e5dd7070Spatrick auto &ExprEvalCtx = ExprEvalContexts.back();
789e5dd7070Spatrick if (ExprEvalCtx.isUnevaluated() || ExprEvalCtx.isConstantEvaluated())
790e5dd7070Spatrick return true;
791e5dd7070Spatrick
792e5dd7070Spatrick // FIXME: Is bailing out early correct here? Should we instead assume that
793e5dd7070Spatrick // the caller is a global initializer?
794*12c85518Srobert FunctionDecl *Caller = getCurFunctionDecl(/*AllowLambda=*/true);
795e5dd7070Spatrick if (!Caller)
796e5dd7070Spatrick return true;
797e5dd7070Spatrick
798e5dd7070Spatrick // If the caller is known-emitted, mark the callee as known-emitted.
799e5dd7070Spatrick // Otherwise, mark the call in our call graph so we can traverse it later.
800e5dd7070Spatrick bool CallerKnownEmitted =
801e5dd7070Spatrick getEmissionStatus(Caller) == FunctionEmissionStatus::Emitted;
802a9ac8606Spatrick SemaDiagnosticBuilder::Kind DiagKind = [this, Caller, Callee,
803e5dd7070Spatrick CallerKnownEmitted] {
804e5dd7070Spatrick switch (IdentifyCUDAPreference(Caller, Callee)) {
805e5dd7070Spatrick case CFP_Never:
806e5dd7070Spatrick case CFP_WrongSide:
807a9ac8606Spatrick assert(Caller && "Never/wrongSide calls require a non-null caller");
808e5dd7070Spatrick // If we know the caller will be emitted, we know this wrong-side call
809e5dd7070Spatrick // will be emitted, so it's an immediate error. Otherwise, defer the
810e5dd7070Spatrick // error until we know the caller is emitted.
811a9ac8606Spatrick return CallerKnownEmitted
812a9ac8606Spatrick ? SemaDiagnosticBuilder::K_ImmediateWithCallStack
813a9ac8606Spatrick : SemaDiagnosticBuilder::K_Deferred;
814e5dd7070Spatrick default:
815a9ac8606Spatrick return SemaDiagnosticBuilder::K_Nop;
816e5dd7070Spatrick }
817e5dd7070Spatrick }();
818e5dd7070Spatrick
819*12c85518Srobert if (DiagKind == SemaDiagnosticBuilder::K_Nop) {
820*12c85518Srobert // For -fgpu-rdc, keep track of external kernels used by host functions.
821*12c85518Srobert if (LangOpts.CUDAIsDevice && LangOpts.GPURelocatableDeviceCode &&
822*12c85518Srobert Callee->hasAttr<CUDAGlobalAttr>() && !Callee->isDefined())
823*12c85518Srobert getASTContext().CUDAExternalDeviceDeclODRUsedByHost.insert(Callee);
824e5dd7070Spatrick return true;
825*12c85518Srobert }
826e5dd7070Spatrick
827e5dd7070Spatrick // Avoid emitting this error twice for the same location. Using a hashtable
828e5dd7070Spatrick // like this is unfortunate, but because we must continue parsing as normal
829e5dd7070Spatrick // after encountering a deferred error, it's otherwise very tricky for us to
830e5dd7070Spatrick // ensure that we only emit this deferred error once.
831e5dd7070Spatrick if (!LocsWithCUDACallDiags.insert({Caller, Loc}).second)
832e5dd7070Spatrick return true;
833e5dd7070Spatrick
834a9ac8606Spatrick SemaDiagnosticBuilder(DiagKind, Loc, diag::err_ref_bad_target, Caller, *this)
835a9ac8606Spatrick << IdentifyCUDATarget(Callee) << /*function*/ 0 << Callee
836a9ac8606Spatrick << IdentifyCUDATarget(Caller);
837a9ac8606Spatrick if (!Callee->getBuiltinID())
838a9ac8606Spatrick SemaDiagnosticBuilder(DiagKind, Callee->getLocation(),
839a9ac8606Spatrick diag::note_previous_decl, Caller, *this)
840e5dd7070Spatrick << Callee;
841a9ac8606Spatrick return DiagKind != SemaDiagnosticBuilder::K_Immediate &&
842a9ac8606Spatrick DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack;
843e5dd7070Spatrick }
844e5dd7070Spatrick
845ec727ea7Spatrick // Check the wrong-sided reference capture of lambda for CUDA/HIP.
846ec727ea7Spatrick // A lambda function may capture a stack variable by reference when it is
847ec727ea7Spatrick // defined and uses the capture by reference when the lambda is called. When
848ec727ea7Spatrick // the capture and use happen on different sides, the capture is invalid and
849ec727ea7Spatrick // should be diagnosed.
CUDACheckLambdaCapture(CXXMethodDecl * Callee,const sema::Capture & Capture)850ec727ea7Spatrick void Sema::CUDACheckLambdaCapture(CXXMethodDecl *Callee,
851ec727ea7Spatrick const sema::Capture &Capture) {
852ec727ea7Spatrick // In host compilation we only need to check lambda functions emitted on host
853ec727ea7Spatrick // side. In such lambda functions, a reference capture is invalid only
854ec727ea7Spatrick // if the lambda structure is populated by a device function or kernel then
855ec727ea7Spatrick // is passed to and called by a host function. However that is impossible,
856ec727ea7Spatrick // since a device function or kernel can only call a device function, also a
857ec727ea7Spatrick // kernel cannot pass a lambda back to a host function since we cannot
858ec727ea7Spatrick // define a kernel argument type which can hold the lambda before the lambda
859ec727ea7Spatrick // itself is defined.
860ec727ea7Spatrick if (!LangOpts.CUDAIsDevice)
861ec727ea7Spatrick return;
862ec727ea7Spatrick
863ec727ea7Spatrick // File-scope lambda can only do init captures for global variables, which
864ec727ea7Spatrick // results in passing by value for these global variables.
865*12c85518Srobert FunctionDecl *Caller = getCurFunctionDecl(/*AllowLambda=*/true);
866ec727ea7Spatrick if (!Caller)
867ec727ea7Spatrick return;
868ec727ea7Spatrick
869ec727ea7Spatrick // In device compilation, we only need to check lambda functions which are
870ec727ea7Spatrick // emitted on device side. For such lambdas, a reference capture is invalid
871ec727ea7Spatrick // only if the lambda structure is populated by a host function then passed
872ec727ea7Spatrick // to and called in a device function or kernel.
873ec727ea7Spatrick bool CalleeIsDevice = Callee->hasAttr<CUDADeviceAttr>();
874ec727ea7Spatrick bool CallerIsHost =
875ec727ea7Spatrick !Caller->hasAttr<CUDAGlobalAttr>() && !Caller->hasAttr<CUDADeviceAttr>();
876ec727ea7Spatrick bool ShouldCheck = CalleeIsDevice && CallerIsHost;
877ec727ea7Spatrick if (!ShouldCheck || !Capture.isReferenceCapture())
878ec727ea7Spatrick return;
879a9ac8606Spatrick auto DiagKind = SemaDiagnosticBuilder::K_Deferred;
880ec727ea7Spatrick if (Capture.isVariableCapture()) {
881a9ac8606Spatrick SemaDiagnosticBuilder(DiagKind, Capture.getLocation(),
882ec727ea7Spatrick diag::err_capture_bad_target, Callee, *this)
883ec727ea7Spatrick << Capture.getVariable();
884ec727ea7Spatrick } else if (Capture.isThisCapture()) {
885*12c85518Srobert // Capture of this pointer is allowed since this pointer may be pointing to
886*12c85518Srobert // managed memory which is accessible on both device and host sides. It only
887*12c85518Srobert // results in invalid memory access if this pointer points to memory not
888*12c85518Srobert // accessible on device side.
889a9ac8606Spatrick SemaDiagnosticBuilder(DiagKind, Capture.getLocation(),
890*12c85518Srobert diag::warn_maybe_capture_bad_target_this_ptr, Callee,
891*12c85518Srobert *this);
892ec727ea7Spatrick }
893ec727ea7Spatrick }
894ec727ea7Spatrick
CUDASetLambdaAttrs(CXXMethodDecl * Method)895e5dd7070Spatrick void Sema::CUDASetLambdaAttrs(CXXMethodDecl *Method) {
896e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
897e5dd7070Spatrick if (Method->hasAttr<CUDAHostAttr>() || Method->hasAttr<CUDADeviceAttr>())
898e5dd7070Spatrick return;
899e5dd7070Spatrick Method->addAttr(CUDADeviceAttr::CreateImplicit(Context));
900e5dd7070Spatrick Method->addAttr(CUDAHostAttr::CreateImplicit(Context));
901e5dd7070Spatrick }
902e5dd7070Spatrick
checkCUDATargetOverload(FunctionDecl * NewFD,const LookupResult & Previous)903e5dd7070Spatrick void Sema::checkCUDATargetOverload(FunctionDecl *NewFD,
904e5dd7070Spatrick const LookupResult &Previous) {
905e5dd7070Spatrick assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
906e5dd7070Spatrick CUDAFunctionTarget NewTarget = IdentifyCUDATarget(NewFD);
907e5dd7070Spatrick for (NamedDecl *OldND : Previous) {
908e5dd7070Spatrick FunctionDecl *OldFD = OldND->getAsFunction();
909e5dd7070Spatrick if (!OldFD)
910e5dd7070Spatrick continue;
911e5dd7070Spatrick
912e5dd7070Spatrick CUDAFunctionTarget OldTarget = IdentifyCUDATarget(OldFD);
913e5dd7070Spatrick // Don't allow HD and global functions to overload other functions with the
914e5dd7070Spatrick // same signature. We allow overloading based on CUDA attributes so that
915e5dd7070Spatrick // functions can have different implementations on the host and device, but
916e5dd7070Spatrick // HD/global functions "exist" in some sense on both the host and device, so
917e5dd7070Spatrick // should have the same implementation on both sides.
918e5dd7070Spatrick if (NewTarget != OldTarget &&
919e5dd7070Spatrick ((NewTarget == CFT_HostDevice) || (OldTarget == CFT_HostDevice) ||
920e5dd7070Spatrick (NewTarget == CFT_Global) || (OldTarget == CFT_Global)) &&
921e5dd7070Spatrick !IsOverload(NewFD, OldFD, /* UseMemberUsingDeclRules = */ false,
922e5dd7070Spatrick /* ConsiderCudaAttrs = */ false)) {
923e5dd7070Spatrick Diag(NewFD->getLocation(), diag::err_cuda_ovl_target)
924e5dd7070Spatrick << NewTarget << NewFD->getDeclName() << OldTarget << OldFD;
925e5dd7070Spatrick Diag(OldFD->getLocation(), diag::note_previous_declaration);
926e5dd7070Spatrick NewFD->setInvalidDecl();
927e5dd7070Spatrick break;
928e5dd7070Spatrick }
929e5dd7070Spatrick }
930e5dd7070Spatrick }
931e5dd7070Spatrick
932e5dd7070Spatrick template <typename AttrTy>
copyAttrIfPresent(Sema & S,FunctionDecl * FD,const FunctionDecl & TemplateFD)933e5dd7070Spatrick static void copyAttrIfPresent(Sema &S, FunctionDecl *FD,
934e5dd7070Spatrick const FunctionDecl &TemplateFD) {
935e5dd7070Spatrick if (AttrTy *Attribute = TemplateFD.getAttr<AttrTy>()) {
936e5dd7070Spatrick AttrTy *Clone = Attribute->clone(S.Context);
937e5dd7070Spatrick Clone->setInherited(true);
938e5dd7070Spatrick FD->addAttr(Clone);
939e5dd7070Spatrick }
940e5dd7070Spatrick }
941e5dd7070Spatrick
inheritCUDATargetAttrs(FunctionDecl * FD,const FunctionTemplateDecl & TD)942e5dd7070Spatrick void Sema::inheritCUDATargetAttrs(FunctionDecl *FD,
943e5dd7070Spatrick const FunctionTemplateDecl &TD) {
944e5dd7070Spatrick const FunctionDecl &TemplateFD = *TD.getTemplatedDecl();
945e5dd7070Spatrick copyAttrIfPresent<CUDAGlobalAttr>(*this, FD, TemplateFD);
946e5dd7070Spatrick copyAttrIfPresent<CUDAHostAttr>(*this, FD, TemplateFD);
947e5dd7070Spatrick copyAttrIfPresent<CUDADeviceAttr>(*this, FD, TemplateFD);
948e5dd7070Spatrick }
949e5dd7070Spatrick
getCudaConfigureFuncName() const950e5dd7070Spatrick std::string Sema::getCudaConfigureFuncName() const {
951e5dd7070Spatrick if (getLangOpts().HIP)
952e5dd7070Spatrick return getLangOpts().HIPUseNewLaunchAPI ? "__hipPushCallConfiguration"
953e5dd7070Spatrick : "hipConfigureCall";
954e5dd7070Spatrick
955e5dd7070Spatrick // New CUDA kernel launch sequence.
956e5dd7070Spatrick if (CudaFeatureEnabled(Context.getTargetInfo().getSDKVersion(),
957e5dd7070Spatrick CudaFeature::CUDA_USES_NEW_LAUNCH))
958e5dd7070Spatrick return "__cudaPushCallConfiguration";
959e5dd7070Spatrick
960e5dd7070Spatrick // Legacy CUDA kernel configuration call
961e5dd7070Spatrick return "cudaConfigureCall";
962e5dd7070Spatrick }
963