diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaCUDA.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaCUDA.cpp | 120 |
1 files changed, 88 insertions, 32 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaCUDA.cpp b/contrib/llvm-project/clang/lib/Sema/SemaCUDA.cpp index 0c61057e1072..283a04683a32 100644 --- a/contrib/llvm-project/clang/lib/Sema/SemaCUDA.cpp +++ b/contrib/llvm-project/clang/lib/Sema/SemaCUDA.cpp @@ -14,8 +14,10 @@ #include "clang/AST/Decl.h" #include "clang/AST/ExprCXX.h" #include "clang/Basic/Cuda.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/SemaInternal.h" @@ -210,6 +212,20 @@ Sema::IdentifyCUDAPreference(const FunctionDecl *Caller, llvm_unreachable("All cases should've been handled by now."); } +template <typename AttrT> static bool hasImplicitAttr(const FunctionDecl *D) { + if (!D) + return false; + if (auto *A = D->getAttr<AttrT>()) + return A->isImplicit(); + return D->isImplicit(); +} + +bool Sema::isCUDAImplicitHostDeviceFunction(const FunctionDecl *D) { + bool IsImplicitDevAttr = hasImplicitAttr<CUDADeviceAttr>(D); + bool IsImplicitHostAttr = hasImplicitAttr<CUDAHostAttr>(D); + return IsImplicitDevAttr && IsImplicitHostAttr; +} + void Sema::EraseUnwantedCUDAMatches( const FunctionDecl *Caller, SmallVectorImpl<std::pair<DeclAccessPair, FunctionDecl *>> &Matches) { @@ -425,6 +441,10 @@ bool Sema::isEmptyCudaConstructor(SourceLocation Loc, CXXConstructorDecl *CD) { if (CD->getParent()->isDynamicClass()) return false; + // Union ctor does not call ctors of its data members. + if (CD->getParent()->isUnion()) + return true; + // The only form of initializer allowed is an empty constructor. // This will recursively check all base classes and member initializers if (!llvm::all_of(CD->inits(), [&](const CXXCtorInitializer *CI) { @@ -464,6 +484,11 @@ bool Sema::isEmptyCudaDestructor(SourceLocation Loc, CXXDestructorDecl *DD) { if (ClassDecl->isDynamicClass()) return false; + // Union does not have base class and union dtor does not call dtors of its + // data members. + if (DD->getParent()->isUnion()) + return true; + // Only empty destructors are allowed. This will recursively check // destructors for all base classes... if (!llvm::all_of(ClassDecl->bases(), [&](const CXXBaseSpecifier &BS) { @@ -503,9 +528,14 @@ void Sema::checkAllowedCUDAInitializer(VarDecl *VD) { // constructor according to CUDA rules. This deviates from NVCC, // but allows us to handle things like constexpr constructors. if (!AllowedInit && - (VD->hasAttr<CUDADeviceAttr>() || VD->hasAttr<CUDAConstantAttr>())) - AllowedInit = VD->getInit()->isConstantInitializer( - Context, VD->getType()->isReferenceType()); + (VD->hasAttr<CUDADeviceAttr>() || VD->hasAttr<CUDAConstantAttr>())) { + auto *Init = VD->getInit(); + AllowedInit = + ((VD->getType()->isDependentType() || Init->isValueDependent()) && + VD->isConstexpr()) || + Init->isConstantInitializer(Context, + VD->getType()->isReferenceType()); + } // Also make sure that destructor, if there is one, is empty. if (AllowedInit) @@ -602,6 +632,13 @@ void Sema::maybeAddCUDAHostDeviceAttrs(FunctionDecl *NewD, NewD->addAttr(CUDADeviceAttr::CreateImplicit(Context)); } +void Sema::MaybeAddCUDAConstantAttr(VarDecl *VD) { + if (getLangOpts().CUDAIsDevice && VD->isConstexpr() && + (VD->isFileVarDecl() || VD->isStaticDataMember())) { + VD->addAttr(CUDAConstantAttr::CreateImplicit(getASTContext())); + } +} + Sema::DeviceDiagBuilder Sema::CUDADiagIfDeviceCode(SourceLocation Loc, unsigned DiagID) { assert(getLangOpts().CUDA && "Should only be called during CUDA compilation"); @@ -674,25 +711,6 @@ bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) { // Otherwise, mark the call in our call graph so we can traverse it later. bool CallerKnownEmitted = getEmissionStatus(Caller) == FunctionEmissionStatus::Emitted; - if (CallerKnownEmitted) { - // Host-side references to a __global__ function refer to the stub, so the - // function itself is never emitted and therefore should not be marked. - if (!shouldIgnoreInHostDeviceCheck(Callee)) - markKnownEmitted( - *this, Caller, Callee, Loc, [](Sema &S, FunctionDecl *FD) { - return S.getEmissionStatus(FD) == FunctionEmissionStatus::Emitted; - }); - } else { - // If we have - // host fn calls kernel fn calls host+device, - // the HD function does not get instantiated on the host. We model this by - // omitting at the call to the kernel from the callgraph. This ensures - // that, when compiling for host, only HD functions actually called from the - // host get marked as known-emitted. - if (!shouldIgnoreInHostDeviceCheck(Callee)) - DeviceCallGraph[Caller].insert({Callee, Loc}); - } - DeviceDiagBuilder::Kind DiagKind = [this, Caller, Callee, CallerKnownEmitted] { switch (IdentifyCUDAPreference(Caller, Callee)) { @@ -729,20 +747,58 @@ bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) { DiagKind != DeviceDiagBuilder::K_ImmediateWithCallStack; } +// Check the wrong-sided reference capture of lambda for CUDA/HIP. +// A lambda function may capture a stack variable by reference when it is +// defined and uses the capture by reference when the lambda is called. When +// the capture and use happen on different sides, the capture is invalid and +// should be diagnosed. +void Sema::CUDACheckLambdaCapture(CXXMethodDecl *Callee, + const sema::Capture &Capture) { + // In host compilation we only need to check lambda functions emitted on host + // side. In such lambda functions, a reference capture is invalid only + // if the lambda structure is populated by a device function or kernel then + // is passed to and called by a host function. However that is impossible, + // since a device function or kernel can only call a device function, also a + // kernel cannot pass a lambda back to a host function since we cannot + // define a kernel argument type which can hold the lambda before the lambda + // itself is defined. + if (!LangOpts.CUDAIsDevice) + return; + + // File-scope lambda can only do init captures for global variables, which + // results in passing by value for these global variables. + FunctionDecl *Caller = dyn_cast<FunctionDecl>(CurContext); + if (!Caller) + return; + + // In device compilation, we only need to check lambda functions which are + // emitted on device side. For such lambdas, a reference capture is invalid + // only if the lambda structure is populated by a host function then passed + // to and called in a device function or kernel. + bool CalleeIsDevice = Callee->hasAttr<CUDADeviceAttr>(); + bool CallerIsHost = + !Caller->hasAttr<CUDAGlobalAttr>() && !Caller->hasAttr<CUDADeviceAttr>(); + bool ShouldCheck = CalleeIsDevice && CallerIsHost; + if (!ShouldCheck || !Capture.isReferenceCapture()) + return; + auto DiagKind = DeviceDiagBuilder::K_Deferred; + if (Capture.isVariableCapture()) { + DeviceDiagBuilder(DiagKind, Capture.getLocation(), + diag::err_capture_bad_target, Callee, *this) + << Capture.getVariable(); + } else if (Capture.isThisCapture()) { + DeviceDiagBuilder(DiagKind, Capture.getLocation(), + diag::err_capture_bad_target_this_ptr, Callee, *this); + } + return; +} + void Sema::CUDASetLambdaAttrs(CXXMethodDecl *Method) { assert(getLangOpts().CUDA && "Should only be called during CUDA compilation"); if (Method->hasAttr<CUDAHostAttr>() || Method->hasAttr<CUDADeviceAttr>()) return; - FunctionDecl *CurFn = dyn_cast<FunctionDecl>(CurContext); - if (!CurFn) - return; - CUDAFunctionTarget Target = IdentifyCUDATarget(CurFn); - if (Target == CFT_Global || Target == CFT_Device) { - Method->addAttr(CUDADeviceAttr::CreateImplicit(Context)); - } else if (Target == CFT_HostDevice) { - Method->addAttr(CUDADeviceAttr::CreateImplicit(Context)); - Method->addAttr(CUDAHostAttr::CreateImplicit(Context)); - } + Method->addAttr(CUDADeviceAttr::CreateImplicit(Context)); + Method->addAttr(CUDAHostAttr::CreateImplicit(Context)); } void Sema::checkCUDATargetOverload(FunctionDecl *NewFD, |