diff --git a/clang/docs/OpenMPSupport.rst b/clang/docs/OpenMPSupport.rst index 8a627d1f0b92d..8af4af8105455 100644 --- a/clang/docs/OpenMPSupport.rst +++ b/clang/docs/OpenMPSupport.rst @@ -578,6 +578,7 @@ implementation. | | | | | | | | | Flang parser: https://github.com/llvm/llvm-project/pull/153807 | | | | | Flang sema: https://github.com/llvm/llvm-project/pull/154779 | +| | | | Clang parse/sema: https://github.com/llvm/llvm-project/pull/158134 | +-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+ | variable-category on default clause | :good:`done` | :none:`unclaimed` | | +-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 60dd2b4c0c6ef..4d7779e9a87d5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -565,6 +565,7 @@ OpenMP Support - Added support for ``variable-category`` modifier in ``default clause``. - Added support for ``defaultmap`` directive implicit-behavior ``storage``. - Added support for ``defaultmap`` directive implicit-behavior ``private``. +- Added parsing and semantic analysis support for ``groupprivate`` directive. Improvements ^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h index ab0a539f84e42..352af42391782 100644 --- a/clang/include/clang/AST/ASTMutationListener.h +++ b/clang/include/clang/AST/ASTMutationListener.h @@ -125,6 +125,12 @@ class ASTMutationListener { /// \param D the declaration marked OpenMP threadprivate. virtual void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) {} + /// A declaration is marked as OpenMP groupprivate which was not + /// previously marked as groupprivate. + /// + /// \param D the declaration marked OpenMP groupprivate. + virtual void DeclarationMarkedOpenMPGroupPrivate(const Decl *D) {} + /// A declaration is marked as OpenMP declaretarget which was not /// previously marked as declaretarget. /// diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index ea68cc70f9131..092160405aff4 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -620,6 +620,11 @@ class ASTNodeTraverser Visit(E); } + void VisitOMPGroupPrivateDecl(const OMPGroupPrivateDecl *D) { + for (const auto *E : D->varlist()) + Visit(E); + } + void VisitOMPDeclareReductionDecl(const OMPDeclareReductionDecl *D) { Visit(D->getCombiner()); if (const auto *Initializer = D->getInitializer()) diff --git a/clang/include/clang/AST/DeclOpenMP.h b/clang/include/clang/AST/DeclOpenMP.h index f3e18ad0339af..06414cef6baf3 100644 --- a/clang/include/clang/AST/DeclOpenMP.h +++ b/clang/include/clang/AST/DeclOpenMP.h @@ -158,6 +158,68 @@ class OMPThreadPrivateDecl final : public OMPDeclarativeDirective { static bool classofKind(Kind K) { return K == OMPThreadPrivate; } }; +/// This represents '#pragma omp groupprivate ...' directive. +/// For example, in the following, both 'a' and 'A::b' are groupprivate: +/// +/// \code +/// int a; +/// #pragma omp groupprivate(a) +/// struct A { +/// static int b; +/// #pragma omp groupprivate(b) +/// }; +/// \endcode +/// +class OMPGroupPrivateDecl final : public OMPDeclarativeDirective { + friend class OMPDeclarativeDirective; + + LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION(); + + OMPGroupPrivateDecl(DeclContext *DC = nullptr, + SourceLocation L = SourceLocation()) + : OMPDeclarativeDirective(OMPGroupPrivate, DC, L) {} + + ArrayRef getVars() const { + auto **Storage = reinterpret_cast(Data->getChildren().data()); + return {Storage, Data->getNumChildren()}; + } + + MutableArrayRef getVars() { + auto **Storage = reinterpret_cast(Data->getChildren().data()); + return {Storage, Data->getNumChildren()}; + } + + void setVars(ArrayRef VL); + +public: + static OMPGroupPrivateDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation L, ArrayRef VL); + static OMPGroupPrivateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID, + unsigned N); + + typedef MutableArrayRef::iterator varlist_iterator; + typedef ArrayRef::iterator varlist_const_iterator; + typedef llvm::iterator_range varlist_range; + typedef llvm::iterator_range varlist_const_range; + + unsigned varlist_size() const { return Data->getNumChildren(); } + bool varlist_empty() const { return Data->getChildren().empty(); } + + varlist_range varlist() { + return varlist_range(varlist_begin(), varlist_end()); + } + varlist_const_range varlist() const { + return varlist_const_range(varlist_begin(), varlist_end()); + } + varlist_iterator varlist_begin() { return getVars().begin(); } + varlist_iterator varlist_end() { return getVars().end(); } + varlist_const_iterator varlist_begin() const { return getVars().begin(); } + varlist_const_iterator varlist_end() const { return getVars().end(); } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == OMPGroupPrivate; } +}; + enum class OMPDeclareReductionInitKind { Call, // Initialized by function call. Direct, // omp_priv() diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index c1944487716de..1d1b7f183f75a 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1887,6 +1887,12 @@ DEF_TRAVERSE_DECL(OMPThreadPrivateDecl, { } }) +DEF_TRAVERSE_DECL(OMPGroupPrivateDecl, { + for (auto *I : D->varlist()) { + TRY_TO(TraverseStmt(I)); + } +}) + DEF_TRAVERSE_DECL(OMPRequiresDecl, { for (auto *C : D->clauselists()) { TRY_TO(TraverseOMPClause(C)); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index cdaed4a176735..2623f9ff6972f 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4585,6 +4585,12 @@ def OMPThreadPrivateDecl : InheritableAttr { let Documentation = [InternalOnly]; } +def OMPGroupPrivateDecl : InheritableAttr { + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + def OMPCaptureNoInit : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly. let Spellings = []; diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 8d6731b50f509..04311055bb600 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -106,6 +106,7 @@ def OutlinedFunction : DeclNode, DeclContext; def Captured : DeclNode, DeclContext; def Import : DeclNode; def OMPThreadPrivate : DeclNode; +def OMPGroupPrivate : DeclNode; def OMPAllocate : DeclNode; def OMPRequires : DeclNode; def Empty : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 409a8202d8a09..8b862ae47af89 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11708,6 +11708,10 @@ def err_omp_threadprivate_incomplete_type : Error< "threadprivate variable with incomplete type %0">; def err_omp_no_dsa_for_variable : Error< "variable %0 must have explicitly specified data sharing attributes">; +def err_omp_groupprivate_incomplete_type : Error< + "groupprivate variable with incomplete type %0">; +def err_omp_groupprivate_with_initializer : Error< + "variable %0 with initializer cannot appear in groupprivate directive">; def err_omp_defaultmap_no_attr_for_variable : Error< "variable %0 must have explicitly specified data sharing attributes, data mapping attributes, or in an is_device_ptr clause">; def note_omp_default_dsa_none : Note< diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 23827051ed724..c0fd7a6d63611 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -227,6 +227,12 @@ class SemaOpenMP : public SemaBase { /// Builds a new OpenMPThreadPrivateDecl and checks its correctness. OMPThreadPrivateDecl *CheckOMPThreadPrivateDecl(SourceLocation Loc, ArrayRef VarList); + /// Called on well-formed '#pragma omp groupprivate'. + DeclGroupPtrTy ActOnOpenMPGroupPrivateDirective(SourceLocation Loc, + ArrayRef VarList); + /// Builds a new OpenMPGroupPrivateDecl and checks its correctness. + OMPGroupPrivateDecl *CheckOMPGroupPrivateDecl(SourceLocation Loc, + ArrayRef VarList); /// Called on well-formed '#pragma omp allocate'. DeclGroupPtrTy ActOnOpenMPAllocateDirective(SourceLocation Loc, ArrayRef VarList, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 680a4d74171a9..b244f0a6e6a95 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -986,6 +986,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case ObjCCategoryImpl: case Import: case OMPThreadPrivate: + case OMPGroupPrivate: case OMPAllocate: case OMPRequires: case OMPCapturedExpr: diff --git a/clang/lib/AST/DeclOpenMP.cpp b/clang/lib/AST/DeclOpenMP.cpp index 32c82f614d6f2..ef08a1c30042f 100644 --- a/clang/lib/AST/DeclOpenMP.cpp +++ b/clang/lib/AST/DeclOpenMP.cpp @@ -48,6 +48,34 @@ void OMPThreadPrivateDecl::setVars(ArrayRef VL) { llvm::copy(VL, getVars().begin()); } +//===----------------------------------------------------------------------===// +// OMPGroupPrivateDecl Implementation. +//===----------------------------------------------------------------------===// + +void OMPGroupPrivateDecl::anchor() {} + +OMPGroupPrivateDecl *OMPGroupPrivateDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, + ArrayRef VL) { + auto *D = OMPDeclarativeDirective::createDirective( + C, DC, {}, VL.size(), L); + D->setVars(VL); + return D; +} + +OMPGroupPrivateDecl *OMPGroupPrivateDecl::CreateDeserialized(ASTContext &C, + GlobalDeclID ID, + unsigned N) { + return OMPDeclarativeDirective::createEmptyDirective( + C, ID, 0, N); +} + +void OMPGroupPrivateDecl::setVars(ArrayRef VL) { + assert(VL.size() == Data->getNumChildren() && + "Number of variables is not the same as the preallocated buffer"); + llvm::copy(VL, getVars().begin()); +} + //===----------------------------------------------------------------------===// // OMPAllocateDecl Implementation. //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 69d33019c0952..11a43e8c7a030 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -124,6 +124,7 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_untied: case OMPC_mergeable: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_flush: case OMPC_depobj: case OMPC_read: @@ -222,6 +223,7 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C) case OMPC_untied: case OMPC_mergeable: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_flush: case OMPC_depobj: case OMPC_read: diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index ea913d766ba57..508685883364c 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -221,6 +221,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str, } case OMPC_unknown: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_if: case OMPC_final: case OMPC_safelen: @@ -565,6 +566,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, llvm_unreachable("Invalid OpenMP 'num_threads' clause modifier"); case OMPC_unknown: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_if: case OMPC_final: case OMPC_safelen: diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 4e735f6d28f34..8b1cd83af2396 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -131,6 +131,7 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) { case Decl::UnnamedGlobalConstant: case Decl::TemplateParamObject: case Decl::OMPThreadPrivate: + case Decl::OMPGroupPrivate: case Decl::OMPAllocate: case Decl::OMPCapturedExpr: case Decl::OMPRequires: diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 7dceb2d208352..7073f76030f45 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2009,6 +2009,19 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( } break; } + case OMPD_groupprivate: { + ConsumeToken(); + DeclDirectiveListParserHelper Helper(this, DKind); + if (!ParseOpenMPSimpleVarList(DKind, Helper, + /*AllowScopeSpecifier=*/true)) { + skipUntilPragmaOpenMPEnd(DKind); + // Skip the last annot_pragma_openmp_end. + ConsumeAnnotationToken(); + return Actions.OpenMP().ActOnOpenMPGroupPrivateDirective( + Loc, Helper.getIdentifiers()); + } + break; + } case OMPD_allocate: { ConsumeToken(); DeclDirectiveListParserHelper Helper(this, DKind); @@ -2731,6 +2744,24 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( SkipUntil(tok::annot_pragma_openmp_end); break; } + case OMPD_groupprivate: { + if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) == + ParsedStmtContext()) { + Diag(Tok, diag::err_omp_immediate_directive) + << getOpenMPDirectiveName(DKind, OMPVersion) << 0; + } + ConsumeToken(); + DeclDirectiveListParserHelper Helper(this, DKind); + if (!ParseOpenMPSimpleVarList(DKind, Helper, + /*AllowScopeSpecifier=*/false)) { + skipUntilPragmaOpenMPEnd(DKind); + DeclGroupPtrTy Res = Actions.OpenMP().ActOnOpenMPGroupPrivateDirective( + Loc, Helper.getIdentifiers()); + Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); + } + SkipUntil(tok::annot_pragma_openmp_end); + break; + } case OMPD_allocate: { // FIXME: Should this be permitted in C++? if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) == @@ -3285,6 +3316,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, skipUntilPragmaOpenMPEnd(DKind); break; case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_uniform: case OMPC_match: if (!WrongDirective) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 6a7a5a9a4303a..3175905b1f6ae 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -249,6 +249,7 @@ class DSAStackTy { /// Stack of used declaration and their data-sharing attributes. DeclSAMapTy Threadprivates; + DeclSAMapTy Groupprivates; const FunctionScopeInfo *CurrentNonCapturingFunctionScope = nullptr; SmallVector, 4> Stack; /// true, if check for DSA must be from parent directive, false, if @@ -1561,6 +1562,12 @@ void DSAStackTy::addDSA(const ValueDecl *D, const Expr *E, OpenMPClauseKind A, Data.RefExpr.setPointer(E); Data.PrivateCopy = nullptr; Data.Modifier = Modifier; + } else if (A == OMPC_groupprivate) { + DSAInfo &Data = Groupprivates[D]; + Data.Attributes = A; + Data.RefExpr.setPointer(E); + Data.PrivateCopy = nullptr; + Data.Modifier = Modifier; } else { DSAInfo &Data = getTopOfStack().SharingMap[D]; assert(Data.Attributes == OMPC_unknown || (A == Data.Attributes) || @@ -3169,7 +3176,8 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, // OpenMP [2.9.2, Syntax, C/C++] // Variables must be file-scope, namespace-scope, or static block-scope. - if (Kind == OMPD_threadprivate && !VD->hasGlobalStorage()) { + if ((Kind == OMPD_threadprivate || Kind == OMPD_groupprivate) && + !VD->hasGlobalStorage()) { Diag(Id.getLoc(), diag::err_omp_global_var_arg) << getOpenMPDirectiveName(Kind, OMPVersion) << !VD->isStaticLocal(); bool IsDecl = @@ -3183,8 +3191,8 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, VarDecl *CanonicalVD = VD->getCanonicalDecl(); NamedDecl *ND = CanonicalVD; // OpenMP [2.9.2, Restrictions, C/C++, p.2] - // A threadprivate directive for file-scope variables must appear outside - // any definition or declaration. + // A threadprivate or groupprivate directive for file-scope variables must + // appear outside any definition or declaration. if (CanonicalVD->getDeclContext()->isTranslationUnit() && !SemaRef.getCurLexicalContext()->isTranslationUnit()) { Diag(Id.getLoc(), diag::err_omp_var_scope) @@ -3197,9 +3205,9 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, return ExprError(); } // OpenMP [2.9.2, Restrictions, C/C++, p.3] - // A threadprivate directive for static class member variables must appear - // in the class definition, in the same scope in which the member - // variables are declared. + // A threadprivate or groupprivate directive for static class member + // variables must appear in the class definition, in the same scope in which + // the member variables are declared. if (CanonicalVD->isStaticDataMember() && !CanonicalVD->getDeclContext()->Equals(SemaRef.getCurLexicalContext())) { Diag(Id.getLoc(), diag::err_omp_var_scope) @@ -3212,9 +3220,9 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, return ExprError(); } // OpenMP [2.9.2, Restrictions, C/C++, p.4] - // A threadprivate directive for namespace-scope variables must appear - // outside any definition or declaration other than the namespace - // definition itself. + // A threadprivate or groupprivate directive for namespace-scope variables + // must appear outside any definition or declaration other than the + // namespace definition itself. if (CanonicalVD->getDeclContext()->isNamespace() && (!SemaRef.getCurLexicalContext()->isFileContext() || !SemaRef.getCurLexicalContext()->Encloses( @@ -3229,8 +3237,9 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, return ExprError(); } // OpenMP [2.9.2, Restrictions, C/C++, p.6] - // A threadprivate directive for static block-scope variables must appear - // in the scope of the variable and not in a nested scope. + // A threadprivate or groupprivate directive for static block-scope + // variables must appear in the scope of the variable and not in a nested + // scope. if (CanonicalVD->isLocalVarDecl() && CurScope && !SemaRef.isDeclInScope(ND, SemaRef.getCurLexicalContext(), CurScope)) { Diag(Id.getLoc(), diag::err_omp_var_scope) @@ -3244,10 +3253,11 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, } // OpenMP [2.9.2, Restrictions, C/C++, p.2-6] - // A threadprivate directive must lexically precede all references to any - // of the variables in its list. - if (Kind == OMPD_threadprivate && VD->isUsed() && - !DSAStack->isThreadPrivate(VD)) { + // A threadprivate or groupprivate directive must lexically precede all + // references to any of the variables in its list. + if ((Kind == OMPD_threadprivate && VD->isUsed() && + !DSAStack->isThreadPrivate(VD)) || + (Kind == OMPD_groupprivate && VD->isUsed())) { Diag(Id.getLoc(), diag::err_omp_var_used) << getOpenMPDirectiveName(Kind, OMPVersion) << VD; return ExprError(); @@ -3270,6 +3280,21 @@ SemaOpenMP::ActOnOpenMPThreadprivateDirective(SourceLocation Loc, return nullptr; } +SemaOpenMP::DeclGroupPtrTy +SemaOpenMP::ActOnOpenMPGroupPrivateDirective(SourceLocation Loc, + ArrayRef VarList) { + if (!getLangOpts().OpenMP || getLangOpts().OpenMP < 60) { + Diag(Loc, diag::err_omp_unexpected_directive) + << getOpenMPDirectiveName(OMPD_groupprivate, getLangOpts().OpenMP); + return nullptr; + } + if (OMPGroupPrivateDecl *D = CheckOMPGroupPrivateDecl(Loc, VarList)) { + SemaRef.CurContext->addDecl(D); + return DeclGroupPtrTy::make(DeclGroupRef(D)); + } + return nullptr; +} + namespace { class LocalVarRefChecker final : public ConstStmtVisitor { @@ -3385,6 +3410,75 @@ SemaOpenMP::CheckOMPThreadPrivateDecl(SourceLocation Loc, return D; } +OMPGroupPrivateDecl * +SemaOpenMP::CheckOMPGroupPrivateDecl(SourceLocation Loc, + ArrayRef VarList) { + ASTContext &Context = getASTContext(); + SmallVector Vars; + for (Expr *RefExpr : VarList) { + auto *DE = cast(RefExpr); + auto *VD = cast(DE->getDecl()); + SourceLocation ILoc = DE->getExprLoc(); + + // Mark variable as used. + VD->setReferenced(); + VD->markUsed(Context); + + QualType QType = VD->getType(); + if (QType->isDependentType() || QType->isInstantiationDependentType()) { + // It will be analyzed later. + Vars.push_back(DE); + continue; + } + + // OpenMP groupprivate restrictions: + // A groupprivate variable must not have an incomplete type. + if (SemaRef.RequireCompleteType( + ILoc, VD->getType(), diag::err_omp_groupprivate_incomplete_type)) { + continue; + } + + // A groupprivate variable must not have a reference type. + if (VD->getType()->isReferenceType()) { + Diag(ILoc, diag::err_omp_ref_type_arg) + << getOpenMPDirectiveName(OMPD_groupprivate) << VD->getType(); + bool IsDecl = + VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly; + Diag(VD->getLocation(), + IsDecl ? diag::note_previous_decl : diag::note_defined_here) + << VD; + continue; + } + + // A variable that is declared with an initializer must not appear in a + // groupprivate directive. + if (VD->getAnyInitializer()) { + Diag(ILoc, diag::err_omp_groupprivate_with_initializer) + << VD->getDeclName(); + bool IsDecl = + VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly; + Diag(VD->getLocation(), + IsDecl ? diag::note_previous_decl : diag::note_defined_here) + << VD; + continue; + } + + Vars.push_back(RefExpr); + DSAStack->addDSA(VD, DE, OMPC_groupprivate); + VD->addAttr(OMPGroupPrivateDeclAttr::CreateImplicit(Context, + SourceRange(Loc, Loc))); + if (ASTMutationListener *ML = Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPGroupPrivate(VD); + } + OMPGroupPrivateDecl *D = nullptr; + if (!Vars.empty()) { + D = OMPGroupPrivateDecl::Create(Context, SemaRef.getCurLexicalContext(), + Loc, Vars); + D->setAccess(AS_public); + } + return D; +} + static OMPAllocateDeclAttr::AllocatorTypeTy getAllocatorKind(Sema &S, DSAStackTy *Stack, Expr *Allocator) { if (!Allocator) @@ -6722,6 +6816,7 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective( case OMPC_flush: case OMPC_depobj: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_uniform: case OMPC_unknown: case OMPC_unified_address: @@ -15705,6 +15800,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, case OMPC_untied: case OMPC_mergeable: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_sizes: case OMPC_allocate: case OMPC_flush: @@ -16391,6 +16487,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSimpleClause( case OMPC_untied: case OMPC_mergeable: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_allocate: case OMPC_flush: case OMPC_depobj: @@ -16904,6 +17001,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSingleExprWithArgClause( case OMPC_untied: case OMPC_mergeable: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_allocate: case OMPC_flush: case OMPC_depobj: @@ -17187,6 +17285,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPClause(OpenMPClauseKind Kind, case OMPC_default: case OMPC_proc_bind: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_allocate: case OMPC_flush: case OMPC_depobj: @@ -17816,6 +17915,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPVarListClause(OpenMPClauseKind Kind, case OMPC_untied: case OMPC_mergeable: case OMPC_threadprivate: + case OMPC_groupprivate: case OMPC_read: case OMPC_write: case OMPC_update: diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index df1a100cab22c..adac3dff5b2b4 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4175,6 +4175,24 @@ Decl *TemplateDeclInstantiator::VisitOMPThreadPrivateDecl( return TD; } +Decl * +TemplateDeclInstantiator::VisitOMPGroupPrivateDecl(OMPGroupPrivateDecl *D) { + SmallVector Vars; + for (auto *I : D->varlist()) { + Expr *Var = SemaRef.SubstExpr(I, TemplateArgs).get(); + assert(isa(Var) && "groupprivate arg is not a DeclRefExpr"); + Vars.push_back(Var); + } + + OMPGroupPrivateDecl *TD = + SemaRef.OpenMP().CheckOMPGroupPrivateDecl(D->getLocation(), Vars); + + TD->setAccess(AS_public); + Owner->addDecl(TD); + + return TD; +} + Decl *TemplateDeclInstantiator::VisitOMPAllocateDecl(OMPAllocateDecl *D) { SmallVector Vars; for (auto *I : D->varlist()) { diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 097da3668857c..69db02f2efc40 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -441,6 +441,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Captured: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPGroupPrivate: case Decl::OMPAllocate: case Decl::OMPRequires: case Decl::OMPCapturedExpr: diff --git a/clang/test/OpenMP/groupprivate_ast_print.cpp b/clang/test/OpenMP/groupprivate_ast_print.cpp new file mode 100644 index 0000000000000..0ffff0d37c941 --- /dev/null +++ b/clang/test/OpenMP/groupprivate_ast_print.cpp @@ -0,0 +1,57 @@ +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -triple x86_64-apple-darwin10.6.0 -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=60 -triple x86_64-unknown-linux-gnu -ast-dump %s | FileCheck %s + +// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=60 -triple x86_64-apple-darwin10.6.0 -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=60 -triple x86_64-unknown-linux-gnu -ast-dump %s | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +struct St{ + int a; +}; + +struct St1{ + int a; + static int b; +#pragma omp groupprivate(b) +}; +// CHECK: VarDecl {{.*}} b 'int' static +// CHECK-NEXT: OMPGroupPrivateDeclAttr +// CHECK-NEXT: OMPGroupPrivateDecl +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'b' 'int' + +int a, b; +#pragma omp groupprivate(a) +#pragma omp groupprivate(b) +// CHECK: VarDecl {{.*}} a 'int' +// CHECK-NEXT: OMPGroupPrivateDeclAttr +// CHECK: VarDecl {{.*}} b 'int' +// CHECK-NEXT: OMPGroupPrivateDeclAttr +// CHECK-NEXT: OMPGroupPrivateDecl +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'a' 'int' +// CHECK-NEXT: OMPGroupPrivateDecl +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'b' 'int' + +template T foo() { + static T v; + #pragma omp groupprivate(v) + return v; +} +// CHECK: OMPGroupPrivateDecl +// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue Var {{.*}} 'v' 'T' + +int main () { + static int a; +#pragma omp groupprivate(a) + a=2; + return (foo()); +} +// CHECK: VarDecl {{.*}} a 'int' static +// CHECK-NEXT: OMPGroupPrivateDeclAttr +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: OMPGroupPrivateDecl +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'a' 'int' + +#endif diff --git a/clang/test/OpenMP/groupprivate_messages.cpp b/clang/test/OpenMP/groupprivate_messages.cpp new file mode 100644 index 0000000000000..aefa290795c41 --- /dev/null +++ b/clang/test/OpenMP/groupprivate_messages.cpp @@ -0,0 +1,111 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -verify -fopenmp -fopenmp-version=60 -ferror-limit 100 -emit-llvm -o - %s +// RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -verify -fopenmp -fopenmp-version=60 -ferror-limit 100 -emit-llvm -o - %s + +// RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -verify -fopenmp-simd -fopenmp-version=60 -ferror-limit 100 -emit-llvm -o - %s +// RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -verify -fopenmp-simd -fopenmp-version=60 -ferror-limit 100 -emit-llvm -o - %s + +#pragma omp groupprivate // expected-error {{expected '(' after 'groupprivate'}} +#pragma omp groupprivate( // expected-error {{expected identifier}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp groupprivate() // expected-error {{expected identifier}} +#pragma omp groupprivate(1) // expected-error {{expected unqualified-id}} +struct CompleteSt{ + int a; +}; + +struct CompleteSt1{ +#pragma omp groupprivate(1) // expected-error {{expected unqualified-id}} + int a; +} d; // expected-note {{'d' defined here}} + +int a; // expected-note {{'a' defined here}} + +#pragma omp groupprivate(a) allocate(a) // expected-warning {{extra tokens at the end of '#pragma omp groupprivate' are ignored}} +#pragma omp groupprivate(u) // expected-error {{use of undeclared identifier 'u'}} +int foo() { // expected-note {{declared here}} + static int l; +#pragma omp groupprivate(l)) // expected-warning {{extra tokens at the end of '#pragma omp groupprivate' are ignored}} + return (a); +} + +int x, y; +#pragma omp groupprivate(x)) // expected-warning {{extra tokens at the end of '#pragma omp groupprivate' are ignored}} +#pragma omp groupprivate(y)), +// expected-warning@-1 {{extra tokens at the end of '#pragma omp groupprivate' are ignored}} +#pragma omp groupprivate(d.a) // expected-error {{expected identifier}} +#pragma omp groupprivate((float)a) // expected-error {{expected unqualified-id}} +int foa; // expected-note {{'foa' declared here}} +#pragma omp groupprivate(faa) // expected-error {{use of undeclared identifier 'faa'; did you mean 'foa'?}} +#pragma omp groupprivate(foo) // expected-error {{'foo' is not a global variable, static local variable or static data member}} +#pragma omp groupprivate (int a=2) // expected-error {{expected unqualified-id}} + +struct IncompleteSt; // expected-note {{forward declaration of 'IncompleteSt'}} + +extern IncompleteSt e; +#pragma omp groupprivate (e) // expected-error {{groupprivate variable with incomplete type 'IncompleteSt'}} + +int &f = a; // expected-note {{'f' defined here}} +#pragma omp groupprivate (f) // expected-error {{arguments of '#pragma omp groupprivate' cannot be of reference type 'int &'}} + +class TestClass { + private: + int a; // expected-note {{declared here}} + static int b; // expected-note {{'b' declared here}} + TestClass() : a(0){} + public: + TestClass (int aaa) : a(aaa) {} +#pragma omp groupprivate (b, a) // expected-error {{'a' is not a global variable, static local variable or static data member}} +} g(10); +#pragma omp groupprivate (b) // expected-error {{use of undeclared identifier 'b'}} +#pragma omp groupprivate (TestClass::b) // expected-error {{'#pragma omp groupprivate' must appear in the scope of the 'TestClass::b' variable declaration}} + +const int h = 12; // expected-note {{'h' defined here}} +const volatile int i = 10; // expected-note {{'i' defined here}} +// For groupprivate these have initializers -> groupprivate forbids variables with initializers. +#pragma omp groupprivate (h, i) // expected-error {{variable 'h' with initializer cannot appear in groupprivate directive}} expected-error {{variable 'i' with initializer cannot appear in groupprivate directive}} + +template +class TempClass { + private: + T a; + TempClass() : a(){} + public: + TempClass (T aaa) : a(aaa) {} + static T s; +#pragma omp groupprivate (s) +}; +#pragma omp groupprivate (s) // expected-error {{use of undeclared identifier 's'}} + +int o; // expected-note {{candidate found by name lookup is 'o'}} +#pragma omp groupprivate (o) +namespace { +int o; // expected-note {{candidate found by name lookup is '(anonymous namespace)::o'}} +#pragma omp groupprivate (o) +} +#pragma omp groupprivate (o) // expected-error {{reference to 'o' is ambiguous}} + +int main(int argc, char **argv) { + + int x, y = argc; + static double d1; + static double d2; + static double d3; // expected-note {{'d3' defined here}} + static double d4; + + d.a = a; + d2++; + ; +#pragma omp groupprivate(argc+y) // expected-error {{expected identifier}} +#pragma omp groupprivate(d2) // expected-error {{'#pragma omp groupprivate' must precede all references to variable 'd2'}} +#pragma omp groupprivate(d1) + { + ++a;d2=0; +#pragma omp groupprivate(d3) // expected-error {{'#pragma omp groupprivate' must appear in the scope of the 'd3' variable declaration}} + } +#pragma omp groupprivate(d3) +label: +#pragma omp groupprivate(d4) // expected-error {{'#pragma omp groupprivate' cannot be an immediate substatement}} + +#pragma omp groupprivate(a) // expected-error {{'#pragma omp groupprivate' must appear in the scope of the 'a' variable declaration}} + return (y); +#pragma omp groupprivate(d) // expected-error {{'#pragma omp groupprivate' must appear in the scope of the 'd' variable declaration}} +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 9526f629bda42..63da874d5af0c 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -7213,6 +7213,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::CXXDeductionGuide: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPGroupPrivate: case Decl::OMPAllocate: case Decl::OMPDeclareReduction: case Decl::OMPDeclareMapper: diff --git a/flang/include/flang/Lower/OpenMP/Clauses.h b/flang/include/flang/Lower/OpenMP/Clauses.h index 18e2f209c2d7a..5267a58c7e7f7 100644 --- a/flang/include/flang/Lower/OpenMP/Clauses.h +++ b/flang/include/flang/Lower/OpenMP/Clauses.h @@ -328,11 +328,14 @@ struct MemoryOrder { struct Threadprivate { using EmptyTrait = std::true_type; }; +struct Groupprivate { + using EmptyTrait = std::true_type; +}; using ClauseBase = tomp::ClauseT; + MemoryOrder, Threadprivate, Groupprivate>; struct Clause : public ClauseBase { Clause(ClauseBase &&base, const parser::CharBlock source = {}) diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp index 78fe5aa031ba1..42b62413f4a26 100644 --- a/flang/lib/Lower/OpenMP/Clauses.cpp +++ b/flang/lib/Lower/OpenMP/Clauses.cpp @@ -254,6 +254,7 @@ MAKE_EMPTY_CLASS(Depobj, Depobj); MAKE_EMPTY_CLASS(Flush, Flush); MAKE_EMPTY_CLASS(MemoryOrder, MemoryOrder); MAKE_EMPTY_CLASS(Threadprivate, Threadprivate); +MAKE_EMPTY_CLASS(Groupprivate, Groupprivate); MAKE_INCOMPLETE_CLASS(AdjustArgs, AdjustArgs); MAKE_INCOMPLETE_CLASS(AppendArgs, AppendArgs); diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 7d3fd7a699ff5..4c7cd1734e0e7 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -2787,6 +2787,7 @@ CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks) CHECK_SIMPLE_CLAUSE(Order, OMPC_order) CHECK_SIMPLE_CLAUSE(Read, OMPC_read) CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) +CHECK_SIMPLE_CLAUSE(Groupprivate, OMPC_groupprivate) CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) CHECK_SIMPLE_CLAUSE(Link, OMPC_link) diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 6a41c24e78149..4d9b8f8a6c51e 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -520,6 +520,9 @@ def OMPC_ThreadLimit : Clause<[Spelling<"thread_limit">]> { def OMPC_ThreadPrivate : Clause<[Spelling<"threadprivate">]> { let isImplicit = true; } +def OMPC_GroupPrivate : Clause<[Spelling<"groupprivate">]> { + let isImplicit = true; +} def OMPC_Threads : Clause<[Spelling<"threads">]> { let clangClass = "OMPThreadsClause"; }