[clang] [llvm] [clang] [OpenMP] New OpenMP 6.0 - Parsing and Sema support for groupprivate (PR #158134)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 12 09:54:23 PDT 2025
https://github.com/Ritanya-B-Bharadwaj updated https://github.com/llvm/llvm-project/pull/158134
>From a9dbb90836fb5f99ce282445a893a10bfaa74903 Mon Sep 17 00:00:00 2001
From: Ritanya B Bharadwaj <ritanya.b.bharadwaj at gmail.com>
Date: Thu, 11 Sep 2025 13:49:20 -0500
Subject: [PATCH] Parsing and Sema support for groupprivate
---
clang/docs/OpenMPSupport.rst | 2 +-
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/AST/ASTMutationListener.h | 6 +
clang/include/clang/AST/ASTNodeTraverser.h | 5 +
clang/include/clang/AST/DeclOpenMP.h | 62 +++++++++
clang/include/clang/AST/RecursiveASTVisitor.h | 6 +
clang/include/clang/Basic/Attr.td | 6 +
clang/include/clang/Basic/DeclNodes.td | 1 +
.../clang/Basic/DiagnosticSemaKinds.td | 4 +
clang/include/clang/Sema/SemaOpenMP.h | 6 +
clang/lib/AST/DeclBase.cpp | 1 +
clang/lib/AST/DeclOpenMP.cpp | 28 ++++
clang/lib/AST/OpenMPClause.cpp | 2 +
clang/lib/Basic/OpenMPKinds.cpp | 2 +
clang/lib/Parse/ParseOpenMP.cpp | 32 +++++
clang/lib/Sema/SemaOpenMP.cpp | 130 ++++++++++++++++--
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 18 +++
clang/test/OpenMP/groupprivate_ast_print.cpp | 57 ++++++++
clang/test/OpenMP/groupprivate_messages.cpp | 111 +++++++++++++++
llvm/include/llvm/Frontend/OpenMP/OMP.td | 7 +
20 files changed, 471 insertions(+), 16 deletions(-)
create mode 100644 clang/test/OpenMP/groupprivate_ast_print.cpp
create mode 100644 clang/test/OpenMP/groupprivate_messages.cpp
diff --git a/clang/docs/OpenMPSupport.rst b/clang/docs/OpenMPSupport.rst
index 76b6a72fec721..d457f5a9aa077 100644
--- a/clang/docs/OpenMPSupport.rst
+++ b/clang/docs/OpenMPSupport.rst
@@ -476,7 +476,7 @@ implementation.
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| Local clause on declare target | :part:`In Progress` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| groupprivate directive | :part:`In Progress` | :none:`unclaimed` | |
+| groupprivate directive | :part:`partial` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| variable-category on default clause | :part:`In Progress` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 031a196bbc83f..66154269abfc1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -342,6 +342,7 @@ OpenMP Support
modifier in the ``adjust_args`` clause.
- Allow array length to be omitted in array section subscript expression.
- Fixed non-contiguous strided update in the ``omp target update`` directive with the ``from`` clause.
+- 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 2c4ec2ce67f36..2c6989f254467 100644
--- a/clang/include/clang/AST/ASTMutationListener.h
+++ b/clang/include/clang/AST/ASTMutationListener.h
@@ -121,6 +121,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 d9dc8290b0e49..f0b16bf667c8d 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -625,6 +625,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<Decl> {
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<Decl> {
+ friend class OMPDeclarativeDirective<Decl>;
+
+ LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION();
+
+ OMPGroupPrivateDecl(DeclContext *DC = nullptr,
+ SourceLocation L = SourceLocation())
+ : OMPDeclarativeDirective<Decl>(OMPGroupPrivate, DC, L) {}
+
+ ArrayRef<const Expr *> getVars() const {
+ auto **Storage = reinterpret_cast<Expr **>(Data->getChildren().data());
+ return {Storage, Data->getNumChildren()};
+ }
+
+ MutableArrayRef<Expr *> getVars() {
+ auto **Storage = reinterpret_cast<Expr **>(Data->getChildren().data());
+ return {Storage, Data->getNumChildren()};
+ }
+
+ void setVars(ArrayRef<Expr *> VL);
+
+public:
+ static OMPGroupPrivateDecl *Create(ASTContext &C, DeclContext *DC,
+ SourceLocation L, ArrayRef<Expr *> VL);
+ static OMPGroupPrivateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+ unsigned N);
+
+ typedef MutableArrayRef<Expr *>::iterator varlist_iterator;
+ typedef ArrayRef<const Expr *>::iterator varlist_const_iterator;
+ typedef llvm::iterator_range<varlist_iterator> varlist_range;
+ typedef llvm::iterator_range<varlist_const_iterator> 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(<expr>)
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 248b89200eace..9e2a4503fcbe7 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1883,6 +1883,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 a9fa4a8f07454..f37bfae3c0d7c 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4551,6 +4551,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<Decl>, DeclContext;
def Captured : DeclNode<Decl>, DeclContext;
def Import : DeclNode<Decl>;
def OMPThreadPrivate : DeclNode<Decl>;
+def OMPGroupPrivate : DeclNode<Decl>;
def OMPAllocate : DeclNode<Decl>;
def OMPRequires : DeclNode<Decl>;
def Empty : DeclNode<Decl>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a7f3d37823075..45f5820d94ead 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11667,6 +11667,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 91c3d4bd5210e..0f23186097491 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<Expr *> VarList);
+ /// Called on well-formed '#pragma omp groupprivate'.
+ DeclGroupPtrTy ActOnOpenMPGroupPrivateDirective(SourceLocation Loc,
+ ArrayRef<Expr *> VarList);
+ /// Builds a new OpenMPGroupPrivateDecl and checks its correctness.
+ OMPGroupPrivateDecl *CheckOMPGroupPrivateDecl(SourceLocation Loc,
+ ArrayRef<Expr *> VarList);
/// Called on well-formed '#pragma omp allocate'.
DeclGroupPtrTy ActOnOpenMPAllocateDirective(SourceLocation Loc,
ArrayRef<Expr *> 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<Expr *> VL) {
llvm::copy(VL, getVars().begin());
}
+//===----------------------------------------------------------------------===//
+// OMPGroupPrivateDecl Implementation.
+//===----------------------------------------------------------------------===//
+
+void OMPGroupPrivateDecl::anchor() {}
+
+OMPGroupPrivateDecl *OMPGroupPrivateDecl::Create(ASTContext &C, DeclContext *DC,
+ SourceLocation L,
+ ArrayRef<Expr *> VL) {
+ auto *D = OMPDeclarativeDirective::createDirective<OMPGroupPrivateDecl>(
+ C, DC, {}, VL.size(), L);
+ D->setVars(VL);
+ return D;
+}
+
+OMPGroupPrivateDecl *OMPGroupPrivateDecl::CreateDeserialized(ASTContext &C,
+ GlobalDeclID ID,
+ unsigned N) {
+ return OMPDeclarativeDirective::createEmptyDirective<OMPGroupPrivateDecl>(
+ C, ID, 0, N);
+}
+
+void OMPGroupPrivateDecl::setVars(ArrayRef<Expr *> 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 588b0dcc6d7b8..16c27b8a73393 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -122,6 +122,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:
@@ -221,6 +222,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 220b31b0f19bc..0803a2276bfa0 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -196,6 +196,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:
@@ -540,6 +541,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/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 5db2f2e2ccf86..d26761f648b3b 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 7d800c446b595..a08984e372aff 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -234,6 +234,7 @@ class DSAStackTy {
/// Stack of used declaration and their data-sharing attributes.
DeclSAMapTy Threadprivates;
+ DeclSAMapTy Groupprivates;
const FunctionScopeInfo *CurrentNonCapturingFunctionScope = nullptr;
SmallVector<std::pair<StackTy, const FunctionScopeInfo *>, 4> Stack;
/// true, if check for DSA must be from parent directive, false, if
@@ -1497,6 +1498,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) ||
@@ -3105,7 +3112,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 =
@@ -3119,8 +3127,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)
@@ -3133,9 +3141,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)
@@ -3148,9 +3156,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(
@@ -3165,8 +3173,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)
@@ -3180,10 +3189,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();
@@ -3206,6 +3216,21 @@ SemaOpenMP::ActOnOpenMPThreadprivateDirective(SourceLocation Loc,
return nullptr;
}
+SemaOpenMP::DeclGroupPtrTy
+SemaOpenMP::ActOnOpenMPGroupPrivateDirective(SourceLocation Loc,
+ ArrayRef<Expr *> 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<LocalVarRefChecker, bool> {
@@ -3321,6 +3346,75 @@ SemaOpenMP::CheckOMPThreadPrivateDecl(SourceLocation Loc,
return D;
}
+OMPGroupPrivateDecl *
+SemaOpenMP::CheckOMPGroupPrivateDecl(SourceLocation Loc,
+ ArrayRef<Expr *> VarList) {
+ ASTContext &Context = getASTContext();
+ SmallVector<Expr *, 8> Vars;
+ for (Expr *RefExpr : VarList) {
+ auto *DE = cast<DeclRefExpr>(RefExpr);
+ auto *VD = cast<VarDecl>(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)
@@ -6627,6 +6721,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:
@@ -15602,6 +15697,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:
@@ -16292,6 +16388,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:
@@ -16753,6 +16850,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:
@@ -17036,6 +17134,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:
@@ -17664,6 +17763,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 f02a295220efb..75de38cd4b390 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4137,6 +4137,24 @@ Decl *TemplateDeclInstantiator::VisitOMPThreadPrivateDecl(
return TD;
}
+Decl *
+TemplateDeclInstantiator::VisitOMPGroupPrivateDecl(OMPGroupPrivateDecl *D) {
+ SmallVector<Expr *, 5> Vars;
+ for (auto *I : D->varlist()) {
+ Expr *Var = SemaRef.SubstExpr(I, TemplateArgs).get();
+ assert(isa<DeclRefExpr>(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<Expr *, 5> Vars;
for (auto *I : D->varlist()) {
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 <class T> 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<int>());
+}
+// 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 at -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 T>
+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/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 79f25bb05f20e..2a6815328e946 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -506,6 +506,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";
}
@@ -873,6 +876,10 @@ def OMP_For : Directive<[Spelling<"for">]> {
let category = CA_Executable;
let languages = [L_C];
}
+def OMP_Groupprivate : Directive<[Spelling<"groupprivate">]> {
+ let association = AS_None;
+ let category = CA_Declarative;
+}
def OMP_Interchange : Directive<[Spelling<"interchange">]> {
let allowedOnceClauses = [
VersionedClause<OMPC_Permutation>,
More information about the llvm-commits
mailing list