From d195bc38fd424b0c928e3c354038a8ca6e2ccac3 Mon Sep 17 00:00:00 2001 From: Alexey Bataev <a.bataev@hotmail.com> Date: Tue, 1 Oct 2013 05:32:34 +0000 Subject: [PATCH] [OpenMP] Added parsing and semantic analysis for firstprivate clause git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191730 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 7 + include/clang/AST/StmtOpenMP.h | 61 +++++ include/clang/Basic/DiagnosticSemaKinds.td | 2 + include/clang/Basic/OpenMPKinds.def | 2 + include/clang/Sema/Sema.h | 5 + lib/AST/Stmt.cpp | 23 ++ lib/AST/StmtPrinter.cpp | 8 + lib/AST/StmtProfile.cpp | 4 + lib/Basic/OpenMPKinds.cpp | 2 + lib/Parse/ParseOpenMP.cpp | 5 +- lib/Sema/SemaOpenMP.cpp | 254 ++++++++++++++++-- lib/Sema/TreeTransform.h | 32 +++ lib/Serialization/ASTReaderStmt.cpp | 13 + lib/Serialization/ASTWriterStmt.cpp | 9 + test/OpenMP/parallel_ast_print.cpp | 40 ++- .../OpenMP/parallel_firstprivate_messages.cpp | 82 ++++++ test/OpenMP/parallel_private_messages.cpp | 8 + test/OpenMP/parallel_shared_messages.cpp | 6 + tools/libclang/CIndex.cpp | 4 + tools/libclang/RecursiveASTVisitor.h | 7 + 20 files changed, 554 insertions(+), 20 deletions(-) create mode 100644 test/OpenMP/parallel_firstprivate_messages.cpp diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 3400e1f323d..d09550f0e2c 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -2366,6 +2366,13 @@ bool RecursiveASTVisitor<Derived>::VisitOMPPrivateClause(OMPPrivateClause *C) { return true; } +template<typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPFirstprivateClause( + OMPFirstprivateClause *C) { + VisitOMPClauseList(C); + return true; +} + template<typename Derived> bool RecursiveASTVisitor<Derived>::VisitOMPSharedClause(OMPSharedClause *C) { VisitOMPClauseList(C); diff --git a/include/clang/AST/StmtOpenMP.h b/include/clang/AST/StmtOpenMP.h index 57097e994c2..8570d8850a2 100644 --- a/include/clang/AST/StmtOpenMP.h +++ b/include/clang/AST/StmtOpenMP.h @@ -249,6 +249,67 @@ public: } }; +/// \brief This represents clause 'firstprivate' in the '#pragma omp ...' +/// directives. +/// +/// \code +/// #pragma omp parallel firstprivate(a,b) +/// \endcode +/// In this example directive '#pragma omp parallel' has clause 'firstprivate' +/// with the variables 'a' and 'b'. +/// +class OMPFirstprivateClause : public OMPClause, + public OMPVarList<OMPFirstprivateClause> { + /// \brief Build clause with number of variables \a N. + /// + /// \param StartLoc Starting location of the clause. + /// \param LParenLoc Location of '('. + /// \param EndLoc Ending location of the clause. + /// \param N Number of the variables in the clause. + /// + OMPFirstprivateClause(SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc, unsigned N) + : OMPClause(OMPC_firstprivate, StartLoc, EndLoc), + OMPVarList<OMPFirstprivateClause>(LParenLoc, N) { } + + /// \brief Build an empty clause. + /// + /// \param N Number of variables. + /// + explicit OMPFirstprivateClause(unsigned N) + : OMPClause(OMPC_firstprivate, SourceLocation(), SourceLocation()), + OMPVarList<OMPFirstprivateClause>(SourceLocation(), N) { } +public: + /// \brief Creates clause with a list of variables \a VL. + /// + /// \param C AST context. + /// \param StartLoc Starting location of the clause. + /// \param LParenLoc Location of '('. + /// \param EndLoc Ending location of the clause. + /// \param VL List of references to the variables. + /// + static OMPFirstprivateClause *Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, + ArrayRef<Expr *> VL); + /// \brief Creates an empty clause with the place for \a N variables. + /// + /// \param C AST context. + /// \param N The number of variables. + /// + static OMPFirstprivateClause *CreateEmpty(const ASTContext &C, unsigned N); + + StmtRange children() { + return StmtRange(reinterpret_cast<Stmt **>(varlist_begin()), + reinterpret_cast<Stmt **>(varlist_end())); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == OMPC_firstprivate; + } +}; + /// \brief This represents clause 'shared' in the '#pragma omp ...' directives. /// /// \code diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 014cce37f41..87ce732cf65 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -6627,6 +6627,8 @@ def err_omp_var_thread_local : Error< "variable %0 cannot be threadprivate because it is thread-local">; def err_omp_private_incomplete_type : Error< "a private variable with incomplete type %0">; +def err_omp_firstprivate_incomplete_type : Error< + "a firstprivate variable with incomplete type %0">; def err_omp_unexpected_clause_value : Error < "expected %0 in OpenMP clause '%1'">; def err_omp_expected_var_name : Error < diff --git a/include/clang/Basic/OpenMPKinds.def b/include/clang/Basic/OpenMPKinds.def index e7e58c26999..6d1a7b27b74 100644 --- a/include/clang/Basic/OpenMPKinds.def +++ b/include/clang/Basic/OpenMPKinds.def @@ -33,11 +33,13 @@ OPENMP_DIRECTIVE(task) // OpenMP clauses. OPENMP_CLAUSE(default, OMPDefaultClause) OPENMP_CLAUSE(private, OMPPrivateClause) +OPENMP_CLAUSE(firstprivate, OMPFirstprivateClause) OPENMP_CLAUSE(shared, OMPSharedClause) // Clauses allowed for OpenMP directives. OPENMP_PARALLEL_CLAUSE(default) OPENMP_PARALLEL_CLAUSE(private) +OPENMP_PARALLEL_CLAUSE(firstprivate) OPENMP_PARALLEL_CLAUSE(shared) // Static attributes for 'default' clause. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 378245585ab..d9af5b6d78c 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7027,6 +7027,11 @@ public: SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// \brief Called on well-formed 'firstprivate' clause. + OMPClause *ActOnOpenMPFirstprivateClause(ArrayRef<Expr *> VarList, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); /// \brief Called on well-formed 'shared' clause. OMPClause *ActOnOpenMPSharedClause(ArrayRef<Expr *> VarList, SourceLocation StartLoc, diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 03215927440..de851615cb7 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -1145,6 +1145,29 @@ OMPPrivateClause *OMPPrivateClause::CreateEmpty(const ASTContext &C, return new (Mem) OMPPrivateClause(N); } +OMPFirstprivateClause *OMPFirstprivateClause::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, + ArrayRef<Expr *> VL) { + void *Mem = C.Allocate(sizeof(OMPFirstprivateClause) + + sizeof(Expr *) * VL.size(), + llvm::alignOf<OMPFirstprivateClause>()); + OMPFirstprivateClause *Clause = new (Mem) OMPFirstprivateClause(StartLoc, + LParenLoc, + EndLoc, + VL.size()); + Clause->setVarRefs(VL); + return Clause; +} + +OMPFirstprivateClause *OMPFirstprivateClause::CreateEmpty(const ASTContext &C, + unsigned N) { + void *Mem = C.Allocate(sizeof(OMPFirstprivateClause) + sizeof(Expr *) * N, + llvm::alignOf<OMPFirstprivateClause>()); + return new (Mem) OMPFirstprivateClause(N); +} + OMPSharedClause *OMPSharedClause::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 1bbd4667fc5..55cfd3f8134 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -619,6 +619,14 @@ void OMPClausePrinter::VisitOMPPrivateClause(OMPPrivateClause *Node) { } } +void OMPClausePrinter::VisitOMPFirstprivateClause(OMPFirstprivateClause *Node) { + if (!Node->varlist_empty()) { + OS << "firstprivate"; + VisitOMPClauseList(Node, '('); + OS << ")"; + } +} + void OMPClausePrinter::VisitOMPSharedClause(OMPSharedClause *Node) { if (!Node->varlist_empty()) { OS << "shared"; diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index 35f37ddaa79..6805e62befb 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -278,6 +278,10 @@ void OMPClauseProfiler::VisitOMPClauseList(T *Node) { void OMPClauseProfiler::VisitOMPPrivateClause(const OMPPrivateClause *C) { VisitOMPClauseList(C); } +void OMPClauseProfiler::VisitOMPFirstprivateClause( + const OMPFirstprivateClause *C) { + VisitOMPClauseList(C); +} void OMPClauseProfiler::VisitOMPSharedClause(const OMPSharedClause *C) { VisitOMPClauseList(C); } diff --git a/lib/Basic/OpenMPKinds.cpp b/lib/Basic/OpenMPKinds.cpp index 89a4faa7d09..1350934d0e6 100644 --- a/lib/Basic/OpenMPKinds.cpp +++ b/lib/Basic/OpenMPKinds.cpp @@ -78,6 +78,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, case OMPC_unknown: case OMPC_threadprivate: case OMPC_private: + case OMPC_firstprivate: case OMPC_shared: case NUM_OPENMP_CLAUSES: break; @@ -100,6 +101,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_unknown: case OMPC_threadprivate: case OMPC_private: + case OMPC_firstprivate: case OMPC_shared: case NUM_OPENMP_CLAUSES: break; diff --git a/lib/Parse/ParseOpenMP.cpp b/lib/Parse/ParseOpenMP.cpp index de76b17dd93..992a4431c7a 100644 --- a/lib/Parse/ParseOpenMP.cpp +++ b/lib/Parse/ParseOpenMP.cpp @@ -252,7 +252,7 @@ bool Parser::ParseOpenMPSimpleVarList(OpenMPDirectiveKind Kind, /// \brief Parsing of OpenMP clauses. /// /// clause: -/// default-clause|private-clause|shared-clause +/// default-clause|private-clause|firstprivate-clause|shared-clause /// OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, OpenMPClauseKind CKind, bool FirstClause) { @@ -278,6 +278,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, Clause = ParseOpenMPSimpleClause(CKind); break; case OMPC_private: + case OMPC_firstprivate: case OMPC_shared: Clause = ParseOpenMPVarListClause(CKind); break; @@ -330,6 +331,8 @@ OMPClause *Parser::ParseOpenMPSimpleClause(OpenMPClauseKind Kind) { /// /// private-clause: /// 'private' '(' list ')' +/// firstprivate-clause: +/// 'firstprivate' '(' list ')' /// shared-clause: /// 'shared' '(' list ')' /// diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index dadfa4129ea..d5778bc6367 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -106,6 +106,11 @@ public: DSAVarData getTopDSA(VarDecl *D); /// \brief Returns data-sharing attributes for the specified declaration. DSAVarData getImplicitDSA(VarDecl *D); + /// \brief Checks if the specified variables has \a CKind data-sharing + /// attribute in \a DKind directive. + DSAVarData hasDSA(VarDecl *D, OpenMPClauseKind CKind, + OpenMPDirectiveKind DKind = OMPD_unknown); + /// \brief Returns currently analyzed directive. OpenMPDirectiveKind getCurrentDirective() const { @@ -138,6 +143,13 @@ DSAStackTy::DSAVarData DSAStackTy::getDSA(StackTy::reverse_iterator Iter, if (!D->isFunctionOrMethodVarDecl()) DVar.CKind = OMPC_shared; + // OpenMP [2.9.1.2, Data-sharing Attribute Rules for Variables Referenced + // in a region but not in construct] + // Variables with static storage duration that are declared in called + // routines in the region are shared. + if (D->hasGlobalStorage()) + DVar.CKind = OMPC_shared; + return DVar; } DVar.DKind = Iter->Directive; @@ -189,16 +201,14 @@ DSAStackTy::DSAVarData DSAStackTy::getDSA(StackTy::reverse_iterator Iter, if (DVarTemp.CKind != OMPC_shared) { DVar.RefExpr = 0; DVar.DKind = OMPD_task; - DVar.CKind = OMPC_unknown; - // TODO: should return OMPC_firstprivate + DVar.CKind = OMPC_firstprivate; return DVar; } if (I->Directive == OMPD_parallel) break; } DVar.DKind = OMPD_task; - // TODO: Should return OMPC_firstprivate instead of OMPC_unknown. DVar.CKind = - (DVarTemp.CKind == OMPC_unknown) ? OMPC_unknown : OMPC_shared; + (DVarTemp.CKind == OMPC_unknown) ? OMPC_firstprivate : OMPC_shared; return DVar; } } @@ -272,9 +282,12 @@ DSAStackTy::DSAVarData DSAStackTy::getTopDSA(VarDecl *D) { // in a Construct, C/C++, predetermined, p.4] // Static data memebers are shared. if (D->isStaticDataMember()) { - // Variables with const-qualified type having no mutable member may be - // listed in a firstprivate clause, even if they are static data members. - // TODO: + // Variables with const-qualified type having no mutable member may be listed + // in a firstprivate clause, even if they are static data members. + DSAVarData DVarTemp = hasDSA(D, OMPC_firstprivate); + if (DVarTemp.CKind == OMPC_firstprivate && DVarTemp.RefExpr) + return DVar; + DVar.CKind = OMPC_shared; return DVar; } @@ -295,7 +308,10 @@ DSAStackTy::DSAVarData DSAStackTy::getTopDSA(VarDecl *D) { !(Actions.getLangOpts().CPlusPlus && RD && RD->hasMutableFields())) { // Variables with const-qualified type having no mutable member may be // listed in a firstprivate clause, even if they are static data members. - // TODO + DSAVarData DVarTemp = hasDSA(D, OMPC_firstprivate); + if (DVarTemp.CKind == OMPC_firstprivate && DVarTemp.RefExpr) + return DVar; + DVar.CKind = OMPC_shared; return DVar; } @@ -323,6 +339,19 @@ DSAStackTy::DSAVarData DSAStackTy::getImplicitDSA(VarDecl *D) { return getDSA(Stack.rbegin() + 1, D); } +DSAStackTy::DSAVarData DSAStackTy::hasDSA(VarDecl *D, OpenMPClauseKind CKind, + OpenMPDirectiveKind DKind) { + for (StackTy::reverse_iterator I = Stack.rbegin() + 1, + E = Stack.rend() - 1; + I != E; ++I) { + if (DKind != OMPD_unknown && DKind != I->Directive) continue; + DSAVarData DVar = getDSA(I, D); + if (DVar.CKind == CKind) + return DVar; + } + return DSAVarData(); +} + void Sema::InitDataSharingAttributesStack() { VarDataSharingAttributesStack = new DSAStackTy(*this); } @@ -551,10 +580,10 @@ class DSAAttrChecker : public StmtVisitor<DSAAttrChecker, void> { Sema &Actions; bool ErrorFound; CapturedStmt *CS; + llvm::SmallVector<Expr *, 8> ImplicitFirstprivate; public: void VisitDeclRefExpr(DeclRefExpr *E) { if(VarDecl *VD = dyn_cast<VarDecl>(E->getDecl())) { - if (VD->isImplicit() && VD->hasAttr<UnusedAttr>()) return; // Skip internally declared variables. if (VD->isLocalVarDecl() && !CS->capturesVariable(VD)) return; @@ -564,9 +593,8 @@ public: DSAStackTy::DSAVarData DVar = Stack->getTopDSA(VD); if (DVar.CKind != OMPC_unknown) { if (DKind == OMPD_task && DVar.CKind != OMPC_shared && - DVar.CKind != OMPC_threadprivate && !DVar.RefExpr) { - // TODO: should be marked as firstprivate. - } + DVar.CKind != OMPC_threadprivate && !DVar.RefExpr) + ImplicitFirstprivate.push_back(DVar.RefExpr); return; } // The default(none) clause requires that each variable that is referenced @@ -588,9 +616,8 @@ public: // Define implicit data-sharing attributes for task. DVar = Stack->getImplicitDSA(VD); - if (DKind == OMPD_task && DVar.CKind != OMPC_shared) { - // TODO: should be marked as firstprivate. - } + if (DKind == OMPD_task && DVar.CKind != OMPC_shared) + ImplicitFirstprivate.push_back(DVar.RefExpr); } } void VisitOMPExecutableDirective(OMPExecutableDirective *S) { @@ -611,6 +638,7 @@ public: } bool isErrorFound() { return ErrorFound; } + ArrayRef<Expr *> getImplicitFirstprivate() { return ImplicitFirstprivate; } DSAAttrChecker(DSAStackTy *S, Sema &Actions, CapturedStmt *CS) : Stack(S), Actions(Actions), ErrorFound(false), CS(CS) { } @@ -631,10 +659,27 @@ StmtResult Sema::ActOnOpenMPExecutableDirective(OpenMPDirectiveKind Kind, DSAChecker.Visit(cast<CapturedStmt>(AStmt)->getCapturedStmt()); if (DSAChecker.isErrorFound()) return StmtError(); + // Generate list of implicitly defined firstprivate variables. + llvm::SmallVector<OMPClause *, 8> ClausesWithImplicit; + ClausesWithImplicit.append(Clauses.begin(), Clauses.end()); + + bool ErrorFound = false; + if (!DSAChecker.getImplicitFirstprivate().empty()) { + if (OMPClause *Implicit = + ActOnOpenMPFirstprivateClause(DSAChecker.getImplicitFirstprivate(), + SourceLocation(), SourceLocation(), + SourceLocation())) { + ClausesWithImplicit.push_back(Implicit); + ErrorFound = cast<OMPFirstprivateClause>(Implicit)->varlist_size() != + DSAChecker.getImplicitFirstprivate().size(); + } else + ErrorFound = true; + } switch (Kind) { case OMPD_parallel: - Res = ActOnOpenMPParallelDirective(Clauses, AStmt, StartLoc, EndLoc); + Res = ActOnOpenMPParallelDirective(ClausesWithImplicit, AStmt, + StartLoc, EndLoc); break; case OMPD_threadprivate: case OMPD_task: @@ -643,6 +688,8 @@ StmtResult Sema::ActOnOpenMPExecutableDirective(OpenMPDirectiveKind Kind, case NUM_OPENMP_DIRECTIVES: llvm_unreachable("Unknown OpenMP directive"); } + + if (ErrorFound) return StmtError(); return Res; } @@ -670,6 +717,7 @@ OMPClause *Sema::ActOnOpenMPSimpleClause(OpenMPClauseKind Kind, ArgumentLoc, StartLoc, LParenLoc, EndLoc); break; case OMPC_private: + case OMPC_firstprivate: case OMPC_shared: case OMPC_threadprivate: case OMPC_unknown: @@ -731,6 +779,9 @@ OMPClause *Sema::ActOnOpenMPVarListClause(OpenMPClauseKind Kind, case OMPC_private: Res = ActOnOpenMPPrivateClause(VarList, StartLoc, LParenLoc, EndLoc); break; + case OMPC_firstprivate: + Res = ActOnOpenMPFirstprivateClause(VarList, StartLoc, LParenLoc, EndLoc); + break; case OMPC_shared: Res = ActOnOpenMPSharedClause(VarList, StartLoc, LParenLoc, EndLoc); break; @@ -876,6 +927,177 @@ OMPClause *Sema::ActOnOpenMPPrivateClause(ArrayRef<Expr *> VarList, return OMPPrivateClause::Create(Context, StartLoc, LParenLoc, EndLoc, Vars); } +OMPClause *Sema::ActOnOpenMPFirstprivateClause(ArrayRef<Expr *> VarList, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + SmallVector<Expr *, 8> Vars; + for (ArrayRef<Expr *>::iterator I = VarList.begin(), E = VarList.end(); + I != E; ++I) { + assert(*I && "NULL expr in OpenMP firstprivate clause."); + if (isa<DependentScopeDeclRefExpr>(*I)) { + // It will be analyzed later. + Vars.push_back(*I); + continue; + } + + SourceLocation ELoc = (*I)->getExprLoc(); + // OpenMP [2.1, C/C++] + // A list item is a variable name. + // OpenMP [2.9.3.3, Restrictions, p.1] + // A variable that is part of another variable (as an array or + // structure element) cannot appear in a private clause. + DeclRefExpr *DE = dyn_cast_or_null<DeclRefExpr>(*I); + if (!DE || !isa<VarDecl>(DE->getDecl())) { + Diag(ELoc, diag::err_omp_expected_var_name) + << (*I)->getSourceRange(); + continue; + } + Decl *D = DE->getDecl(); + VarDecl *VD = cast<VarDecl>(D); + + QualType Type = VD->getType(); + if (Type->isDependentType() || Type->isInstantiationDependentType()) { + // It will be analyzed later. + Vars.push_back(DE); + continue; + } + + // OpenMP [2.9.3.3, Restrictions, C/C++, p.3] + // A variable that appears in a private clause must not have an incomplete + // type or a reference type. + if (RequireCompleteType(ELoc, Type, + diag::err_omp_firstprivate_incomplete_type)) { + continue; + } + if (Type->isReferenceType()) { + Diag(ELoc, diag::err_omp_clause_ref_type_arg) + << getOpenMPClauseName(OMPC_firstprivate) << Type; + bool IsDecl = VD->isThisDeclarationADefinition(Context) == + VarDecl::DeclarationOnly; + Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl : + diag::note_defined_here) << VD; + continue; + } + + // OpenMP [2.9.3.4, Restrictions, C/C++, p.1] + // A variable of class type (or array thereof) that appears in a private + // clause requires an accesible, unambiguous copy constructor for the + // class type. + Type = Context.getBaseElementType(Type); + CXXRecordDecl *RD = getLangOpts().CPlusPlus ? + Type.getNonReferenceType()->getAsCXXRecordDecl() : 0; + if (RD) { + CXXConstructorDecl *CD = LookupCopyingConstructor(RD, 0); + PartialDiagnostic PD = + PartialDiagnostic(PartialDiagnostic::NullDiagnostic()); + if (!CD || + CheckConstructorAccess(ELoc, CD, + InitializedEntity::InitializeTemporary(Type), + CD->getAccess(), PD) == AR_inaccessible || + CD->isDeleted()) { + Diag(ELoc, diag::err_omp_required_method) + << getOpenMPClauseName(OMPC_firstprivate) << 1; + bool IsDecl = VD->isThisDeclarationADefinition(Context) == + VarDecl::DeclarationOnly; + Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl : + diag::note_defined_here) << VD; + Diag(RD->getLocation(), diag::note_previous_decl) << RD; + continue; + } + MarkFunctionReferenced(ELoc, CD); + DiagnoseUseOfDecl(CD, ELoc); + + CXXDestructorDecl *DD = RD->getDestructor(); + if (DD) { + if (CheckDestructorAccess(ELoc, DD, PD) == AR_inaccessible || + DD->isDeleted()) { + Diag(ELoc, diag::err_omp_required_method) + << getOpenMPClauseName(OMPC_firstprivate) << 4; + bool IsDecl = VD->isThisDeclarationADefinition(Context) == + VarDecl::DeclarationOnly; + Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl : + diag::note_defined_here) << VD; + Diag(RD->getLocation(), diag::note_previous_decl) << RD; + continue; + } + MarkFunctionReferenced(ELoc, DD); + DiagnoseUseOfDecl(DD, ELoc); + } + } + + // If StartLoc and EndLoc are invalid - this is an implicit firstprivate + // variable and it was checked already. + if (StartLoc.isValid() && EndLoc.isValid()) { + DSAStackTy::DSAVarData DVar = DSAStack->getTopDSA(VD); + Type = Type.getNonReferenceType().getCanonicalType(); + bool IsConstant = Type.isConstant(Context); + Type = Context.getBaseElementType(Type); + // OpenMP [2.4.13, Data-sharing Attribute Clauses] + // A list item that specifies a given variable may not appear in more + // than one clause on the same directive, except that a variable may be + // specified in both firstprivate and lastprivate clauses. + // TODO: add processing for lastprivate. + if (DVar.CKind != OMPC_unknown && DVar.CKind != OMPC_firstprivate && + DVar.RefExpr) { + Diag(ELoc, diag::err_omp_wrong_dsa) + << getOpenMPClauseName(DVar.CKind) + << getOpenMPClauseName(OMPC_firstprivate); + Diag(DVar.RefExpr->getExprLoc(), diag::note_omp_explicit_dsa) + << getOpenMPClauseName(DVar.CKind); + continue; + } + + // OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced + // in a Construct] + // Variables with the predetermined data-sharing attributes may not be + // listed in data-sharing attributes clauses, except for the cases + // listed below. For these exceptions only, listing a predetermined + // variable in a data-sharing attribute clause is allowed and overrides + // the variable's predetermined data-sharing attributes. + // OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced + // in a Construct, C/C++, p.2] + // Variables with const-qualified type having no mutable member may be + // listed in a firstprivate clause, even if they are static data members. + if (!(IsConstant || VD->isStaticDataMember()) && !DVar.RefExpr && + DVar.CKind != OMPC_unknown && DVar.CKind != OMPC_shared) { + Diag(ELoc, diag::err_omp_wrong_dsa) + << getOpenMPClauseName(DVar.CKind) + << getOpenMPClauseName(OMPC_firstprivate); + Diag(VD->getLocation(), diag::note_omp_predetermined_dsa) + << getOpenMPClauseName(DVar.CKind); + continue; + } + + // OpenMP [2.9.3.4, Restrictions, p.2] + // A list item that is private within a parallel region must not appear + // in a firstprivate clause on a worksharing construct if any of the + // worksharing regions arising from the worksharing construct ever bind + // to any of the parallel regions arising from the parallel construct. + // OpenMP [2.9.3.4, Restrictions, p.3] + // A list item that appears in a reduction clause of a parallel construct + // must not appear in a firstprivate clause on a worksharing or task + // construct if any of the worksharing or task regions arising from the + // worksharing or task construct ever bind to any of the parallel regions + // arising from the parallel construct. + // OpenMP [2.9.3.4, Restrictions, p.4] + // A list item that appears in a reduction clause in worksharing + // construct must not appear in a firstprivate clause in a task construct + // encountered during execution of any of the worksharing regions arising + // from the worksharing construct. + // TODO: + } + + DSAStack->addDSA(VD, DE, OMPC_firstprivate); + Vars.push_back(DE); + } + + if (Vars.empty()) return 0; + + return OMPFirstprivateClause::Create(Context, StartLoc, LParenLoc, EndLoc, + Vars); +} + OMPClause *Sema::ActOnOpenMPSharedClause(ArrayRef<Expr *> VarList, SourceLocation StartLoc, SourceLocation LParenLoc, diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 5b019a96c06..977d0132b7e 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1313,6 +1313,18 @@ public: EndLoc); } + /// \brief Build a new OpenMP 'firstprivate' clause. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + OMPClause *RebuildOMPFirstprivateClause(ArrayRef<Expr *> VarList, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + return getSema().ActOnOpenMPFirstprivateClause(VarList, StartLoc, LParenLoc, + EndLoc); + } + OMPClause *RebuildOMPSharedClause(ArrayRef<Expr *> VarList, SourceLocation StartLoc, SourceLocation LParenLoc, @@ -6341,6 +6353,26 @@ TreeTransform<Derived>::TransformOMPPrivateClause(OMPPrivateClause *C) { C->getLocEnd()); } +template<typename Derived> +OMPClause * +TreeTransform<Derived>::TransformOMPFirstprivateClause( + OMPFirstprivateClause *C) { + llvm::SmallVector<Expr *, 16> Vars; + Vars.reserve(C->varlist_size()); + for (OMPFirstprivateClause::varlist_iterator I = C->varlist_begin(), + E = C->varlist_end(); + I != E; ++I) { + ExprResult EVar = getDerived().TransformExpr(cast<Expr>(*I)); + if (EVar.isInvalid()) + return 0; + Vars.push_back(EVar.take()); + } + return getDerived().RebuildOMPFirstprivateClause(Vars, + C->getLocStart(), + C->getLParenLoc(), + C->getLocEnd()); +} + template<typename Derived> OMPClause * TreeTransform<Derived>::TransformOMPSharedClause(OMPSharedClause *C) { diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 23cd9cc726b..1115e8fd4f4 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1697,6 +1697,9 @@ OMPClause *OMPClauseReader::readClause() { case OMPC_private: C = OMPPrivateClause::CreateEmpty(Context, Record[Idx++]); break; + case OMPC_firstprivate: + C = OMPFirstprivateClause::CreateEmpty(Context, Record[Idx++]); + break; case OMPC_shared: C = OMPSharedClause::CreateEmpty(Context, Record[Idx++]); break; @@ -1725,6 +1728,16 @@ void OMPClauseReader::VisitOMPPrivateClause(OMPPrivateClause *C) { C->setVarRefs(Vars); } +void OMPClauseReader::VisitOMPFirstprivateClause(OMPFirstprivateClause *C) { + C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx)); + unsigned NumVars = C->varlist_size(); + SmallVector<Expr *, 16> Vars; + Vars.reserve(NumVars); + for (unsigned i = 0; i != NumVars; ++i) + Vars.push_back(Reader->Reader.ReadSubExpr()); + C->setVarRefs(Vars); +} + void OMPClauseReader::VisitOMPSharedClause(OMPSharedClause *C) { C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx)); unsigned NumVars = C->varlist_size(); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index e38d8c69a72..072fc98e391 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1712,6 +1712,15 @@ void OMPClauseWriter::VisitOMPPrivateClause(OMPPrivateClause *C) { Writer->Writer.AddStmt(*I); } +void OMPClauseWriter::VisitOMPFirstprivateClause(OMPFirstprivateClause *C) { + Record.push_back(C->varlist_size()); + Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); + for (OMPFirstprivateClause::varlist_iterator I = C->varlist_begin(), + E = C->varlist_end(); + I != E; ++I) + Writer->Writer.AddStmt(*I); +} + void OMPClauseWriter::VisitOMPSharedClause(OMPSharedClause *C) { Record.push_back(C->varlist_size()); Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); diff --git a/test/OpenMP/parallel_ast_print.cpp b/test/OpenMP/parallel_ast_print.cpp index 44108e7ec2f..f2fd2f7b69f 100644 --- a/test/OpenMP/parallel_ast_print.cpp +++ b/test/OpenMP/parallel_ast_print.cpp @@ -8,7 +8,41 @@ void foo() {} + +template <typename T> +T tmain(T argc, T *argv) { + T b = argc, c, d, e, f, g; + static T a; +#pragma omp parallel + a=2; +#pragma omp parallel default(none), private(argc,b) firstprivate(argv) shared (d) + foo(); + return 0; +} +// CHECK: template <typename T = int> int tmain(int argc, int *argv) { +// CHECK-NEXT: int b = argc, c, d, e, f, g; +// CHECK-NEXT: static int a; +// CHECK-NEXT: #pragma omp parallel +// CHECK-NEXT: a = 2; +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) +// CHECK-NEXT: foo() +// CHECK: template <typename T = float> float tmain(float argc, float *argv) { +// CHECK-NEXT: float b = argc, c, d, e, f, g; +// CHECK-NEXT: static float a; +// CHECK-NEXT: #pragma omp parallel +// CHECK-NEXT: a = 2; +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) +// CHECK-NEXT: foo() +// CHECK: template <typename T> T tmain(T argc, T *argv) { +// CHECK-NEXT: T b = argc, c, d, e, f, g; +// CHECK-NEXT: static T a; +// CHECK-NEXT: #pragma omp parallel +// CHECK-NEXT: a = 2; +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) +// CHECK-NEXT: foo() + int main (int argc, char **argv) { + float x; int b = argc, c, d, e, f, g; static int a; // CHECK: static int a; @@ -16,11 +50,11 @@ int main (int argc, char **argv) { // CHECK-NEXT: #pragma omp parallel a=2; // CHECK-NEXT: a = 2; -#pragma omp parallel default(none), private(argc,b) -// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) +#pragma omp parallel default(none), private(argc,b) firstprivate(argv) +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) foo(); // CHECK-NEXT: foo(); - return (0); + return tmain(b, &b) + tmain(x, &x); } #endif diff --git a/test/OpenMP/parallel_firstprivate_messages.cpp b/test/OpenMP/parallel_firstprivate_messages.cpp new file mode 100644 index 00000000000..780059e282b --- /dev/null +++ b/test/OpenMP/parallel_firstprivate_messages.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s + +void foo() { +} + +bool foobool(int argc) { + return argc; +} + +struct S1; // expected-note {{declared here}} expected-note{{forward declaration of 'S1'}} +extern S1 a; +class S2 { + mutable int a; +public: + S2():a(0) { } + S2(S2 &s2):a(s2.a) { } + static float S2s; + static const float S2sc; +}; +const float S2::S2sc = 0; +const S2 b; +const S2 ba[5]; +class S3 { + int a; +public: + S3():a(0) { } + S3(S3 &s3):a(s3.a) { } +}; +const S3 c; +const S3 ca[5]; +extern const int f; +class S4 { // expected-note {{'S4' declared here}} + int a; + S4(); + S4(const S4 &s4); +public: + S4(int v):a(v) { } +}; +class S5 { // expected-note {{'S5' declared here}} + int a; + S5():a(0) {} + S5(const S5 &s5):a(s5.a) { } +public: + S5(int v):a(v) { } +}; + +S3 h; +#pragma omp threadprivate(h) // expected-note {{defined as threadprivate or thread local}} + +int main(int argc, char **argv) { + const int d = 5; + const int da[5] = { 0 }; + S4 e(4); // expected-note {{'e' defined here}} + S5 g(5); // expected-note {{'g' defined here}} + int i; + int &j = i; // expected-note {{'j' defined here}} + #pragma omp parallel firstprivate // expected-error {{expected '(' after 'firstprivate'}} + #pragma omp parallel firstprivate ( // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp parallel firstprivate () // expected-error {{expected expression}} + #pragma omp parallel firstprivate (argc // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp parallel firstprivate (argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp parallel firstprivate (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} + #pragma omp parallel firstprivate (argc) + #pragma omp parallel firstprivate (S1) // expected-error {{'S1' does not refer to a value}} + #pragma omp parallel firstprivate (a, b, c, d, f) // expected-error {{firstprivate variable with incomplete type 'S1'}} + #pragma omp parallel firstprivate (argv[1]) // expected-error {{expected variable name}} + #pragma omp parallel firstprivate(ba) + #pragma omp parallel firstprivate(ca) + #pragma omp parallel firstprivate(da) + #pragma omp parallel firstprivate(S2::S2s) + #pragma omp parallel firstprivate(S2::S2sc) + #pragma omp parallel firstprivate(e, g) // expected-error 2 {{firstprivate variable must have an accessible, unambiguous copy constructor}} + #pragma omp parallel firstprivate(h) // expected-error {{threadprivate or thread local variable cannot be firstprivate}} + #pragma omp parallel private(i), firstprivate(i) // expected-error {{private variable cannot be firstprivate}} expected-note{{defined as private}} + foo(); + #pragma omp parallel shared(i) + #pragma omp parallel firstprivate(i) + #pragma omp parallel firstprivate(j) // expected-error {{arguments of OpenMP clause 'firstprivate' cannot be of reference type}} + foo(); + + return 0; +} diff --git a/test/OpenMP/parallel_private_messages.cpp b/test/OpenMP/parallel_private_messages.cpp index 102bf300aa2..2037d56daf0 100644 --- a/test/OpenMP/parallel_private_messages.cpp +++ b/test/OpenMP/parallel_private_messages.cpp @@ -64,10 +64,18 @@ int main(int argc, char **argv) { #pragma omp parallel private(S2::S2s) // expected-error {{shared variable cannot be private}} #pragma omp parallel private(e, g) // expected-error 2 {{private variable must have an accessible, unambiguous default constructor}} #pragma omp parallel private(threadvar) // expected-error {{threadprivate or thread local variable cannot be private}} + #pragma omp parallel shared(i), private(i) // expected-error {{shared variable cannot be private}} expected-note {{defined as shared}} + foo(); + #pragma omp parallel firstprivate(i) private(i) // expected-error {{firstprivate variable cannot be private}} expected-note {{defined as firstprivate}} foo(); #pragma omp parallel private(i) #pragma omp parallel private(j) // expected-error {{arguments of OpenMP clause 'private' cannot be of reference type 'int &'}} foo(); + #pragma omp parallel firstprivate(i) + for (int k = 0; k < 10; ++k) { + #pragma omp parallel private(i) + foo(); + } return 0; } diff --git a/test/OpenMP/parallel_shared_messages.cpp b/test/OpenMP/parallel_shared_messages.cpp index 0d8894e0d44..211d392fe04 100644 --- a/test/OpenMP/parallel_shared_messages.cpp +++ b/test/OpenMP/parallel_shared_messages.cpp @@ -68,10 +68,16 @@ int main(int argc, char **argv) { #pragma omp parallel shared(h) // expected-error {{threadprivate or thread local variable cannot be shared}} #pragma omp parallel private(i), shared(i) // expected-error {{private variable cannot be shared}} expected-note {{defined as private}} foo(); + #pragma omp parallel firstprivate(i), shared(i) // expected-error {{firstprivate variable cannot be shared}} expected-note {{defined as firstprivate}} + foo(); #pragma omp parallel private(i) #pragma omp parallel shared(i) #pragma omp parallel shared(j) foo(); + #pragma omp parallel firstprivate(i) + #pragma omp parallel shared(i) + #pragma omp parallel shared(j) + foo(); return 0; } diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index b40a9134816..967750f954a 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -1934,6 +1934,10 @@ void OMPClauseEnqueue::VisitOMPClauseList(T *Node) { void OMPClauseEnqueue::VisitOMPPrivateClause(const OMPPrivateClause *C) { VisitOMPClauseList(C); } +void OMPClauseEnqueue::VisitOMPFirstprivateClause( + const OMPFirstprivateClause *C) { + VisitOMPClauseList(C); +} void OMPClauseEnqueue::VisitOMPSharedClause(const OMPSharedClause *C) { VisitOMPClauseList(C); } diff --git a/tools/libclang/RecursiveASTVisitor.h b/tools/libclang/RecursiveASTVisitor.h index fe936f922ef..3ad5acbb041 100644 --- a/tools/libclang/RecursiveASTVisitor.h +++ b/tools/libclang/RecursiveASTVisitor.h @@ -2344,6 +2344,13 @@ bool RecursiveASTVisitor<Derived>::VisitOMPPrivateClause(OMPPrivateClause *C) { return true; } +template<typename Derived> +bool RecursiveASTVisitor<Derived>::VisitOMPFirstprivateClause( + OMPFirstprivateClause *C) { + VisitOMPClauseList(C); + return true; +} + template<typename Derived> bool RecursiveASTVisitor<Derived>::VisitOMPSharedClause(OMPSharedClause *C) { VisitOMPClauseList(C); -- GitLab