diff --git a/clang/include/clang/AST/DataRecursiveASTVisitor.h b/clang/include/clang/AST/DataRecursiveASTVisitor.h index acee4457c5b7..1c618f55fde3 100644 --- a/clang/include/clang/AST/DataRecursiveASTVisitor.h +++ b/clang/include/clang/AST/DataRecursiveASTVisitor.h @@ -2391,6 +2391,15 @@ bool RecursiveASTVisitor::VisitOMPCopyinClause(OMPCopyinClause *C) { return true; } +template +bool DataRecursiveASTVisitor::VisitOMPReductionClause( + OMPReductionClause *C) { + TRY_TO(TraverseNestedNameSpecifierLoc(C->getQualifierLoc())); + TRY_TO(TraverseDeclarationNameInfo(C->getNameInfo())); + VisitOMPClauseList(C); + return true; +} + // FIXME: look at the following tricky-seeming exprs to see if we // need to recurse on anything. These are ones that have methods // returning decls or qualtypes or nestednamespecifier -- though I'm diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 74a6a9eedc8b..d205dcd94566 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -431,7 +431,8 @@ public: StmtRange children() { return StmtRange(); } }; -/// \brief This represents 'proc_bind' clause in the '#pragma omp ...' directive. +/// \brief This represents 'proc_bind' clause in the '#pragma omp ...' +/// directive. /// /// \code /// #pragma omp parallel proc_bind(master) @@ -471,8 +472,8 @@ public: /// \param EndLoc Ending location of the clause. /// OMPProcBindClause(OpenMPProcBindClauseKind A, SourceLocation ALoc, - SourceLocation StartLoc, SourceLocation LParenLoc, - SourceLocation EndLoc) + SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc) : OMPClause(OMPC_proc_bind, StartLoc, EndLoc), LParenLoc(LParenLoc), Kind(A), KindKwLoc(ALoc) {} @@ -739,6 +740,99 @@ public: } }; +/// \brief This represents clause 'reduction' in the '#pragma omp ...' +/// directives. +/// +/// \code +/// #pragma omp parallel reduction(+:a,b) +/// \endcode +/// In this example directive '#pragma omp parallel' has clause 'reduction' +/// with operator '+' and the variables 'a' and 'b'. +/// +class OMPReductionClause : public OMPVarListClause { + friend class OMPClauseReader; + /// \brief Location of ':'. + SourceLocation ColonLoc; + /// \brief Nested name specifier for C++. + NestedNameSpecifierLoc QualifierLoc; + /// \brief Name of custom operator. + DeclarationNameInfo NameInfo; + + /// \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 ColonLoc Location of ':'. + /// \param N Number of the variables in the clause. + /// \param QualifierLoc The nested-name qualifier with location information + /// \param NameInfo The full name info for reduction identifier. + /// + OMPReductionClause(SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation EndLoc, unsigned N, + NestedNameSpecifierLoc QualifierLoc, + const DeclarationNameInfo &NameInfo) + : OMPVarListClause(OMPC_reduction, StartLoc, + LParenLoc, EndLoc, N), + ColonLoc(ColonLoc), QualifierLoc(QualifierLoc), NameInfo(NameInfo) {} + + /// \brief Build an empty clause. + /// + /// \param N Number of variables. + /// + explicit OMPReductionClause(unsigned N) + : OMPVarListClause(OMPC_reduction, SourceLocation(), + SourceLocation(), SourceLocation(), + N), + ColonLoc(), QualifierLoc(), NameInfo() {} + + /// \brief Sets location of ':' symbol in clause. + void setColonLoc(SourceLocation CL) { ColonLoc = CL; } + /// \brief Sets the name info for specified reduction identifier. + void setNameInfo(DeclarationNameInfo DNI) { NameInfo = DNI; } + /// \brief Sets the nested name specifier. + void setQualifierLoc(NestedNameSpecifierLoc NSL) { QualifierLoc = NSL; } + +public: + /// \brief Creates clause with a list of variables \a VL. + /// + /// \param StartLoc Starting location of the clause. + /// \param LParenLoc Location of '('. + /// \param ColonLoc Location of ':'. + /// \param EndLoc Ending location of the clause. + /// \param N Number of the variables in the clause. + /// \param QualifierLoc The nested-name qualifier with location information + /// \param NameInfo The full name info for reduction identifier. + /// + static OMPReductionClause * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation EndLoc, ArrayRef VL, + NestedNameSpecifierLoc QualifierLoc, + const DeclarationNameInfo &NameInfo); + /// \brief Creates an empty clause with the place for \a N variables. + /// + /// \param C AST context. + /// \param N The number of variables. + /// + static OMPReductionClause *CreateEmpty(const ASTContext &C, unsigned N); + + /// \brief Gets location of ':' symbol in clause. + SourceLocation getColonLoc() const { return ColonLoc; } + /// \brief Gets the name info for specified reduction identifier. + const DeclarationNameInfo &getNameInfo() const { return NameInfo; } + /// \brief Gets the nested name specifier. + NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } + + StmtRange children() { + return StmtRange(reinterpret_cast(varlist_begin()), + reinterpret_cast(varlist_end())); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == OMPC_reduction; + } +}; + /// \brief This represents clause 'linear' in the '#pragma omp ...' /// directives. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 12664d0ce878..61fb0aad8fd4 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2393,6 +2393,15 @@ bool RecursiveASTVisitor::VisitOMPSharedClause(OMPSharedClause *C) { } template +bool RecursiveASTVisitor::VisitOMPReductionClause( + OMPReductionClause *C) { + TRY_TO(TraverseNestedNameSpecifierLoc(C->getQualifierLoc())); + TRY_TO(TraverseDeclarationNameInfo(C->getNameInfo())); + VisitOMPClauseList(C); + return true; +} + +template bool RecursiveASTVisitor::VisitOMPLinearClause(OMPLinearClause *C) { VisitOMPClauseList(C); TraverseStmt(C->getStep()); diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1751860f4526..c2fb55276465 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6965,6 +6965,8 @@ def err_omp_firstprivate_incomplete_type : Error< "a firstprivate variable with incomplete type %0">; def err_omp_lastprivate_incomplete_type : Error< "a lastprivate variable with incomplete type %0">; +def err_omp_reduction_incomplete_type : Error< + "a reduction 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< @@ -7044,6 +7046,24 @@ def err_omp_loop_cannot_use_stmt : Error< "'%0' statement cannot be used in OpenMP for loop">; def err_omp_simd_region_cannot_use_stmt : Error< "'%0' statement cannot be used in OpenMP simd region">; +def err_omp_unknown_reduction_identifier : Error< + "incorrect reduction identifier, expected one of '+', '-', '*', '&', '|', '^', '&&', '||', 'min' or 'max'">; +def err_omp_reduction_type_array : Error< + "a reduction variable with array type %0">; +def err_omp_reduction_ref_type_arg : Error< + "argument of OpenMP clause 'reduction' must reference the same object in all threads">; +def err_omp_clause_not_arithmetic_type_arg : Error< + "arguments of OpenMP clause 'reduction' for 'min' or 'max' must be of %select{scalar|arithmetic}0 type">; +def err_omp_clause_floating_type_arg : Error< + "arguments of OpenMP clause 'reduction' with bitwise operators cannot be of floating type">; +def err_omp_once_referenced : Error< + "variable can appear only once in OpenMP '%0' clause">; +def note_omp_referenced : Note< + "previously referenced here">; +def err_omp_reduction_in_task : Error< + "reduction variables may not be accessed in an explicit task">; +def err_omp_reduction_id_not_compatible : Error< + "variable of type %0 is not valid for specified reduction operation">; } // end of OpenMP category let CategoryName = "Related Result Type Issue" in { diff --git a/clang/include/clang/Basic/OpenMPKinds.def b/clang/include/clang/Basic/OpenMPKinds.def index 0a6b162abd5b..ecef38f7f197 100644 --- a/clang/include/clang/Basic/OpenMPKinds.def +++ b/clang/include/clang/Basic/OpenMPKinds.def @@ -47,6 +47,7 @@ OPENMP_CLAUSE(private, OMPPrivateClause) OPENMP_CLAUSE(firstprivate, OMPFirstprivateClause) OPENMP_CLAUSE(lastprivate, OMPLastprivateClause) OPENMP_CLAUSE(shared, OMPSharedClause) +OPENMP_CLAUSE(reduction, OMPReductionClause) OPENMP_CLAUSE(linear, OMPLinearClause) OPENMP_CLAUSE(aligned, OMPAlignedClause) OPENMP_CLAUSE(copyin, OMPCopyinClause) @@ -60,6 +61,7 @@ OPENMP_PARALLEL_CLAUSE(proc_bind) OPENMP_PARALLEL_CLAUSE(private) OPENMP_PARALLEL_CLAUSE(firstprivate) OPENMP_PARALLEL_CLAUSE(shared) +OPENMP_PARALLEL_CLAUSE(reduction) OPENMP_PARALLEL_CLAUSE(copyin) // FIXME: more clauses allowed for directive 'omp simd'. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 414a67e25630..2f0787798d0a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7355,13 +7355,13 @@ public: SourceLocation LParenLoc, SourceLocation EndLoc); - OMPClause *ActOnOpenMPVarListClause(OpenMPClauseKind Kind, - ArrayRef Vars, - Expr *TailExpr, - SourceLocation StartLoc, - SourceLocation LParenLoc, - SourceLocation ColonLoc, - SourceLocation EndLoc); + OMPClause * + ActOnOpenMPVarListClause(OpenMPClauseKind Kind, ArrayRef Vars, + Expr *TailExpr, SourceLocation StartLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation EndLoc, + CXXScopeSpec &ReductionIdScopeSpec, + const DeclarationNameInfo &ReductionId); /// \brief Called on well-formed 'private' clause. OMPClause *ActOnOpenMPPrivateClause(ArrayRef VarList, SourceLocation StartLoc, @@ -7382,6 +7382,13 @@ public: SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// \brief Called on well-formed 'reduction' clause. + OMPClause * + ActOnOpenMPReductionClause(ArrayRef VarList, SourceLocation StartLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation EndLoc, + CXXScopeSpec &ReductionIdScopeSpec, + const DeclarationNameInfo &ReductionId); /// \brief Called on well-formed 'linear' clause. OMPClause *ActOnOpenMPLinearClause(ArrayRef VarList, Expr *Step, diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 52906877cf28..826a1d851f10 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -1286,6 +1286,27 @@ void OMPExecutableDirective::setClauses(ArrayRef Clauses) { std::copy(Clauses.begin(), Clauses.end(), getClauses().begin()); } +OMPReductionClause *OMPReductionClause::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc, SourceLocation ColonLoc, ArrayRef VL, + NestedNameSpecifierLoc QualifierLoc, const DeclarationNameInfo &NameInfo) { + void *Mem = C.Allocate(llvm::RoundUpToAlignment(sizeof(OMPReductionClause), + llvm::alignOf()) + + sizeof(Expr *) * VL.size()); + OMPReductionClause *Clause = new (Mem) OMPReductionClause( + StartLoc, LParenLoc, EndLoc, ColonLoc, VL.size(), QualifierLoc, NameInfo); + Clause->setVarRefs(VL); + return Clause; +} + +OMPReductionClause *OMPReductionClause::CreateEmpty(const ASTContext &C, + unsigned N) { + void *Mem = C.Allocate(llvm::RoundUpToAlignment(sizeof(OMPReductionClause), + llvm::alignOf()) + + sizeof(Expr *) * N); + return new (Mem) OMPReductionClause(N); +} + OMPParallelDirective *OMPParallelDirective::Create( const ASTContext &C, SourceLocation StartLoc, diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index cd206965fb85..fdf2d5450191 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -676,6 +676,28 @@ void OMPClausePrinter::VisitOMPSharedClause(OMPSharedClause *Node) { } } +void OMPClausePrinter::VisitOMPReductionClause(OMPReductionClause *Node) { + if (!Node->varlist_empty()) { + OS << "reduction("; + NestedNameSpecifier *QualifierLoc = + Node->getQualifierLoc().getNestedNameSpecifier(); + OverloadedOperatorKind OOK = + Node->getNameInfo().getName().getCXXOverloadedOperator(); + if (QualifierLoc == nullptr && OOK != OO_None) { + // Print reduction identifier in C format + OS << getOperatorSpelling(OOK); + } else { + // Use C++ format + if (QualifierLoc != nullptr) + QualifierLoc->print(OS, Policy); + OS << Node->getNameInfo(); + } + OS << ":"; + VisitOMPClauseList(Node, ' '); + OS << ")"; + } +} + void OMPClausePrinter::VisitOMPLinearClause(OMPLinearClause *Node) { if (!Node->varlist_empty()) { OS << "linear"; diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 8d97d71db78a..17cd28e46a49 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -308,6 +308,13 @@ OMPClauseProfiler::VisitOMPLastprivateClause(const OMPLastprivateClause *C) { void OMPClauseProfiler::VisitOMPSharedClause(const OMPSharedClause *C) { VisitOMPClauseList(C); } +void OMPClauseProfiler::VisitOMPReductionClause( + const OMPReductionClause *C) { + Profiler->VisitNestedNameSpecifier( + C->getQualifierLoc().getNestedNameSpecifier()); + Profiler->VisitName(C->getNameInfo().getName()); + VisitOMPClauseList(C); +} void OMPClauseProfiler::VisitOMPLinearClause(const OMPLinearClause *C) { VisitOMPClauseList(C); Profiler->VisitStmt(C->getStep()); diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 8b93c441d455..15a88037a815 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -88,6 +88,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, case OMPC_firstprivate: case OMPC_lastprivate: case OMPC_shared: + case OMPC_reduction: case OMPC_linear: case OMPC_aligned: case OMPC_copyin: @@ -127,6 +128,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_firstprivate: case OMPC_lastprivate: case OMPC_shared: + case OMPC_reduction: case OMPC_linear: case OMPC_aligned: case OMPC_copyin: diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index dc79c0e795dd..9c52d645cfdf 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -258,7 +258,8 @@ bool Parser::ParseOpenMPSimpleVarList(OpenMPDirectiveKind Kind, /// clause: /// if-clause | num_threads-clause | safelen-clause | default-clause | /// private-clause | firstprivate-clause | shared-clause | linear-clause | -/// aligned-clause | collapse-clause | lastprivate-clause +/// aligned-clause | collapse-clause | lastprivate-clause | +/// reduction-clause /// OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, OpenMPClauseKind CKind, bool FirstClause) { @@ -307,6 +308,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_firstprivate: case OMPC_lastprivate: case OMPC_shared: + case OMPC_reduction: case OMPC_linear: case OMPC_aligned: case OMPC_copyin: @@ -394,6 +396,52 @@ OMPClause *Parser::ParseOpenMPSimpleClause(OpenMPClauseKind Kind) { Tok.getLocation()); } +static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec, + UnqualifiedId &ReductionId) { + SourceLocation TemplateKWLoc; + if (ReductionIdScopeSpec.isEmpty()) { + auto OOK = OO_None; + switch (P.getCurToken().getKind()) { + case tok::plus: + OOK = OO_Plus; + break; + case tok::minus: + OOK = OO_Minus; + break; + case tok::star: + OOK = OO_Star; + break; + case tok::amp: + OOK = OO_Amp; + break; + case tok::pipe: + OOK = OO_Pipe; + break; + case tok::caret: + OOK = OO_Caret; + break; + case tok::ampamp: + OOK = OO_AmpAmp; + break; + case tok::pipepipe: + OOK = OO_PipePipe; + break; + default: + break; + } + if (OOK != OO_None) { + SourceLocation OpLoc = P.ConsumeToken(); + SourceLocation SymbolLocations[] = { OpLoc, OpLoc, SourceLocation() }; + ReductionId.setOperatorFunctionId(OpLoc, OOK, SymbolLocations); + return false; + } + } + return P.ParseUnqualifiedId(ReductionIdScopeSpec, /*EnteringContext*/ false, + /*AllowDestructorName*/ false, + /*AllowConstructorName*/ false, ParsedType(), + TemplateKWLoc, ReductionId); +} + /// \brief Parsing of OpenMP clause 'private', 'firstprivate', 'lastprivate', /// 'shared', 'copyin', or 'reduction'. /// @@ -409,19 +457,44 @@ OMPClause *Parser::ParseOpenMPSimpleClause(OpenMPClauseKind Kind) { /// 'linear' '(' list [ ':' linear-step ] ')' /// aligned-clause: /// 'aligned' '(' list [ ':' alignment ] ')' +/// reduction-clause: +/// 'reduction' '(' reduction-identifier ':' list ')' /// OMPClause *Parser::ParseOpenMPVarListClause(OpenMPClauseKind Kind) { SourceLocation Loc = Tok.getLocation(); SourceLocation LOpen = ConsumeToken(); SourceLocation ColonLoc = SourceLocation(); + // Optional scope specifier and unqualified id for reduction identifier. + CXXScopeSpec ReductionIdScopeSpec; + UnqualifiedId ReductionId; + bool InvalidReductionId = false; // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, getOpenMPClauseName(Kind))) return nullptr; + // Handle reduction-identifier for reduction clause. + if (Kind == OMPC_reduction) { + ColonProtectionRAIIObject ColonRAII(*this); + if (getLangOpts().CPlusPlus) { + ParseOptionalCXXScopeSpecifier(ReductionIdScopeSpec, ParsedType(), false); + } + InvalidReductionId = + ParseReductionId(*this, ReductionIdScopeSpec, ReductionId); + if (InvalidReductionId) { + SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } + if (Tok.is(tok::colon)) { + ColonLoc = ConsumeToken(); + } else { + Diag(Tok, diag::warn_pragma_expected_colon) << "reduction identifier"; + } + } + SmallVector Vars; - bool IsComma = true; + bool IsComma = !InvalidReductionId; const bool MayHaveTail = (Kind == OMPC_linear || Kind == OMPC_aligned); while (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) && Tok.isNot(tok::annot_pragma_openmp_end))) { @@ -460,10 +533,13 @@ OMPClause *Parser::ParseOpenMPVarListClause(OpenMPClauseKind Kind) { // Parse ')'. T.consumeClose(); - if (Vars.empty() || (MustHaveTail && !TailExpr)) + if (Vars.empty() || (MustHaveTail && !TailExpr) || InvalidReductionId) return nullptr; - return Actions.ActOnOpenMPVarListClause(Kind, Vars, TailExpr, Loc, LOpen, - ColonLoc, Tok.getLocation()); + return Actions.ActOnOpenMPVarListClause( + Kind, Vars, TailExpr, Loc, LOpen, ColonLoc, Tok.getLocation(), + ReductionIdScopeSpec, + ReductionId.isValid() ? Actions.GetNameFromUnqualifiedId(ReductionId) + : DeclarationNameInfo()); } diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index ddedf72684bc..8d6b93c8fc60 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -119,6 +119,10 @@ public: /// attribute in \a DKind directive. DSAVarData hasDSA(VarDecl *D, OpenMPClauseKind CKind, OpenMPDirectiveKind DKind = OMPD_unknown); + /// \brief Checks if the specified variables has \a CKind data-sharing + /// attribute in an innermost \a DKind directive. + DSAVarData hasInnermostDSA(VarDecl *D, OpenMPClauseKind CKind, + OpenMPDirectiveKind DKind); /// \brief Returns currently analyzed directive. OpenMPDirectiveKind getCurrentDirective() const { @@ -154,7 +158,6 @@ DSAStackTy::DSAVarData DSAStackTy::getDSA(StackTy::reverse_iterator Iter, // File-scope or namespace-scope variables referenced in called routines // in the region are shared unless they appear in a threadprivate // directive. - // TODO if (!D->isFunctionOrMethodVarDecl()) DVar.CKind = OMPC_shared; @@ -214,7 +217,6 @@ DSAStackTy::DSAVarData DSAStackTy::getDSA(StackTy::reverse_iterator Iter, // In a task construct, if no default clause is present, a variable that in // the enclosing context is determined to be shared by all implicit tasks // bound to the current team is shared. - // TODO if (DVar.DKind == OMPD_task) { DSAVarData DVarTemp; for (StackTy::reverse_iterator I = std::next(Iter), @@ -399,6 +401,21 @@ DSAStackTy::DSAVarData DSAStackTy::hasDSA(VarDecl *D, OpenMPClauseKind CKind, return DSAVarData(); } +DSAStackTy::DSAVarData DSAStackTy::hasInnermostDSA(VarDecl *D, + OpenMPClauseKind CKind, + OpenMPDirectiveKind DKind) { + assert(DKind != OMPD_unknown && "Directive must be specified explicitly"); + for (auto I = Stack.rbegin(), EE = std::prev(Stack.rend()); I != EE; ++I) { + if (DKind != I->Directive) + continue; + DSAVarData DVar = getDSA(I, D); + if (DVar.CKind == CKind) + return DVar; + return DSAVarData(); + } + return DSAVarData(); +} + void Sema::InitDataSharingAttributesStack() { VarDataSharingAttributesStack = new DSAStackTy(*this); } @@ -702,7 +719,16 @@ public: // A list item that appears in a reduction clause of the innermost // enclosing worksharing or parallel construct may not be accessed in an // explicit task. - // TODO: + DVar = Stack->hasInnermostDSA(VD, OMPC_reduction, OMPD_parallel); + if (DKind == OMPD_task && DVar.CKind == OMPC_reduction) { + ErrorFound = true; + Actions.Diag(ELoc, diag::err_omp_reduction_in_task); + if (DVar.RefExpr) { + Actions.Diag(DVar.RefExpr->getExprLoc(), diag::note_omp_explicit_dsa) + << getOpenMPClauseName(OMPC_reduction); + } + return; + } // Define implicit data-sharing attributes for task. DVar = Stack->getImplicitDSA(VD); @@ -1357,6 +1383,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, case OMPC_firstprivate: case OMPC_lastprivate: case OMPC_shared: + case OMPC_reduction: case OMPC_linear: case OMPC_aligned: case OMPC_copyin: @@ -1532,6 +1559,7 @@ OMPClause *Sema::ActOnOpenMPSimpleClause( case OMPC_firstprivate: case OMPC_lastprivate: case OMPC_shared: + case OMPC_reduction: case OMPC_linear: case OMPC_aligned: case OMPC_copyin: @@ -1617,11 +1645,11 @@ OMPClause *Sema::ActOnOpenMPProcBindClause(OpenMPProcBindClauseKind Kind, OMPProcBindClause(Kind, KindKwLoc, StartLoc, LParenLoc, EndLoc); } -OMPClause * -Sema::ActOnOpenMPVarListClause(OpenMPClauseKind Kind, ArrayRef VarList, - Expr *TailExpr, SourceLocation StartLoc, - SourceLocation LParenLoc, - SourceLocation ColonLoc, SourceLocation EndLoc) { +OMPClause *Sema::ActOnOpenMPVarListClause( + OpenMPClauseKind Kind, ArrayRef VarList, Expr *TailExpr, + SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation EndLoc, CXXScopeSpec &ReductionIdScopeSpec, + const DeclarationNameInfo &ReductionId) { OMPClause *Res = nullptr; switch (Kind) { case OMPC_private: @@ -1636,6 +1664,11 @@ Sema::ActOnOpenMPVarListClause(OpenMPClauseKind Kind, ArrayRef VarList, case OMPC_shared: Res = ActOnOpenMPSharedClause(VarList, StartLoc, LParenLoc, EndLoc); break; + case OMPC_reduction: + Res = + ActOnOpenMPReductionClause(VarList, StartLoc, LParenLoc, ColonLoc, + EndLoc, ReductionIdScopeSpec, ReductionId); + break; case OMPC_linear: Res = ActOnOpenMPLinearClause(VarList, TailExpr, StartLoc, LParenLoc, ColonLoc, EndLoc); @@ -2194,6 +2227,323 @@ OMPClause *Sema::ActOnOpenMPSharedClause(ArrayRef VarList, return OMPSharedClause::Create(Context, StartLoc, LParenLoc, EndLoc, Vars); } +namespace { +class DSARefChecker : public StmtVisitor { + DSAStackTy *Stack; + +public: + bool VisitDeclRefExpr(DeclRefExpr *E) { + if (VarDecl *VD = dyn_cast(E->getDecl())) { + DSAStackTy::DSAVarData DVar = Stack->getTopDSA(VD); + if (DVar.CKind == OMPC_shared && !DVar.RefExpr) + return false; + if (DVar.CKind != OMPC_unknown) + return true; + DSAStackTy::DSAVarData DVarPrivate = Stack->hasDSA(VD, OMPC_private); + DSAStackTy::DSAVarData DVarFirstprivate = + Stack->hasDSA(VD, OMPC_firstprivate); + DSAStackTy::DSAVarData DVarReduction = Stack->hasDSA(VD, OMPC_reduction); + if (DVarPrivate.CKind != OMPC_unknown || + DVarFirstprivate.CKind != OMPC_unknown || + DVarReduction.CKind != OMPC_unknown) + return true; + return false; + } + return false; + } + bool VisitStmt(Stmt *S) { + for (auto Child : S->children()) { + if (Child && Visit(Child)) + return true; + } + return false; + } + DSARefChecker(DSAStackTy *S) : Stack(S) {} +}; +} + +OMPClause *Sema::ActOnOpenMPReductionClause( + ArrayRef VarList, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation EndLoc, + CXXScopeSpec &ReductionIdScopeSpec, + const DeclarationNameInfo &ReductionId) { + // TODO: Allow scope specification search when 'declare reduction' is + // supported. + assert(ReductionIdScopeSpec.isEmpty() && + "No support for scoped reduction identifiers yet."); + + auto DN = ReductionId.getName(); + auto OOK = DN.getCXXOverloadedOperator(); + BinaryOperatorKind BOK = BO_Comma; + + // OpenMP [2.14.3.6, reduction clause] + // C + // reduction-identifier is either an identifier or one of the following + // operators: +, -, *, &, |, ^, && and || + // C++ + // reduction-identifier is either an id-expression or one of the following + // operators: +, -, *, &, |, ^, && and || + // FIXME: Only 'min' and 'max' identifiers are supported for now. + switch (OOK) { + case OO_Plus: + case OO_Minus: + BOK = BO_AddAssign; + break; + case OO_Star: + BOK = BO_MulAssign; + break; + case OO_Amp: + BOK = BO_AndAssign; + break; + case OO_Pipe: + BOK = BO_OrAssign; + break; + case OO_Caret: + BOK = BO_XorAssign; + break; + case OO_AmpAmp: + BOK = BO_LAnd; + break; + case OO_PipePipe: + BOK = BO_LOr; + break; + default: + if (auto II = DN.getAsIdentifierInfo()) { + if (II->isStr("max")) + BOK = BO_GT; + else if (II->isStr("min")) + BOK = BO_LT; + } + break; + } + SourceRange ReductionIdRange; + if (ReductionIdScopeSpec.isValid()) { + ReductionIdRange.setBegin(ReductionIdScopeSpec.getBeginLoc()); + } + ReductionIdRange.setEnd(ReductionId.getEndLoc()); + if (BOK == BO_Comma) { + // Not allowed reduction identifier is found. + Diag(ReductionId.getLocStart(), diag::err_omp_unknown_reduction_identifier) + << ReductionIdRange; + return nullptr; + } + + SmallVector Vars; + for (auto RefExpr : VarList) { + assert(RefExpr && "nullptr expr in OpenMP reduction clause."); + if (isa(RefExpr)) { + // It will be analyzed later. + Vars.push_back(RefExpr); + continue; + } + + if (RefExpr->isTypeDependent() || RefExpr->isValueDependent() || + RefExpr->isInstantiationDependent() || + RefExpr->containsUnexpandedParameterPack()) { + // It will be analyzed later. + Vars.push_back(RefExpr); + continue; + } + + auto ELoc = RefExpr->getExprLoc(); + auto ERange = RefExpr->getSourceRange(); + // OpenMP [2.1, C/C++] + // A list item is a variable or array section, subject to the restrictions + // specified in Section 2.4 on page 42 and in each of the sections + // describing clauses and directives for which a list appears. + // OpenMP [2.14.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. + auto DE = dyn_cast(RefExpr); + if (!DE || !isa(DE->getDecl())) { + Diag(ELoc, diag::err_omp_expected_var_name) << ERange; + continue; + } + auto D = DE->getDecl(); + auto VD = cast(D); + auto Type = VD->getType(); + // 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_reduction_incomplete_type)) + continue; + // OpenMP [2.14.3.6, reduction clause, Restrictions] + // Arrays may not appear in a reduction clause. + if (Type.getNonReferenceType()->isArrayType()) { + Diag(ELoc, diag::err_omp_reduction_type_array) << Type << ERange; + bool IsDecl = + VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly; + Diag(VD->getLocation(), + IsDecl ? diag::note_previous_decl : diag::note_defined_here) + << VD; + continue; + } + // OpenMP [2.14.3.6, reduction clause, Restrictions] + // A list item that appears in a reduction clause must not be + // const-qualified. + if (Type.getNonReferenceType().isConstant(Context)) { + Diag(ELoc, diag::err_omp_const_variable) + << getOpenMPClauseName(OMPC_reduction) << Type << ERange; + 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.6, Restrictions, C/C++, p.4] + // If a list-item is a reference type then it must bind to the same object + // for all threads of the team. + VarDecl *VDDef = VD->getDefinition(); + if (Type->isReferenceType() && VDDef) { + DSARefChecker Check(DSAStack); + if (Check.Visit(VDDef->getInit())) { + Diag(ELoc, diag::err_omp_reduction_ref_type_arg) << ERange; + Diag(VDDef->getLocation(), diag::note_defined_here) << VDDef; + continue; + } + } + // OpenMP [2.14.3.6, reduction clause, Restrictions] + // The type of a list item that appears in a reduction clause must be valid + // for the reduction-identifier. For a max or min reduction in C, the type + // of the list item must be an allowed arithmetic data type: char, int, + // float, double, or _Bool, possibly modified with long, short, signed, or + // unsigned. For a max or min reduction in C++, the type of the list item + // must be an allowed arithmetic data type: char, wchar_t, int, float, + // double, or bool, possibly modified with long, short, signed, or unsigned. + if ((BOK == BO_GT || BOK == BO_LT) && + !(Type->isScalarType() || + (getLangOpts().CPlusPlus && Type->isArithmeticType()))) { + Diag(ELoc, diag::err_omp_clause_not_arithmetic_type_arg) + << getLangOpts().CPlusPlus; + bool IsDecl = + VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly; + Diag(VD->getLocation(), + IsDecl ? diag::note_previous_decl : diag::note_defined_here) + << VD; + continue; + } + if ((BOK == BO_OrAssign || BOK == BO_AndAssign || BOK == BO_XorAssign) && + !getLangOpts().CPlusPlus && Type->isFloatingType()) { + Diag(ELoc, diag::err_omp_clause_floating_type_arg); + bool IsDecl = + VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly; + Diag(VD->getLocation(), + IsDecl ? diag::note_previous_decl : diag::note_defined_here) + << VD; + continue; + } + bool Suppress = getDiagnostics().getSuppressAllDiagnostics(); + getDiagnostics().setSuppressAllDiagnostics(true); + ExprResult ReductionOp = + BuildBinOp(DSAStack->getCurScope(), ReductionId.getLocStart(), BOK, + RefExpr, RefExpr); + getDiagnostics().setSuppressAllDiagnostics(Suppress); + if (ReductionOp.isInvalid()) { + Diag(ELoc, diag::err_omp_reduction_id_not_compatible) << Type + << ReductionIdRange; + bool IsDecl = + VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly; + Diag(VD->getLocation(), + IsDecl ? diag::note_previous_decl : diag::note_defined_here) + << VD; + continue; + } + + // OpenMP [2.14.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.14.3.6, Restrictions, p.3] + // Any number of reduction clauses can be specified on the directive, + // but a list item can appear only once in the reduction clauses for that + // directive. + DSAStackTy::DSAVarData DVar = DSAStack->getTopDSA(VD); + if (DVar.CKind == OMPC_reduction) { + Diag(ELoc, diag::err_omp_once_referenced) + << getOpenMPClauseName(OMPC_reduction); + if (DVar.RefExpr) { + Diag(DVar.RefExpr->getExprLoc(), diag::note_omp_referenced); + } + } else if (DVar.CKind != OMPC_unknown) { + Diag(ELoc, diag::err_omp_wrong_dsa) + << getOpenMPClauseName(DVar.CKind) + << getOpenMPClauseName(OMPC_reduction); + if (DVar.RefExpr) { + Diag(DVar.RefExpr->getExprLoc(), diag::note_omp_explicit_dsa) + << getOpenMPClauseName(DVar.CKind); + } else { + Diag(VD->getLocation(), diag::note_omp_predetermined_dsa) + << getOpenMPClauseName(DVar.CKind); + } + continue; + } + + // OpenMP [2.14.3.6, Restrictions, p.1] + // A list item that appears in a reduction clause of a worksharing + // construct must be shared in the parallel regions to which any of the + // worksharing regions arising from the worksharing construct bind. + // TODO Implement it later. + + CXXRecordDecl *RD = getLangOpts().CPlusPlus + ? Type.getNonReferenceType()->getAsCXXRecordDecl() + : nullptr; + if (RD) { + CXXConstructorDecl *CD = LookupDefaultConstructor(RD); + 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_reduction) << 0; + 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_reduction) << 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); + } + } + + DSAStack->addDSA(VD, DE, OMPC_reduction); + Vars.push_back(DE); + } + + if (Vars.empty()) + return nullptr; + + return OMPReductionClause::Create( + Context, StartLoc, LParenLoc, ColonLoc, EndLoc, Vars, + ReductionIdScopeSpec.getWithLocInContext(Context), ReductionId); +} + OMPClause *Sema::ActOnOpenMPLinearClause(ArrayRef VarList, Expr *Step, SourceLocation StartLoc, SourceLocation LParenLoc, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 830688585483..b0dbbd166d28 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1425,6 +1425,22 @@ public: EndLoc); } + /// \brief Build a new OpenMP 'reduction' clause. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + OMPClause *RebuildOMPReductionClause(ArrayRef VarList, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation ColonLoc, + SourceLocation EndLoc, + CXXScopeSpec &ReductionIdScopeSpec, + const DeclarationNameInfo &ReductionId) { + return getSema().ActOnOpenMPReductionClause( + VarList, StartLoc, LParenLoc, ColonLoc, EndLoc, ReductionIdScopeSpec, + ReductionId); + } + /// \brief Build a new OpenMP 'linear' clause. /// /// By default, performs semantic analysis to build the new OpenMP clause. @@ -6348,10 +6364,8 @@ StmtResult TreeTransform::TransformOMPExecutableDirective( I != E; ++I) { if (*I) { OMPClause *Clause = getDerived().TransformOMPClause(*I); - if (!Clause) { - return StmtError(); - } - TClauses.push_back(Clause); + if (Clause) + TClauses.push_back(Clause); } else { TClauses.push_back(nullptr); } @@ -6361,7 +6375,7 @@ StmtResult TreeTransform::TransformOMPExecutableDirective( } StmtResult AssociatedStmt = getDerived().TransformStmt(D->getAssociatedStmt()); - if (AssociatedStmt.isInvalid()) { + if (AssociatedStmt.isInvalid() || TClauses.size() != Clauses.size()) { return StmtError(); } @@ -6508,6 +6522,31 @@ TreeTransform::TransformOMPSharedClause(OMPSharedClause *C) { C->getLParenLoc(), C->getLocEnd()); } +template +OMPClause * +TreeTransform::TransformOMPReductionClause(OMPReductionClause *C) { + llvm::SmallVector Vars; + Vars.reserve(C->varlist_size()); + for (auto *VE : C->varlists()) { + ExprResult EVar = getDerived().TransformExpr(cast(VE)); + if (EVar.isInvalid()) + return nullptr; + Vars.push_back(EVar.get()); + } + CXXScopeSpec ReductionIdScopeSpec; + ReductionIdScopeSpec.Adopt(C->getQualifierLoc()); + + DeclarationNameInfo NameInfo = C->getNameInfo(); + if (NameInfo.getName()) { + NameInfo = getDerived().TransformDeclarationNameInfo(NameInfo); + if (!NameInfo.getName()) + return nullptr; + } + return getDerived().RebuildOMPReductionClause( + Vars, C->getLocStart(), C->getLParenLoc(), C->getColonLoc(), + C->getLocEnd(), ReductionIdScopeSpec, NameInfo); +} + template OMPClause * TreeTransform::TransformOMPLinearClause(OMPLinearClause *C) { @@ -10017,7 +10056,11 @@ TreeTransform::TransformCapturedStmt(CapturedStmt *S) { } getSema().ActOnCapturedRegionStart(Loc, /*CurScope*/nullptr, S->getCapturedRegionKind(), Params); - StmtResult Body = getDerived().TransformStmt(S->getCapturedStmt()); + StmtResult Body; + { + Sema::CompoundScopeRAII CompoundScope(getSema()); + Body = getDerived().TransformStmt(S->getCapturedStmt()); + } if (Body.isInvalid()) { getSema().ActOnCapturedRegionError(); diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 7a4c8b0282e9..b913ad8b6ad6 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1703,6 +1703,9 @@ OMPClause *OMPClauseReader::readClause() { case OMPC_shared: C = OMPSharedClause::CreateEmpty(Context, Record[Idx++]); break; + case OMPC_reduction: + C = OMPReductionClause::CreateEmpty(Context, Record[Idx++]); + break; case OMPC_linear: C = OMPLinearClause::CreateEmpty(Context, Record[Idx++]); break; @@ -1794,6 +1797,24 @@ void OMPClauseReader::VisitOMPSharedClause(OMPSharedClause *C) { C->setVarRefs(Vars); } +void OMPClauseReader::VisitOMPReductionClause(OMPReductionClause *C) { + C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx)); + C->setColonLoc(Reader->ReadSourceLocation(Record, Idx)); + NestedNameSpecifierLoc NNSL = + Reader->Reader.ReadNestedNameSpecifierLoc(Reader->F, Record, Idx); + DeclarationNameInfo DNI; + Reader->ReadDeclarationNameInfo(DNI, Record, Idx); + C->setQualifierLoc(NNSL); + C->setNameInfo(DNI); + + unsigned NumVars = C->varlist_size(); + SmallVector Vars; + Vars.reserve(NumVars); + for (unsigned i = 0; i != NumVars; ++i) + Vars.push_back(Reader->Reader.ReadSubExpr()); + C->setVarRefs(Vars); +} + void OMPClauseReader::VisitOMPLinearClause(OMPLinearClause *C) { C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx)); C->setColonLoc(Reader->ReadSourceLocation(Record, Idx)); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index c5fb9b170372..108bc7324348 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1735,6 +1735,16 @@ void OMPClauseWriter::VisitOMPSharedClause(OMPSharedClause *C) { Writer->Writer.AddStmt(VE); } +void OMPClauseWriter::VisitOMPReductionClause(OMPReductionClause *C) { + Record.push_back(C->varlist_size()); + Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); + Writer->Writer.AddSourceLocation(C->getColonLoc(), Record); + Writer->Writer.AddNestedNameSpecifierLoc(C->getQualifierLoc(), Record); + Writer->Writer.AddDeclarationNameInfo(C->getNameInfo(), Record); + for (auto *VE : C->varlists()) + Writer->Writer.AddStmt(VE); +} + void OMPClauseWriter::VisitOMPLinearClause(OMPLinearClause *C) { Record.push_back(C->varlist_size()); Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); diff --git a/clang/test/OpenMP/parallel_ast_print.cpp b/clang/test/OpenMP/parallel_ast_print.cpp index 631d179c5888..0415c0ca555c 100644 --- a/clang/test/OpenMP/parallel_ast_print.cpp +++ b/clang/test/OpenMP/parallel_ast_print.cpp @@ -35,9 +35,9 @@ T tmain(T argc, T *argv) { S s; #pragma omp parallel a=2; -#pragma omp parallel default(none), private(argc,b) firstprivate(argv) shared (d) if (argc > 0) num_threads(C) copyin(S::TS) proc_bind(master) +#pragma omp parallel default(none), private(argc,b) firstprivate(argv) shared (d) if (argc > 0) num_threads(C) copyin(S::TS) proc_bind(master) reduction(+:c) reduction(max:e) foo(); -#pragma omp parallel if (C) num_threads(s) proc_bind(close) +#pragma omp parallel if (C) num_threads(s) proc_bind(close) reduction(^:e, f) reduction(&& : g) foo(); return 0; } @@ -48,9 +48,9 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: S s; // CHECK-NEXT: #pragma omp parallel // CHECK-NEXT: a = 2; -// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) num_threads(5) copyin(S::TS) proc_bind(master) +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) num_threads(5) copyin(S::TS) proc_bind(master) reduction(+: c) reduction(max: e) // CHECK-NEXT: foo() -// CHECK-NEXT: #pragma omp parallel if(5) num_threads(s) proc_bind(close) +// CHECK-NEXT: #pragma omp parallel if(5) num_threads(s) proc_bind(close) reduction(^: e,f) reduction(&&: g) // CHECK-NEXT: foo() // CHECK: template long tmain(long argc, long *argv) { // CHECK-NEXT: long b = argc, c, d, e, f, g; @@ -58,9 +58,9 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: S s; // CHECK-NEXT: #pragma omp parallel // CHECK-NEXT: a = 2; -// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) num_threads(1) copyin(S::TS) proc_bind(master) +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) num_threads(1) copyin(S::TS) proc_bind(master) reduction(+: c) reduction(max: e) // CHECK-NEXT: foo() -// CHECK-NEXT: #pragma omp parallel if(1) num_threads(s) proc_bind(close) +// CHECK-NEXT: #pragma omp parallel if(1) num_threads(s) proc_bind(close) reduction(^: e,f) reduction(&&: g) // CHECK-NEXT: foo() // CHECK: template T tmain(T argc, T *argv) { // CHECK-NEXT: T b = argc, c, d, e, f, g; @@ -68,9 +68,9 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: S s; // CHECK-NEXT: #pragma omp parallel // CHECK-NEXT: a = 2; -// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) num_threads(C) copyin(S::TS) proc_bind(master) +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) num_threads(C) copyin(S::TS) proc_bind(master) reduction(+: c) reduction(max: e) // CHECK-NEXT: foo() -// CHECK-NEXT: #pragma omp parallel if(C) num_threads(s) proc_bind(close) +// CHECK-NEXT: #pragma omp parallel if(C) num_threads(s) proc_bind(close) reduction(^: e,f) reduction(&&: g) // CHECK-NEXT: foo() enum Enum { }; @@ -86,8 +86,8 @@ 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) firstprivate(argv) if (argc > 0) num_threads(ee) copyin(a) proc_bind(spread) -// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) if(argc > 0) num_threads(ee) copyin(a) proc_bind(spread) +#pragma omp parallel default(none), private(argc,b) firstprivate(argv) if (argc > 0) num_threads(ee) copyin(a) proc_bind(spread) reduction(| : c, d) reduction(* : e) +// CHECK-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) if(argc > 0) num_threads(ee) copyin(a) proc_bind(spread) reduction(|: c,d) reduction(*: e) foo(); // CHECK-NEXT: foo(); return tmain(b, &b) + tmain(x, &x); diff --git a/clang/test/OpenMP/parallel_reduction_messages.cpp b/clang/test/OpenMP/parallel_reduction_messages.cpp new file mode 100644 index 000000000000..1afed8517fbe --- /dev/null +++ b/clang/test/OpenMP/parallel_reduction_messages.cpp @@ -0,0 +1,215 @@ +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -ferror-limit 100 -o - %s + +void foo() { +} + +bool foobool(int argc) { + return argc; +} + +struct S1; // expected-note {{declared here}} expected-note 4 {{forward declaration of 'S1'}} +extern S1 a; +class S2 { + mutable int a; + S2 &operator +=(const S2 &arg) {return (*this);} +public: + S2():a(0) { } + S2(S2 &s2):a(s2.a) { } + static float S2s; // expected-note 2 {{predetermined as shared}} + static const float S2sc; +}; +const float S2::S2sc = 0; // expected-note 2 {{'S2sc' defined here}} +S2 b; // expected-note 2 {{'b' defined here}} +const S2 ba[5]; // expected-note 2 {{'ba' defined here}} +class S3 { + int a; +public: + S3():a(0) { } + S3(const S3 &s3):a(s3.a) { } + S3 operator +=(const S3 &arg1) {return arg1;} +}; +int operator +=(const S3 &arg1, const S3 &arg2) {return 5;} +S3 c; // expected-note 2 {{'c' defined here}} +const S3 ca[5]; // expected-note 2 {{'ca' defined here}} +extern const int f; // expected-note 4 {{'f' declared here}} +class S4 { // expected-note {{'S4' declared here}} + int a; + S4(); + S4(const S4 &s4); + S4 &operator +=(const S4 &arg) {return (*this);} +public: + S4(int v):a(v) { } +}; +S4 &operator &=(S4 &arg1, S4 &arg2) {return arg1;} +class S5 { + int a; + S5():a(0) {} + S5(const S5 &s5):a(s5.a) { } + S5 &operator +=(const S5 &arg); +public: + S5(int v):a(v) { } +}; +class S6 { + int a; + public: + S6():a(6){ } + operator int() { return 6; } +} o; // expected-note 2 {{'o' defined here}} + +S3 h, k; +#pragma omp threadprivate(h) // expected-note 2 {{defined as threadprivate or thread local}} + +template // expected-note {{declared here}} +T tmain(T argc) { // expected-note 2 {{'argc' defined here}} + const T d = T(); // expected-note 4 {{'d' defined here}} + const T da[5] = { T() }; // expected-note 2 {{'da' defined here}} + T qa[5] = { T() }; + T i; + T &j = i; // expected-note 4 {{'j' defined here}} + S3 &p = k; // expected-note 2 {{'p' defined here}} + const T &r = da[(int)i]; // expected-note 2 {{'r' defined here}} + T &q = qa[(int)i]; // expected-note 2 {{'q' defined here}} + T fl; // expected-note {{'fl' defined here}} + #pragma omp parallel reduction // expected-error {{expected '(' after 'reduction'}} + foo(); + #pragma omp parallel reduction + // expected-error {{expected '(' after 'reduction'}} expected-warning {{extra tokens at the end of '#pragma omp parallel' are ignored}} + foo(); + #pragma omp parallel reduction ( // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected ')'}} expected-note {{to match this '('}} + foo(); + #pragma omp parallel reduction (- // expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + foo(); + #pragma omp parallel reduction () // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}} + foo(); + #pragma omp parallel reduction (*) // expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected expression}} + foo(); + #pragma omp parallel reduction (\) // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}} + foo(); + #pragma omp parallel reduction (&: argc // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{variable of type 'float' is not valid for specified reduction operation}} + foo(); + #pragma omp parallel reduction (| :argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{variable of type 'float' is not valid for specified reduction operation}} + foo(); + #pragma omp parallel reduction (|| :argc ? i : argc) // expected-error 2 {{expected variable name}} + foo(); + #pragma omp parallel reduction (foo:argc) //expected-error {{incorrect reduction identifier, expected one of '+', '-', '*', '&', '|', '^', '&&', '||', 'min' or 'max'}} + foo(); + #pragma omp parallel reduction (&& :argc) + foo(); + #pragma omp parallel reduction (^ : T) // expected-error {{'T' does not refer to a value}} + foo(); + #pragma omp parallel reduction (+ : a, b, c, d, f) // expected-error {{reduction variable with incomplete type 'S1'}} expected-error 3 {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel reduction (min : a, b, c, d, f) // expected-error {{reduction variable with incomplete type 'S1'}} expected-error 2 {{arguments of OpenMP clause 'reduction' for 'min' or 'max' must be of arithmetic type}} expected-error 3 {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel reduction (max : qa[1]) // expected-error 2 {{expected variable name}} + foo(); + #pragma omp parallel reduction(+ : ba) // expected-error {{a reduction variable with array type 'const S2 [5]'}} + foo(); + #pragma omp parallel reduction(* : ca) // expected-error {{a reduction variable with array type 'const S3 [5]'}} + foo(); + #pragma omp parallel reduction(- : da) // expected-error {{a reduction variable with array type 'const int [5]'}} expected-error {{a reduction variable with array type 'const float [5]'}} + foo(); + #pragma omp parallel reduction(^ : fl) // expected-error {{variable of type 'float' is not valid for specified reduction operation}} + foo(); + #pragma omp parallel reduction(&& : S2::S2s) // expected-error {{shared variable cannot be reduction}} + foo(); + #pragma omp parallel reduction(&& : S2::S2sc) // expected-error {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel reduction(+ : h, k) // expected-error {{threadprivate or thread local variable cannot be reduction}} + foo(); + #pragma omp parallel reduction(+ : o) // expected-error {{variable of type 'class S6' is not valid for specified reduction operation}} + foo(); + #pragma omp parallel private(i), reduction(+ : j), reduction(+:q) // expected-error 4 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}} + foo(); + #pragma omp parallel private(k) + #pragma omp parallel reduction(+ : p), reduction(+ : p) // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}} + foo(); + #pragma omp parallel reduction(+ : p), reduction(+ : p) // expected-error 3 {{variable can appear only once in OpenMP 'reduction' clause}} expected-note 3 {{previously referenced here}} + foo(); + #pragma omp parallel reduction(+ : r) // expected-error 2 {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel shared(i) + #pragma omp parallel reduction(min : i) + #pragma omp parallel reduction(max : j) // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}} + foo(); + + return T(); +} + +int main(int argc, char **argv) { + const int d = 5; // expected-note 2 {{'d' defined here}} + const int da[5] = { 0 }; // expected-note {{'da' defined here}} + int qa[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 2 {{'j' defined here}} + S3 &p = k; // expected-note 2 {{'p' defined here}} + const int &r = da[i]; // expected-note {{'r' defined here}} + int &q = qa[i]; // expected-note {{'q' defined here}} + float fl; // expected-note {{'fl' defined here}} + #pragma omp parallel reduction // expected-error {{expected '(' after 'reduction'}} + foo(); + #pragma omp parallel reduction + // expected-error {{expected '(' after 'reduction'}} expected-warning {{extra tokens at the end of '#pragma omp parallel' are ignored}} + foo(); + #pragma omp parallel reduction ( // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected ')'}} expected-note {{to match this '('}} + foo(); + #pragma omp parallel reduction (- // expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + foo(); + #pragma omp parallel reduction () // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}} + foo(); + #pragma omp parallel reduction (*) // expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected expression}} + foo(); + #pragma omp parallel reduction (\) // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}} + foo(); + #pragma omp parallel reduction (foo: argc // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{incorrect reduction identifier, expected one of '+', '-', '*', '&', '|', '^', '&&', '||', 'min' or 'max'}} + foo(); + #pragma omp parallel reduction (| :argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + foo(); + #pragma omp parallel reduction (|| :argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} + foo(); + #pragma omp parallel reduction (~:argc) // expected-error {{expected unqualified-id}} + foo(); + #pragma omp parallel reduction (&& :argc) + foo(); + #pragma omp parallel reduction (^ : S1) // expected-error {{'S1' does not refer to a value}} + foo(); + #pragma omp parallel reduction (+ : a, b, c, d, f) // expected-error {{reduction variable with incomplete type 'S1'}} expected-error 2 {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel reduction (min : a, b, c, d, f) // expected-error {{reduction variable with incomplete type 'S1'}} expected-error 2 {{arguments of OpenMP clause 'reduction' for 'min' or 'max' must be of arithmetic type}} expected-error 2 {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel reduction (max : argv[1]) // expected-error {{expected variable name}} + foo(); + #pragma omp parallel reduction(+ : ba) // expected-error {{a reduction variable with array type 'const S2 [5]'}} + foo(); + #pragma omp parallel reduction(* : ca) // expected-error {{a reduction variable with array type 'const S3 [5]'}} + foo(); + #pragma omp parallel reduction(- : da) // expected-error {{a reduction variable with array type 'const int [5]'}} + foo(); + #pragma omp parallel reduction(^ : fl) // expected-error {{variable of type 'float' is not valid for specified reduction operation}} + foo(); + #pragma omp parallel reduction(&& : S2::S2s) // expected-error {{shared variable cannot be reduction}} + foo(); + #pragma omp parallel reduction(&& : S2::S2sc) // expected-error {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel reduction(& : e, g) // expected-error {{reduction variable must have an accessible, unambiguous default constructor}} expected-error {{variable of type 'S5' is not valid for specified reduction operation}} + foo(); + #pragma omp parallel reduction(+ : h, k) // expected-error {{threadprivate or thread local variable cannot be reduction}} + foo(); + #pragma omp parallel reduction(+ : o) // expected-error {{variable of type 'class S6' is not valid for specified reduction operation}} + foo(); + #pragma omp parallel private(i), reduction(+ : j), reduction(+:q) // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}} + foo(); + #pragma omp parallel private(k) + #pragma omp parallel reduction(+ : p), reduction(+ : p) // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}} + foo(); + #pragma omp parallel reduction(+ : p), reduction(+ : p) // expected-error {{variable can appear only once in OpenMP 'reduction' clause}} expected-note {{previously referenced here}} + foo(); + #pragma omp parallel reduction(+ : r) // expected-error {{const-qualified variable cannot be reduction}} + foo(); + #pragma omp parallel shared(i) + #pragma omp parallel reduction(min : i) + #pragma omp parallel reduction(max : j) // expected-error {{argument of OpenMP clause 'reduction' must reference the same object in all threads}} + foo(); + + return tmain(argc) + tmain(fl); // expected-note {{in instantiation of function template specialization 'tmain' requested here}} expected-note {{in instantiation of function template specialization 'tmain' requested here}} +} diff --git a/clang/test/OpenMP/simd_aligned_messages.cpp b/clang/test/OpenMP/simd_aligned_messages.cpp index f6bdcdd19627..84cf40c1731b 100644 --- a/clang/test/OpenMP/simd_aligned_messages.cpp +++ b/clang/test/OpenMP/simd_aligned_messages.cpp @@ -52,13 +52,14 @@ template T test_template(T* arr, N num) { // expected-error@+1 {{argument to 'aligned' clause must be a positive integer value}} #pragma omp simd aligned(arr:L) for (i = 0; i < num; ++i) { - T cur = arr[ind2]; + T cur = arr[(int)ind2]; ind2 += L; sum += cur; } // expected-error@+1 {{argument of aligned clause should be array, pointer, reference to array or reference to pointer, not 'int'}} #pragma omp simd aligned(num:4) for (i = 0; i < num; ++i); + return T(); } template int test_warn() { diff --git a/clang/test/OpenMP/simd_linear_messages.cpp b/clang/test/OpenMP/simd_linear_messages.cpp index e90bc69444a7..b8b783107993 100644 --- a/clang/test/OpenMP/simd_linear_messages.cpp +++ b/clang/test/OpenMP/simd_linear_messages.cpp @@ -50,10 +50,11 @@ template T test_template(T* arr, N num) { // expected-error@+1 {{argument of a linear clause should be of integral or pointer type}} #pragma omp simd linear(ind2:L) for (i = 0; i < num; ++i) { - T cur = arr[ind2]; + T cur = arr[(int)ind2]; ind2 += L; sum += cur; } + return T(); } template int test_warn() { diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 4d296d46b73a..962e3a340668 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1963,6 +1963,9 @@ void OMPClauseEnqueue::VisitOMPLastprivateClause( void OMPClauseEnqueue::VisitOMPSharedClause(const OMPSharedClause *C) { VisitOMPClauseList(C); } +void OMPClauseEnqueue::VisitOMPReductionClause(const OMPReductionClause *C) { + VisitOMPClauseList(C); +} void OMPClauseEnqueue::VisitOMPLinearClause(const OMPLinearClause *C) { VisitOMPClauseList(C); Visitor->AddStmt(C->getStep());