Index: include/clang/AST/DataRecursiveASTVisitor.h =================================================================== --- include/clang/AST/DataRecursiveASTVisitor.h +++ include/clang/AST/DataRecursiveASTVisitor.h @@ -1390,6 +1390,14 @@ } }) +DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, { + TRY_TO(TraverseStmt(D->getCombiner())); + if (auto *Initializer = D->getInitializer()) + TRY_TO(TraverseStmt(Initializer)); + TRY_TO(TraverseType(D->getType())); + return true; +}) + // A helper method for TemplateDecl's children. template bool RecursiveASTVisitor::TraverseTemplateParameterListHelper( Index: include/clang/AST/DeclBase.h =================================================================== --- include/clang/AST/DeclBase.h +++ include/clang/AST/DeclBase.h @@ -161,7 +161,10 @@ /// This declaration is a function-local extern declaration of a /// variable or function. This may also be IDNS_Ordinary if it /// has been declared outside any function. - IDNS_LocalExtern = 0x0800 + IDNS_LocalExtern = 0x0800, + + /// This declaration is an OpenMP user defined reduction construction. + IDNS_OMPReduction = 0x1000 }; /// ObjCDeclQualifier - 'Qualifiers' written next to the return and @@ -251,7 +254,7 @@ SourceLocation Loc; /// DeclKind - This indicates which class this is. - unsigned DeclKind : 8; + unsigned DeclKind : 7; /// InvalidDecl - This indicates a semantic error occurred. unsigned InvalidDecl : 1; @@ -291,7 +294,7 @@ unsigned Hidden : 1; /// IdentifierNamespace - This specifies what IDNS_* namespace this lives in. - unsigned IdentifierNamespace : 12; + unsigned IdentifierNamespace : 13; /// \brief If 0, we have not computed the linkage of this declaration. /// Otherwise, it is the linkage + 1. @@ -1113,6 +1116,7 @@ /// ObjCContainerDecl /// LinkageSpecDecl /// BlockDecl +/// OMPDeclareReductionDecl /// class DeclContext { /// DeclKind - This indicates which class this is. Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -1561,6 +1561,14 @@ CXXBasePath &Path, DeclarationName Name); /// \brief Base-class lookup callback that determines whether there exists + /// an OpenMP declare reduction member with the given name. + /// + /// This callback can be used with \c lookupInBases() to find members + /// of the given name within a C++ class hierarchy. + static bool FindOMPReductionMember(const CXXBaseSpecifier *Specifier, + CXXBasePath &Path, DeclarationName Name); + + /// \brief Base-class lookup callback that determines whether there exists /// a member with the given name that can be used in a nested-name-specifier. /// /// This callback can be used with \c lookupInBases() to find members of Index: include/clang/AST/DeclOpenMP.h =================================================================== --- include/clang/AST/DeclOpenMP.h +++ include/clang/AST/DeclOpenMP.h @@ -15,11 +15,13 @@ #ifndef LLVM_CLANG_AST_DECLOPENMP_H #define LLVM_CLANG_AST_DECLOPENMP_H -#include "clang/AST/DeclBase.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/AST/Type.h" #include "llvm/ADT/ArrayRef.h" namespace clang { -class Expr; /// \brief This represents '#pragma omp threadprivate ...' directive. /// For example, in the following, both 'a' and 'A::b' are threadprivate: @@ -85,6 +87,83 @@ static bool classofKind(Kind K) { return K == OMPThreadPrivate; } }; +/// \brief This represents '#pragma omp declare reduction ...' directive. +/// For example, in the following, declared reduction 'foo' for types 'int' and +/// 'float': +/// +/// \code +/// #pragma omp declare reduction (foo : int,float : omp_out += omp_in) \ +/// initializer (omp_priv = 0) +/// \endcode +/// +/// Here 'omp_out += omp_in' is a combiner and 'omp_priv = 0' is an initializer. +class OMPDeclareReductionDecl : public NamedDecl, public DeclContext { +private: + friend class ASTDeclReader; + /// \brief Combiner for declare reduction construct. + Expr *Combiner; + /// \brief Initializer for declare reduction construct. + Expr *Initializer; + /// \brief Reference to the previous declare reduction construct in the same + /// scope with the same name. Required for proper templates instantiation if + /// the declare reduction construct is declared inside compound statement. + LazyDeclPtr PrevDeclInScope; + /// \brief Type of declare reduction construct. + QualType Ty; + + virtual void anchor(); + + OMPDeclareReductionDecl(Kind DK, DeclContext *DC, SourceLocation L, + DeclarationName Name, QualType Ty, + OMPDeclareReductionDecl *PrevDeclInScope) + : NamedDecl(DK, DC, L, Name), DeclContext(DK), Combiner(nullptr), + Initializer(nullptr), PrevDeclInScope(PrevDeclInScope), Ty(Ty) {} + + void setPrevDeclInScope(OMPDeclareReductionDecl *Prev) { + PrevDeclInScope = Prev; + } + void setType(QualType T) { Ty = T; } + +public: + /// \brief Create declare reduction node. + static OMPDeclareReductionDecl * + Create(ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName Name, + QualType Ty, OMPDeclareReductionDecl *PrevDeclInScope); + /// \brief Create deserialized declare reduction node. + static OMPDeclareReductionDecl *CreateDeserialized(ASTContext &C, + unsigned ID); + + /// \brief Get combiner expression of the declare reduction construct. + Expr *getCombiner() { return Combiner; } + const Expr *getCombiner() const { return Combiner; } + /// \brief Set combiner expression for the declare reduction construct. + void setCombiner(Expr *E) { Combiner = E; } + + /// \brief Get initializer expression (if specified) of the declare reduction + /// construct. + Expr *getInitializer() { return Initializer; } + const Expr *getInitializer() const { return Initializer; } + /// \brief Set initializer expression for the declare reduction construct. + void setInitializer(Expr *E) { Initializer = E; } + + /// \brief Get reference to previous declare reduction construct in the same + /// scope with the same name. + OMPDeclareReductionDecl *getPrevDeclInScope(); + const OMPDeclareReductionDecl *getPrevDeclInScope() const; + + QualType getType() const { return Ty; } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == OMPDeclareReduction; } + static DeclContext *castToDeclContext(const OMPDeclareReductionDecl *D) { + return static_cast(const_cast(D)); + } + static OMPDeclareReductionDecl *castFromDeclContext(const DeclContext *DC) { + return static_cast( + const_cast(DC)); + } +}; + } // end namespace clang #endif Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -1470,6 +1470,14 @@ } }) +DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, { + TRY_TO(TraverseStmt(D->getCombiner())); + if (auto *Initializer = D->getInitializer()) + TRY_TO(TraverseStmt(Initializer)); + TRY_TO(TraverseType(D->getType())); + return true; +}) + // A helper method for TemplateDecl's children. template bool RecursiveASTVisitor::TraverseTemplateParameterListHelper( Index: include/clang/Basic/DeclNodes.td =================================================================== --- include/clang/Basic/DeclNodes.td +++ include/clang/Basic/DeclNodes.td @@ -71,6 +71,7 @@ def ObjCImplementation : DDecl; def ObjCProperty : DDecl; def ObjCCompatibleAlias : DDecl; + def OMPDeclareReduction : DDecl, DeclContext; def LinkageSpec : Decl, DeclContext; def ObjCPropertyImpl : Decl; def FileScopeAsm : Decl; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -991,6 +991,8 @@ "'#pragma omp %0' cannot be an immediate substatement">; def err_omp_expected_identifier_for_critical : Error< "expected identifier specifying the name of the 'omp critical' directive">; +def err_omp_expected_reduction_identifier : Error< + "expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'">; // Pragma loop support. def err_pragma_loop_missing_argument : Error< Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7723,6 +7723,9 @@ "parent region for 'omp %select{cancellation point/cancel}0' construct cannot be nowait">; def err_omp_parent_cancel_region_ordered : Error< "parent region for 'omp %select{cancellation point/cancel}0' construct cannot be ordered">; +def err_omp_reduction_wrong_type : Error<"reduction type cannot be %select{qualified with 'const', 'volatile' or 'restrict'|a function|a reference|an array}0 type">; +def err_omp_wrong_var_in_declare_reduction : Error<"only %select{'omp_priv' or 'omp_orig'|'omp_in' or 'omp_out'}0 variables are allowed in %select{initializer|combiner}0 expression">; +def err_omp_declare_reduction_redefinition : Error<"redefinition of user-defined reduction for type %0">; def err_omp_array_section_use : Error<"OpenMP array section is not allowed here">; def err_omp_typecheck_section_value : Error< "subscripted value is not an array or pointer">; Index: include/clang/Basic/OpenMPKinds.def =================================================================== --- include/clang/Basic/OpenMPKinds.def +++ include/clang/Basic/OpenMPKinds.def @@ -112,6 +112,7 @@ OPENMP_DIRECTIVE_EXT(parallel_sections, "parallel sections") OPENMP_DIRECTIVE_EXT(for_simd, "for simd") OPENMP_DIRECTIVE_EXT(cancellation_point, "cancellation point") +OPENMP_DIRECTIVE_EXT(declare_reduction, "declare reduction") // OpenMP clauses. OPENMP_CLAUSE(if, OMPIfClause) Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -2424,7 +2424,9 @@ //===--------------------------------------------------------------------===// // OpenMP: Directives and clauses. /// \brief Parses declarative OpenMP directives. - DeclGroupPtrTy ParseOpenMPDeclarativeDirective(); + DeclGroupPtrTy ParseOpenMPDeclarativeDirective(AccessSpecifier AS); + /// \brief Parse 'omp declare reduction' construct. + DeclGroupPtrTy ParseOpenMPDeclareReductionDirective(AccessSpecifier AS); /// \brief Parses simple list of variables. /// /// \param Kind Kind of the directive. Index: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -104,6 +104,9 @@ /// \brief Whether a statement was dropped because it was invalid. bool HasDroppedStmt; + /// \brief True if current scope is for OpenMP declare reduction combiner. + bool HasOMPDeclareReductionCombiner; + /// A flag that is set when parsing a method that must call super's /// implementation, such as \c -dealloc, \c -finalize, or any method marked /// with \c __attribute__((objc_requires_super)). @@ -330,6 +333,10 @@ HasDroppedStmt = true; } + void setHasOMPDeclareReductionCombiner() { + HasOMPDeclareReductionCombiner = true; + } + void setHasCXXTry(SourceLocation TryLoc) { setHasBranchProtectedScope(); FirstCXXTryLoc = TryLoc; @@ -352,6 +359,7 @@ HasBranchIntoScope(false), HasIndirectGoto(false), HasDroppedStmt(false), + HasOMPDeclareReductionCombiner(false), ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false), Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -144,6 +144,7 @@ class ObjCPropertyDecl; class ObjCProtocolDecl; class OMPThreadPrivateDecl; + class OMPDeclareReductionDecl; class OMPClause; class OverloadCandidateSet; class OverloadExpr; @@ -2642,6 +2643,8 @@ LookupObjCProtocolName, /// Look up implicit 'self' parameter of an objective-c method. LookupObjCImplicitSelfParam, + /// \brief Look up the name of an OpenMP user-defined reduction operation. + LookupOMPReductionName, /// \brief Look up any declaration with any name. LookupAnyName }; @@ -7764,6 +7767,27 @@ OMPThreadPrivateDecl *CheckOMPThreadPrivateDecl( SourceLocation Loc, ArrayRef VarList); + /// \brief Check if the specified type is allowed to be used in 'omp declare + /// reduction' construct. + QualType ActOnOpenMPDeclareReductionType(SourceLocation TyLoc, + TypeResult ParsedType); + /// \brief Called on start of '#pragma omp declare reduction'. + DeclGroupPtrTy ActOnOpenMPDeclareReductionDirectiveStart( + Scope *S, DeclContext *DC, DeclarationName Name, + ArrayRef> ReductionTypes, + AccessSpecifier AS, Decl *PrevDeclInScope = nullptr); + /// \brief Initialize declare reduction construct initializer. + void ActOnOpenMPDeclareReductionCombinerStart(Scope *S, Decl *D); + /// \brief Finish current declare reduction construct initializer. + void ActOnOpenMPDeclareReductionCombinerEnd(Decl *D, Expr *Combiner); + /// \brief Initialize declare reduction construct initializer. + void ActOnOpenMPDeclareReductionInitializerStart(Scope *S, Decl *D); + /// \brief Finish current declare reduction construct initializer. + void ActOnOpenMPDeclareReductionInitializerEnd(Decl *D, Expr *Initializer); + /// \brief Called on well-formed '#pragma omp declare reduction'. + DeclGroupPtrTy + ActOnOpenMPDeclareReductionDirectiveEnd(Scope *S, + DeclGroupPtrTy DeclReductions); /// \brief Initialization of captured region for OpenMP region. void ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope); Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1137,6 +1137,8 @@ DECL_EMPTY, /// \brief An ObjCTypeParamDecl record. DECL_OBJC_TYPE_PARAM, + /// \brief An OMPDeclareReductionDecl record. + DECL_OMP_DECLARE_REDUCTION, }; /// \brief Record codes for each kind of statement or expression. Index: lib/AST/CXXInheritance.cpp =================================================================== --- lib/AST/CXXInheritance.cpp +++ lib/AST/CXXInheritance.cpp @@ -405,6 +405,21 @@ return false; } +bool CXXRecordDecl::FindOMPReductionMember(const CXXBaseSpecifier *Specifier, + CXXBasePath &Path, + DeclarationName Name) { + RecordDecl *BaseRecord = + Specifier->getType()->castAs()->getDecl(); + + for (Path.Decls = BaseRecord->lookup(Name); !Path.Decls.empty(); + Path.Decls = Path.Decls.slice(1)) { + if (Path.Decls.front()->isInIdentifierNamespace(IDNS_OMPReduction)) + return true; + } + + return false; +} + bool CXXRecordDecl:: FindNestedNameSpecifierMember(const CXXBaseSpecifier *Specifier, CXXBasePath &Path, Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -18,6 +18,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -1459,6 +1460,10 @@ if (OldK == NewK) return true; + // Declare reduction is always replaceable. + if (OMPDeclareReductionDecl::classofKind(NewK)) + return false; + // A compatibility alias for a class can be replaced by an interface. if (ObjCCompatibleAliasDecl::classofKind(OldK) && ObjCInterfaceDecl::classofKind(NewK)) Index: lib/AST/DeclBase.cpp =================================================================== --- lib/AST/DeclBase.cpp +++ lib/AST/DeclBase.cpp @@ -625,6 +625,9 @@ case TemplateTemplateParm: return IDNS_Ordinary | IDNS_Tag | IDNS_Type; + case OMPDeclareReduction: + return IDNS_OMPReduction; + // Never have names. case Friend: case FriendTemplate: @@ -951,6 +954,7 @@ case Decl::LinkageSpec: case Decl::Block: case Decl::Captured: + case Decl::OMPDeclareReduction: // There is only one DeclContext for these entities. return this; Index: lib/AST/DeclOpenMP.cpp =================================================================== --- lib/AST/DeclOpenMP.cpp +++ lib/AST/DeclOpenMP.cpp @@ -52,3 +52,35 @@ std::copy(VL.begin(), VL.end(), Vars); } +//===----------------------------------------------------------------------===// +// OMPDeclareReductionDecl Implementation. +//===----------------------------------------------------------------------===// + +void OMPDeclareReductionDecl::anchor() {} + +OMPDeclareReductionDecl *OMPDeclareReductionDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName Name, + QualType T, OMPDeclareReductionDecl *PrevDeclInScope) { + OMPDeclareReductionDecl *D = new (C, DC) OMPDeclareReductionDecl( + OMPDeclareReduction, DC, L, Name, T, PrevDeclInScope); + return D; +} + +OMPDeclareReductionDecl * +OMPDeclareReductionDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + OMPDeclareReductionDecl *D = new (C, ID) OMPDeclareReductionDecl( + OMPDeclareReduction, /*DC=*/nullptr, SourceLocation(), DeclarationName(), + QualType(), /*PrevDeclInScope=*/nullptr); + return D; +} + +OMPDeclareReductionDecl *OMPDeclareReductionDecl::getPrevDeclInScope() { + return cast_or_null( + PrevDeclInScope.get(getASTContext().getExternalSource())); +} +const OMPDeclareReductionDecl * +OMPDeclareReductionDecl::getPrevDeclInScope() const { + return cast_or_null( + PrevDeclInScope.get(getASTContext().getExternalSource())); +} + Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -92,6 +92,7 @@ void VisitUsingDecl(UsingDecl *D); void VisitUsingShadowDecl(UsingShadowDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); + void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D); void PrintTemplateParameters(const TemplateParameterList *Params, const TemplateArgumentList *Args = nullptr); @@ -333,7 +334,7 @@ // FIXME: Need to be able to tell the DeclPrinter when const char *Terminator = nullptr; - if (isa(*D)) + if (isa(*D) || isa(*D)) Terminator = nullptr; else if (isa(*D) && cast(*D)->isThisDeclarationADefinition()) @@ -1358,3 +1359,34 @@ } } +void DeclPrinter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + if (!D->isInvalidDecl()) { + Out << "#pragma omp declare reduction ("; + if (D->getDeclName().getNameKind() == DeclarationName::CXXOperatorName) { + static const char *const OperatorNames[NUM_OVERLOADED_OPERATORS] = { + nullptr, +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ + Spelling, +#include "clang/Basic/OperatorKinds.def" + }; + const char *OpName = + OperatorNames[D->getDeclName().getCXXOverloadedOperator()]; + assert(OpName && "not an overloaded operator"); + Out << OpName; + } else { + assert(D->getDeclName().isIdentifier()); + D->printName(Out); + } + Out << " : "; + D->getType().print(Out, Policy); + Out << " : "; + D->getCombiner()->printPretty(Out, 0, Policy, 0); + Out << ")"; + if (auto *Init = D->getInitializer()) { + Out << " initializer("; + Init->printPretty(Out, 0, Policy, 0); + Out << ")"; + } + } +} + Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -66,8 +67,9 @@ } const DeclContext *DC = D->getDeclContext(); - if (const CapturedDecl *CD = dyn_cast(DC)) - return getEffectiveDeclContext(CD); + if (isa(DC) || isa(DC)) { + return getEffectiveDeclContext(cast(DC)); + } if (const auto *VD = dyn_cast(D)) if (VD->isExternC()) Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -19,6 +19,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -58,8 +59,9 @@ } const DeclContext *DC = D->getDeclContext(); - if (const CapturedDecl *CD = dyn_cast(DC)) - return getEffectiveDeclContext(CD); + if (isa(DC) || isa(DC)) { + return getEffectiveDeclContext(cast(DC)); + } return DC; } Index: lib/Basic/OpenMPKinds.cpp =================================================================== --- lib/Basic/OpenMPKinds.cpp +++ lib/Basic/OpenMPKinds.cpp @@ -399,6 +399,7 @@ case OMPD_taskwait: case OMPD_taskgroup: case OMPD_cancellation_point: + case OMPD_declare_reduction: break; } return false; Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -21,6 +21,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" @@ -114,6 +115,9 @@ return EmitVarDecl(VD); } + case Decl::OMPDeclareReduction: + return CGM.EmitOMPDeclareReduction(cast(&D)); + case Decl::Typedef: // typedef int X; case Decl::TypeAlias: { // using X = int; [C++0x] const TypedefNameDecl &TD = cast(D); @@ -1803,3 +1807,9 @@ if (D.hasAttr()) EmitVarAnnotations(&D, DeclPtr.getPointer()); } + +void CodeGenModule::EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D) { + llvm_unreachable("Codegen for 'omp declare reduction' is not supported yet."); +} + + Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -21,6 +21,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Mangle.h" #include "clang/Basic/ABI.h" @@ -1110,6 +1111,9 @@ /// \param D Threadprivate declaration. void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D); + /// \brief Emit a code for declare reduction construct. + void EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D); + /// Returns whether the given record is blacklisted from control flow /// integrity checks. bool IsCFIBlacklistedRecord(const CXXRecordDecl *RD); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -3564,6 +3564,10 @@ break; } + case Decl::OMPDeclareReduction: + EmitOMPDeclareReduction(cast(D)); + break; + default: // Make sure we handled everything we should, every other kind is a // non-top-level decl. FIXME: Would be nice to have an isTopLevelDeclKind Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -3610,13 +3610,11 @@ } if (Tok.is(tok::annot_pragma_openmp)) { - // Result can be ignored, because it must be always empty. - auto Res = ParseOpenMPDeclarativeDirective(); - assert(!Res); - // Silence possible warnings. - (void)Res; + // There may be declared reduction operator inside structure/union. + (void)ParseOpenMPDeclarativeDirective(AS_public); continue; } + if (!Tok.is(tok::at)) { auto CFieldCallback = [&](ParsingFieldDeclarator &FD) { // Install the declarator into the current TagDecl. Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -2897,7 +2897,7 @@ } if (Tok.is(tok::annot_pragma_openmp)) - return ParseOpenMPDeclarativeDirective(); + return ParseOpenMPDeclarativeDirective(AS); // Parse all the comma separated declarators. return ParseCXXClassMemberDeclaration(AS, AccessAttrs.getList()); Index: lib/Parse/ParseOpenMP.cpp =================================================================== --- lib/Parse/ParseOpenMP.cpp +++ lib/Parse/ParseOpenMP.cpp @@ -33,6 +33,8 @@ const OpenMPDirectiveKind F[][3] = { {OMPD_unknown /*cancellation*/, OMPD_unknown /*point*/, OMPD_cancellation_point}, + {OMPD_unknown /*declare*/, OMPD_unknown /*reduction*/, + OMPD_declare_reduction}, {OMPD_target, OMPD_unknown /*data*/, OMPD_target_data}, {OMPD_for, OMPD_simd, OMPD_for_simd}, {OMPD_parallel, OMPD_for, OMPD_parallel_for}, @@ -46,13 +48,14 @@ bool TokenMatched = false; for (unsigned i = 0; i < llvm::array_lengthof(F); ++i) { - if (!Tok.isAnnotation() && DKind == OMPD_unknown) { + if (!Tok.isAnnotation() && DKind == OMPD_unknown) TokenMatched = - (i == 0) && - !P.getPreprocessor().getSpelling(Tok).compare("cancellation"); - } else { + ((i == 0) && + !P.getPreprocessor().getSpelling(Tok).compare("cancellation")) || + ((i == 1) && + !P.getPreprocessor().getSpelling(Tok).compare("declare")); + else TokenMatched = DKind == F[i][0] && DKind != OMPD_unknown; - } if (TokenMatched) { Tok = P.getPreprocessor().LookAhead(0); @@ -61,15 +64,15 @@ TokenIsAnnotation ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok)); - - if (!TokenIsAnnotation && SDKind == OMPD_unknown) { + if (!Tok.isAnnotation() && SDKind == OMPD_unknown) TokenMatched = ((i == 0) && !P.getPreprocessor().getSpelling(Tok).compare("point")) || - ((i == 1) && !P.getPreprocessor().getSpelling(Tok).compare("data")); - } else { + ((i == 1) && + !P.getPreprocessor().getSpelling(Tok).compare("reduction")) || + ((i == 2) && !P.getPreprocessor().getSpelling(Tok).compare("data")); + else TokenMatched = SDKind == F[i][1] && SDKind != OMPD_unknown; - } if (TokenMatched) { P.ConsumeToken(); @@ -80,12 +83,223 @@ return DKind; } +static DeclarationName parseOpenMPReductionId(Parser &P) { + const Token Tok = P.getCurToken(); + Sema &Actions = P.getActions(); + OverloadedOperatorKind OOK = OO_None; + switch (Tok.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; + case tok::identifier: // identifier + break; + default: + P.Diag(Tok.getLocation(), diag::err_omp_expected_reduction_identifier); + P.SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end, + Parser::StopBeforeMatch); + return DeclarationName(); + } + P.ConsumeToken(); + auto &DeclNames = Actions.getASTContext().DeclarationNames; + return OOK == OO_None ? DeclNames.getIdentifier(Tok.getIdentifierInfo()) + : DeclNames.getCXXOperatorName(OOK); +} + +/// \brief Parse 'omp declare reduction' construct. +/// +/// declare-reduction-directive: +/// annot_pragma_openmp 'declare' 'reduction' +/// '(' ':' {',' } ':' ')' +/// ['initializer' '(' ('omp_priv' '=' )| ')'] +/// annot_pragma_openmp_end +/// is either a base language identifier or one of the following +/// operators: '+', '-', '*', '&', '|', '^', '&&' and '||'. +/// +Parser::DeclGroupPtrTy +Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) { + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, + getOpenMPDirectiveName(OMPD_declare_reduction))) { + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + return DeclGroupPtrTy(); + } + + DeclarationName Name = parseOpenMPReductionId(*this); + if (Name.isEmpty() && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + + // Consume ':'. + bool IsCorrect = !ExpectAndConsume(tok::colon); + + if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + + if (Tok.is(tok::colon) || Tok.is(tok::annot_pragma_openmp_end)) { + Diag(Tok.getLocation(), diag::err_expected_type); + IsCorrect = false; + } + + if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + + SmallVector, 8> ReductionTypes; + SmallVector, 8> CombinersInitializers; + // Parse list of types until ':' token. + do { + ColonProtectionRAIIObject ColonRAII(*this); + SourceRange Range; + TypeResult TR = ParseTypeName(&Range, Declarator::PrototypeContext, AS); + if (TR.isUsable()) { + auto ReductionType = + Actions.ActOnOpenMPDeclareReductionType(Range.getBegin(), TR); + if (!ReductionType.isNull()) + ReductionTypes.push_back( + std::make_pair(ReductionType, Range.getBegin())); + } else { + SkipUntil(tok::comma, tok::colon, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } + + if (Tok.is(tok::colon) || Tok.is(tok::annot_pragma_openmp_end)) + break; + + // Consume ','. + if (ExpectAndConsume(tok::comma)) { + IsCorrect = false; + if (Tok.is(tok::annot_pragma_openmp_end)) { + Diag(Tok.getLocation(), diag::err_expected_type); + return DeclGroupPtrTy(); + } + } + } while (Tok.isNot(tok::annot_pragma_openmp_end)); + + if (ReductionTypes.empty()) { + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + return DeclGroupPtrTy(); + } + + if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end)) + return DeclGroupPtrTy(); + + // Consume ':'. + if (ExpectAndConsume(tok::colon)) + IsCorrect = false; + + if (Tok.is(tok::annot_pragma_openmp_end)) { + Diag(Tok.getLocation(), diag::err_expected_expression); + return DeclGroupPtrTy(); + } + + DeclGroupPtrTy DRD = Actions.ActOnOpenMPDeclareReductionDirectiveStart( + getCurScope(), Actions.getCurLexicalContext(), Name, ReductionTypes, AS); + + // Parse expression and then parse initializer if any for each + // correct type. + unsigned I = 0, E = ReductionTypes.size(); + for (auto *D : DRD.get()) { + TentativeParsingAction TPA(*this); + ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope | + Scope::OpenMPDirectiveScope); + // Parse expression. + Actions.ActOnOpenMPDeclareReductionCombinerStart(getCurScope(), D); + ExprResult CombinerResult = + Actions.ActOnFinishFullExpr(ParseAssignmentExpression().get(), + D->getLocation(), /*DiscardedValue=*/true); + Actions.ActOnOpenMPDeclareReductionCombinerEnd(D, CombinerResult.get()); + + if (CombinerResult.isInvalid() && Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + TPA.Commit(); + IsCorrect = false; + break; + } + IsCorrect = !T.consumeClose() && IsCorrect && !CombinerResult.isInvalid(); + ExprResult InitializerResult; + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + // Parse expression. + if (Tok.is(tok::identifier) && + Tok.getIdentifierInfo()->isStr("initializer")) { + ConsumeToken(); + } else { + Diag(Tok.getLocation(), diag::err_expected) << "'initializer'"; + TPA.Commit(); + IsCorrect = false; + break; + } + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + IsCorrect = + !T.expectAndConsume(diag::err_expected_lparen_after, "initializer") && + IsCorrect; + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope | + Scope::OpenMPDirectiveScope); + // Parse expression. + Actions.ActOnOpenMPDeclareReductionInitializerStart(getCurScope(), D); + InitializerResult = Actions.ActOnFinishFullExpr( + ParseAssignmentExpression().get(), D->getLocation(), + /*DiscardedValue=*/true); + Actions.ActOnOpenMPDeclareReductionInitializerEnd( + D, InitializerResult.get()); + if (InitializerResult.isInvalid() && Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + TPA.Commit(); + IsCorrect = false; + break; + } + IsCorrect = + !T.consumeClose() && IsCorrect && !InitializerResult.isInvalid(); + } + } + + CombinersInitializers.push_back( + std::make_pair(CombinerResult.get(), InitializerResult.get())); + ++I; + // Revert parsing if not the last type, otherwise accept it, we're done with + // parsing. + if (I != E) + TPA.Revert(); + else + TPA.Commit(); + } + return Actions.ActOnOpenMPDeclareReductionDirectiveEnd(getCurScope(), DRD); +} + /// \brief Parsing of declarative OpenMP directives. /// /// threadprivate-directive: /// annot_pragma_openmp 'threadprivate' simple-variable-list +/// annot_pragma_openmp_end +/// +/// declare-reduction-directive: +/// annot_pragma_openmp 'declare' 'reduction' [...] +/// annot_pragma_openmp_end /// -Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { +Parser::DeclGroupPtrTy +Parser::ParseOpenMPDeclarativeDirective(AccessSpecifier AS) { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -109,6 +323,22 @@ return Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers); } break; + case OMPD_declare_reduction: + ConsumeToken(); + if (auto Res = ParseOpenMPDeclareReductionDirective(AS)) { + // The last seen token is annot_pragma_openmp_end - need to check for + // extra tokens. + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_declare_reduction); + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + } + // Skip the last annot_pragma_openmp_end. + ConsumeToken(); + return Res; + } + break; case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); break; @@ -141,7 +371,9 @@ << getOpenMPDirectiveName(DKind); break; } - SkipUntil(tok::annot_pragma_openmp_end); + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + ConsumeAnyToken(); return DeclGroupPtrTy(); } @@ -151,6 +383,12 @@ /// annot_pragma_openmp 'threadprivate' simple-variable-list /// annot_pragma_openmp_end /// +/// declare-reduction-directive: +/// annot_pragma_openmp 'declare' 'reduction' '(' ':' +/// {',' } ':' ')' ['initializer' '(' +/// ('omp_priv' '=' |) ')'] +/// annot_pragma_openmp_end +/// /// executable-directive: /// annot_pragma_openmp 'parallel' | 'simd' | 'for' | 'sections' | /// 'section' | 'single' | 'master' | 'critical' [ '(' ')' ] | @@ -196,6 +434,22 @@ } SkipUntil(tok::annot_pragma_openmp_end); break; + case OMPD_declare_reduction: + ConsumeToken(); + if (auto Res = ParseOpenMPDeclareReductionDirective(/*AS=*/AS_none)) { + // The last seen token is annot_pragma_openmp_end - need to check for + // extra tokens. + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_declare_reduction); + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + } + ConsumeAnyToken(); + Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); + } else + SkipUntil(tok::annot_pragma_openmp_end); + break; case OMPD_flush: if (PP.LookAhead(0).is(tok::l_paren)) { FlushHasClause = true; Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -657,7 +657,7 @@ HandlePragmaOpenCLExtension(); return DeclGroupPtrTy(); case tok::annot_pragma_openmp: - return ParseOpenMPDeclarativeDirective(); + return ParseOpenMPDeclarativeDirective(/*AS=*/AS_none); case tok::annot_pragma_ms_pointers_to_members: HandlePragmaMSPointersToMembers(); return DeclGroupPtrTy(); Index: lib/Sema/ScopeInfo.cpp =================================================================== --- lib/Sema/ScopeInfo.cpp +++ lib/Sema/ScopeInfo.cpp @@ -28,6 +28,7 @@ HasBranchIntoScope = false; HasIndirectGoto = false; HasDroppedStmt = false; + HasOMPDeclareReductionCombiner = false; ObjCShouldCallSuper = false; ObjCIsDesignatedInit = false; ObjCWarnForNoDesignatedInitChain = false; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -5532,7 +5532,7 @@ static bool shouldConsiderLinkage(const VarDecl *VD) { const DeclContext *DC = VD->getDeclContext()->getRedeclContext(); - if (DC->isFunctionOrMethod()) + if (DC->isFunctionOrMethod() || isa(DC)) return VD->hasExternalStorage(); if (DC->isFileContext()) return true; @@ -5543,7 +5543,8 @@ static bool shouldConsiderLinkage(const FunctionDecl *FD) { const DeclContext *DC = FD->getDeclContext()->getRedeclContext(); - if (DC->isFileContext() || DC->isFunctionOrMethod()) + if (DC->isFileContext() || DC->isFunctionOrMethod() || + isa(DC)) return true; if (DC->isRecord()) return false; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -368,6 +368,19 @@ DeduceReturnType(FD, Loc)) return true; } + + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions + // Only the variables omp_in and omp_out are allowed in the combiner. + // Only the variables omp_priv and omp_orig are allowed in the + // initializer-clause. + auto *DRD = dyn_cast(CurContext); + if (LangOpts.OpenMP && DRD && !CurContext->containsDecl(D) && + isa(D)) { + Diag(Loc, diag::err_omp_wrong_var_in_declare_reduction) + << getCurFunction()->HasOMPDeclareReductionCombiner; + Diag(D->getLocation(), diag::note_entity_declared_at) << D; + return true; + } DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass, ObjCPropertyAccess); Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -280,6 +280,10 @@ IDNS = Decl::IDNS_ObjCProtocol; break; + case Sema::LookupOMPReductionName: + IDNS = Decl::IDNS_OMPReduction; + break; + case Sema::LookupAnyName: IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Using | Decl::IDNS_Namespace | Decl::IDNS_ObjCProtocol @@ -1917,6 +1921,10 @@ BaseCallback = &LookupAnyMember; break; + case LookupOMPReductionName: + BaseCallback = &CXXRecordDecl::FindOMPReductionMember; + break; + case LookupUsingDeclName: // This lookup is for redeclarations only. Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -21,6 +21,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtOpenMP.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeOrdering.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" @@ -1428,6 +1429,7 @@ case OMPD_cancellation_point: case OMPD_cancel: case OMPD_flush: + case OMPD_declare_reduction: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -2325,6 +2327,7 @@ AllowedNameModifiers.push_back(OMPD_target_data); break; case OMPD_threadprivate: + case OMPD_declare_reduction: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -7398,6 +7401,224 @@ DepLoc, ColonLoc, Vars); } +QualType Sema::ActOnOpenMPDeclareReductionType(SourceLocation TyLoc, + TypeResult ParsedType) { + assert(ParsedType.isUsable()); + + QualType ReductionType = GetTypeFromParser(ParsedType.get()); + if (ReductionType.isNull()) + return QualType(); + + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions, C\C++ + // A type name in a declare reduction directive cannot be a function type, an + // array type, a reference type, or a type qualified with const, volatile or + // restrict. + if (ReductionType.hasQualifiers()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 0; + return QualType(); + } + + if (ReductionType->isFunctionType()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 1; + return QualType(); + } + if (ReductionType->isReferenceType()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 2; + return QualType(); + } + if (ReductionType->isArrayType()) { + Diag(TyLoc, diag::err_omp_reduction_wrong_type) << 3; + return QualType(); + } + return ReductionType; +} + +Sema::DeclGroupPtrTy Sema::ActOnOpenMPDeclareReductionDirectiveStart( + Scope *S, DeclContext *DC, DeclarationName Name, + ArrayRef> ReductionTypes, + AccessSpecifier AS, Decl *PrevDeclInScope) { + SmallVector Decls; + Decls.reserve(ReductionTypes.size()); + + LookupResult Lookup(*this, Name, SourceLocation(), LookupOMPReductionName, + ForRedeclaration); + // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions + // A reduction-identifier may not be re-declared in the current scope for the + // same type or for a type that is compatible according to the base language + // rules. + llvm::DenseMap PreviousRedeclTypes; + OMPDeclareReductionDecl *PrevDRD = nullptr; + bool InCompoundScope = true; + if (S) { + // Find previous declaration with the same name not referenced in other + // declarations. + FunctionScopeInfo *ParentFn = getEnclosingFunction(); + InCompoundScope = ParentFn && !ParentFn->CompoundScopes.empty(); + LookupName(Lookup, S); + FilterLookupForScope(Lookup, DC, S, /*ConsiderLinkage=*/false, + /*AllowInlineNamespace=*/false); + llvm::DenseMap UsedAsPrevious; + auto Filter = Lookup.makeFilter(); + while (Filter.hasNext()) { + auto *PrevDecl = cast(Filter.next()); + if (InCompoundScope) { + auto I = UsedAsPrevious.find(PrevDecl); + if (I == UsedAsPrevious.end()) + UsedAsPrevious[PrevDecl] = false; + if (auto *D = PrevDecl->getPrevDeclInScope()) + UsedAsPrevious[D] = true; + } + PreviousRedeclTypes[PrevDecl->getType().getCanonicalType()] = + PrevDecl->getLocation(); + } + Filter.done(); + if (InCompoundScope) { + for (auto &PrevData : UsedAsPrevious) { + if (!PrevData.second) { + PrevDRD = PrevData.first; + break; + } + } + } + } else if (PrevDeclInScope) { + auto *PrevDRDInScope = PrevDRD = + cast(PrevDeclInScope); + do { + PreviousRedeclTypes[PrevDRDInScope->getType().getCanonicalType()] = + PrevDRDInScope->getLocation(); + PrevDRDInScope = PrevDRDInScope->getPrevDeclInScope(); + } while (PrevDRDInScope); + } + for (auto &TyData : ReductionTypes) { + auto I = PreviousRedeclTypes.find(TyData.first.getCanonicalType()); + bool Invalid = false; + if (I != PreviousRedeclTypes.end()) { + Diag(TyData.second, diag::err_omp_declare_reduction_redefinition) + << TyData.first; + Diag(I->second, diag::note_previous_definition); + Invalid = true; + } + PreviousRedeclTypes[TyData.first.getCanonicalType()] = TyData.second; + auto *DRD = OMPDeclareReductionDecl::Create(Context, DC, TyData.second, + Name, TyData.first, PrevDRD); + DC->addDecl(DRD); + DRD->setAccess(AS); + Decls.push_back(DRD); + if (Invalid) + DRD->setInvalidDecl(); + else + PrevDRD = DRD; + } + + return DeclGroupPtrTy::make( + DeclGroupRef::Create(Context, Decls.begin(), Decls.size())); +} + +void Sema::ActOnOpenMPDeclareReductionCombinerStart(Scope *S, Decl *D) { + auto *DRD = cast(D); + + // Enter new function scope. + PushFunctionScope(); + getCurFunction()->setHasBranchProtectedScope(); + getCurFunction()->setHasOMPDeclareReductionCombiner(); + + if (S) + PushDeclContext(S, DRD); + else + CurContext = DRD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); + + QualType ReductionType = DRD->getType(); + // Create 'T omp_in;' implicit param. + auto *OmpInParm = + ImplicitParamDecl::Create(Context, DRD, D->getLocation(), + &Context.Idents.get("omp_in"), ReductionType); + // Create 'T &omp_out;' implicit param. + auto *OmpOutParm = ImplicitParamDecl::Create( + Context, DRD, D->getLocation(), &Context.Idents.get("omp_out"), + Context.getLValueReferenceType(ReductionType)); + if (S) { + PushOnScopeChains(OmpInParm, S); + PushOnScopeChains(OmpOutParm, S); + } else { + DRD->addDecl(OmpInParm); + DRD->addDecl(OmpOutParm); + } +} + +void Sema::ActOnOpenMPDeclareReductionCombinerEnd(Decl *D, Expr *Combiner) { + auto *DRD = cast(D); + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + if (Combiner) + DRD->setCombiner(Combiner); + else + DRD->setInvalidDecl(); +} + +void Sema::ActOnOpenMPDeclareReductionInitializerStart(Scope *S, Decl *D) { + auto *DRD = cast(D); + + // Enter new function scope. + PushFunctionScope(); + getCurFunction()->setHasBranchProtectedScope(); + + if (S) + PushDeclContext(S, DRD); + else + CurContext = DRD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); + + QualType ReductionType = DRD->getType(); + // Create 'T omp_orig;' implicit param. + auto *OmpOrigParm = + ImplicitParamDecl::Create(Context, DRD, D->getLocation(), + &Context.Idents.get("omp_orig"), ReductionType); + // Create 'T &omp_priv;' implicit param. + auto *OmpPrivParm = ImplicitParamDecl::Create( + Context, DRD, D->getLocation(), &Context.Idents.get("omp_priv"), + Context.getLValueReferenceType(ReductionType)); + if (S) { + PushOnScopeChains(OmpPrivParm, S); + PushOnScopeChains(OmpOrigParm, S); + } else { + DRD->addDecl(OmpPrivParm); + DRD->addDecl(OmpOrigParm); + } +} + +void Sema::ActOnOpenMPDeclareReductionInitializerEnd(Decl *D, + Expr *Initializer) { + auto *DRD = cast(D); + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + if (Initializer) + DRD->setInitializer(Initializer); + else + DRD->setInvalidDecl(); +} + +Sema::DeclGroupPtrTy +Sema::ActOnOpenMPDeclareReductionDirectiveEnd(Scope *S, + DeclGroupPtrTy DeclReductions) { + for (auto *D : DeclReductions.get()) { + auto *DRD = cast(D); + if (S) + PushOnScopeChains(DRD, S, /*AddToContext=*/false); + } + return DeclReductions; +} + OMPClause *Sema::ActOnOpenMPDeviceClause(Expr *Device, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc) { Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2472,6 +2472,82 @@ return TD; } +Decl *TemplateDeclInstantiator::VisitOMPDeclareReductionDecl( + OMPDeclareReductionDecl *D) { + // Instantiate type and check if it is allowed. + QualType SubstReductionType = SemaRef.ActOnOpenMPDeclareReductionType( + D->getLocation(), + ParsedType::make(SemaRef.SubstType(D->getType(), TemplateArgs, + D->getLocation(), DeclarationName()))); + if (SubstReductionType.isNull()) + return nullptr; + bool IsCorrect = !SubstReductionType.isNull(); + // Create instantiated copy. + std::pair ReductionTypes[] = { + std::make_pair(SubstReductionType, D->getLocation())}; + auto *PrevDeclInScope = D->getPrevDeclInScope(); + if (PrevDeclInScope && !PrevDeclInScope->isInvalidDecl()) { + PrevDeclInScope = cast( + SemaRef.CurrentInstantiationScope->findInstantiationOf(PrevDeclInScope) + ->get()); + } + auto DRD = SemaRef.ActOnOpenMPDeclareReductionDirectiveStart( + /*S=*/nullptr, Owner, D->getDeclName(), ReductionTypes, D->getAccess(), + PrevDeclInScope); + auto *NewDRD = cast(DRD.get().getSingleDecl()); + if (isDeclWithinFunction(NewDRD)) + SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewDRD); + Expr *SubstCombiner = nullptr; + Expr *SubstInitializer = nullptr; + // Combiners instantiation sequence. + if (D->getCombiner()) { + SemaRef.ActOnOpenMPDeclareReductionCombinerStart( + /*S=*/nullptr, NewDRD); + const char *Names[] = {"omp_in", "omp_out"}; + for (unsigned I = 0; I < llvm::array_lengthof(Names); ++I) { + DeclarationName DN(&SemaRef.Context.Idents.get(Names[I])); + auto OldLookup = D->lookup(DN); + auto Lookup = NewDRD->lookup(DN); + if (!OldLookup.empty() && !Lookup.empty()) { + assert(Lookup.size() == 1 && OldLookup.size() == 1); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldLookup.front(), + Lookup.front()); + } + } + SubstCombiner = SemaRef.SubstExpr(D->getCombiner(), TemplateArgs).get(); + SemaRef.ActOnOpenMPDeclareReductionCombinerEnd(NewDRD, SubstCombiner); + // Initializers instantiation sequence. + if (D->getInitializer()) { + SemaRef.ActOnOpenMPDeclareReductionInitializerStart( + /*S=*/nullptr, NewDRD); + const char *Names[] = {"omp_orig", "omp_priv"}; + for (unsigned I = 0; I < llvm::array_lengthof(Names); ++I) { + DeclarationName DN(&SemaRef.Context.Idents.get(Names[I])); + auto OldLookup = D->lookup(DN); + auto Lookup = NewDRD->lookup(DN); + if (!OldLookup.empty() && !Lookup.empty()) { + assert(Lookup.size() == 1 && OldLookup.size() == 1); + SemaRef.CurrentInstantiationScope->InstantiatedLocal( + OldLookup.front(), Lookup.front()); + } + } + SubstInitializer = + SemaRef.SubstExpr(D->getInitializer(), TemplateArgs).get(); + IsCorrect = IsCorrect && SubstCombiner && + (!D->getInitializer() || SubstInitializer); + SemaRef.ActOnOpenMPDeclareReductionInitializerEnd(NewDRD, + SubstInitializer); + } + } else + IsCorrect = false; + + if (!IsCorrect) + NewDRD->setInvalidDecl(); + (void)SemaRef.ActOnOpenMPDeclareReductionDirectiveEnd(/*S=*/nullptr, DRD); + + return NewDRD; +} + Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) { return VisitFunctionDecl(D, nullptr); } Index: lib/Serialization/ASTCommon.cpp =================================================================== --- lib/Serialization/ASTCommon.cpp +++ lib/Serialization/ASTCommon.cpp @@ -329,6 +329,7 @@ case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPDeclareReduction: return false; // These indirectly derive from Redeclarable but are not actually Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -349,6 +349,7 @@ void VisitObjCPropertyDecl(ObjCPropertyDecl *D); void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); + void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D); /// We've merged the definition \p MergedDef into the existing definition /// \p Def. Ensure that \p Def is made visible whenever \p MergedDef is made @@ -2349,6 +2350,15 @@ D->setVars(Vars); } +void ASTDeclReader::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + VisitNamedDecl(D); + D->setLocation(Reader.ReadSourceLocation(F, Record, Idx)); + D->setCombiner(Reader.ReadExpr(F)); + D->setInitializer(Reader.ReadExpr(F)); + D->PrevDeclInScope = Reader.ReadDeclID(F, Record, Idx); + D->setType(Reader.readType(F, Record, Idx)); +} + //===----------------------------------------------------------------------===// // Attribute Reading //===----------------------------------------------------------------------===// @@ -2398,7 +2408,8 @@ isa(D) || isa(D) || isa(D) || - isa(D)) + isa(D) || + isa(D)) return true; if (VarDecl *Var = dyn_cast(D)) return Var->isFileVarDecl() && @@ -3294,6 +3305,9 @@ case DECL_OMP_THREADPRIVATE: D = OMPThreadPrivateDecl::CreateDeserialized(Context, ID, Record[Idx++]); break; + case DECL_OMP_DECLARE_REDUCTION: + D = OMPDeclareReductionDecl::CreateDeserialized(Context, ID); + break; case DECL_EMPTY: D = EmptyDecl::CreateDeserialized(Context, ID); break; Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -131,6 +131,7 @@ void VisitObjCPropertyDecl(ObjCPropertyDecl *D); void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); + void VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D); /// Add an Objective-C type parameter list to the given record. void AddObjCTypeParamList(ObjCTypeParamList *typeParams) { @@ -1617,6 +1618,16 @@ Code = serialization::DECL_OMP_THREADPRIVATE; } +void ASTDeclWriter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { + VisitNamedDecl(D); + Writer.AddSourceLocation(D->getLocStart(), Record); + Writer.AddStmt(D->getCombiner()); + Writer.AddStmt(D->getInitializer()); + Writer.AddDeclRef(D->getPrevDeclInScope(), Record); + Writer.AddTypeRef(D->getType(), Record); + Code = serialization::DECL_OMP_DECLARE_REDUCTION; +} + //===----------------------------------------------------------------------===// // ASTWriter Implementation //===----------------------------------------------------------------------===// Index: test/OpenMP/declare_reduction_ast_print.c =================================================================== --- test/OpenMP/declare_reduction_ast_print.c +++ test/OpenMP/declare_reduction_ast_print.c @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) +// CHECK: #pragma omp declare reduction (+ : int : omp_out *= omp_in) +// CHECK-NEXT: #pragma omp declare reduction (+ : char : omp_out *= omp_in) + +#pragma omp declare reduction(fun : float : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) +// CHECK: #pragma omp declare reduction (fun : float : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + +// CHECK: struct SSS { +struct SSS { + int field; +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) + // CHECK: #pragma omp declare reduction (+ : int : omp_out *= omp_in) + // CHECK-NEXT: #pragma omp declare reduction (+ : char : omp_out *= omp_in) +}; +// CHECK: }; + +void init(struct SSS *priv, struct SSS orig); + +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) +// CHECK: #pragma omp declare reduction (fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + +// CHECK: int main() { +int main() { +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + // CHECK: #pragma omp declare reduction (fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + { +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + // CHECK: #pragma omp declare reduction (fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + } + return 0; +} +// CHECK: } + +#endif Index: test/OpenMP/declare_reduction_ast_print.cpp =================================================================== --- test/OpenMP/declare_reduction_ast_print.cpp +++ test/OpenMP/declare_reduction_ast_print.cpp @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) +// CHECK: #pragma omp declare reduction (+ : int : omp_out *= omp_in) +// CHECK-NEXT: #pragma omp declare reduction (+ : char : omp_out *= omp_in) + +// CHECK: #pragma omp declare reduction (fun : int : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + +template +class SSS { +public: +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + // CHECK: #pragma omp declare reduction (fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) +}; + +SSS d; + +void init(SSS &lhs, SSS rhs); + +#pragma omp declare reduction(fun : SSS < int > : omp_out = omp_in) initializer(init(omp_priv, omp_orig)) +// CHECK: #pragma omp declare reduction (fun : SSS : omp_out = omp_in) initializer(init(omp_priv, omp_orig)) + +// CHECK: template int foo(int a) { +// CHECK: #pragma omp declare reduction (fun : int : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: { +// CHECK: #pragma omp declare reduction (fun : int : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: } +// CHECK: return a; +// CHECK: } + +// CHECK: template T foo(T a) { +// CHECK: #pragma omp declare reduction (fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: { +// CHECK: #pragma omp declare reduction (fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15); +// CHECK: } +// CHECK: return a; +// CHECK: } +template +T foo(T a) { +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + { +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = omp_orig + 15) + } + return a; +} + +int main() { + int i = 0; + SSS sss; + // TODO: Add support for scoped reduction identifiers + // #pragma omp parallel reduction(SSS::fun : i) + // TODO-CHECK: #pragma omp parallel reduction(SSS::fun: i) + { + i += 1; + } + // #pragma omp parallel reduction(::fun:sss) + // TODO-CHECK: #pragma omp parallel reduction(::fun: sss) + { + } + return foo(15); +} + +#endif Index: test/OpenMP/declare_reduction_messages.c =================================================================== --- test/OpenMP/declare_reduction_messages.c +++ test/OpenMP/declare_reduction_messages.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s + +int temp; // expected-note 6 {{'temp' declared here}} + +#pragma omp declare reduction // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction { // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction( // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(# // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(/ // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(+ // expected-error {{expected ':'}} +#pragma omp declare reduction(for // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(if: // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} expected-error {{expected a type}} +#pragma omp declare reduction(oper: // expected-error {{expected a type}} +#pragma omp declare reduction(oper; // expected-error {{expected ':'}} expected-error {{expected a type}} +#pragma omp declare reduction(fun : int // expected-error {{expected ':'}} expected-error {{expected expression}} +#pragma omp declare reduction(+ : const int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(- : volatile int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(* : int; // expected-error {{expected ','}} expected-error {{expected a type}} +#pragma omp declare reduction(& : double char: // expected-error {{cannot combine with previous 'double' declaration specifier}} expected-error {{expected expression}} +#pragma omp declare reduction(^ : double, char, : // expected-error {{expected a type}} expected-error {{expected expression}} +#pragma omp declare reduction(&& : int, S: // expected-error {{unknown type name 'S'}} expected-error {{expected expression}} +#pragma omp declare reduction(|| : int, double : temp += omp_in) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(| : char, float : omp_out += temp) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long : omp_out += omp_in) { // expected-error {{expected 'initializer'}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun : unsigned : omp_out += temp)) // expected-error {{expected 'initializer'}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long(void) : omp_out += omp_in) // expected-error {{reduction type cannot be a function type}} +#pragma omp declare reduction(fun : long[3] : omp_out += omp_in) // expected-error {{reduction type cannot be an array type}} +#pragma omp declare reduction(fun23 : long, int, long : omp_out += omp_in) // expected-error {{redefinition of user-defined reduction for type 'long'}} expected-note {{previous definition is here}} + +#pragma omp declare reduction(fun222 : long : omp_out += omp_in) +#pragma omp declare reduction(fun1 : long : omp_out += omp_in) initializer // expected-error {{expected '(' after 'initializer'}} +#pragma omp declare reduction(fun2 : long : omp_out += omp_in) initializer { // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun3 : long : omp_out += omp_in) initializer[ // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun4 : long : omp_out += omp_in) initializer() // expected-error {{expected expression}} +#pragma omp declare reduction(fun5 : long : omp_out += omp_in) initializer(temp) // expected-error {{only 'omp_priv' or 'omp_orig' variables are allowed in initializer expression}} +#pragma omp declare reduction(fun6 : long : omp_out += omp_in) initializer(omp_orig // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun7 : long : omp_out += omp_in) initializer(omp_priv 12) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun8 : long : omp_out += omp_in) initializer(omp_priv = 23) // expected-note {{previous definition is here}} +#pragma omp declare reduction(fun8 : long : omp_out += omp_in) initializer(omp_priv = 23)) // expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{redefinition of user-defined reduction for type 'long'}} +#pragma omp declare reduction(fun9 : long : omp_out += omp_in) initializer(omp_priv = ) // expected-error {{expected expression}} + +int fun(int arg) { +#pragma omp declare reduction(red : int : omp_out++) + { +#pragma omp declare reduction(red : int : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red : int : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'int'}} + { +#pragma omp declare reduction(red : int : omp_out++) + } + } + return arg; +} Index: test/OpenMP/declare_reduction_messages.cpp =================================================================== --- test/OpenMP/declare_reduction_messages.cpp +++ test/OpenMP/declare_reduction_messages.cpp @@ -0,0 +1,106 @@ +// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s + +int temp; // expected-note 7 {{'temp' declared here}} + +#pragma omp declare reduction // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction { // expected-error {{expected '(' after 'declare reduction'}} +#pragma omp declare reduction( // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(# // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(/ // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(+ // expected-error {{expected ':'}} +#pragma omp declare reduction(operator // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} +#pragma omp declare reduction(operator: // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'}} expected-error {{expected a type}} +#pragma omp declare reduction(oper: // expected-error {{expected a type}} +#pragma omp declare reduction(oper; // expected-error {{expected ':'}} expected-error {{expected a type}} +#pragma omp declare reduction(fun : int // expected-error {{expected ':'}} expected-error {{expected expression}} +#pragma omp declare reduction(+ : const int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(- : volatile int: // expected-error {{reduction type cannot be qualified with 'const', 'volatile' or 'restrict'}} +#pragma omp declare reduction(* : int; // expected-error {{expected ','}} expected-error {{expected a type}} +#pragma omp declare reduction(& : double char: // expected-error {{cannot combine with previous 'double' declaration specifier}} expected-error {{expected expression}} +#pragma omp declare reduction(^ : double, char, : // expected-error {{expected a type}} expected-error {{expected expression}} +#pragma omp declare reduction(&& : int, S: // expected-error {{unknown type name 'S'}} expected-error {{expected expression}} +#pragma omp declare reduction(|| : int, double : temp += omp_in) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(| : char, float : omp_out += ::temp) // expected-error 2 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long : omp_out += omp_in) { // expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{expected 'initializer'}} +#pragma omp declare reduction(fun : unsigned : omp_out += ::temp)) // expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} expected-error {{expected 'initializer'}} expected-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun : long & : omp_out += omp_in) // expected-error {{reduction type cannot be a reference type}} +#pragma omp declare reduction(fun : long(void) : omp_out += omp_in) // expected-error {{reduction type cannot be a function type}} +#pragma omp declare reduction(fun : long[3] : omp_out += omp_in) // expected-error {{reduction type cannot be an array type}} +#pragma omp declare reduction(fun23 : long, int, long : omp_out += omp_in) // expected-error {{redefinition of user-defined reduction for type 'long'}} expected-note {{previous definition is here}} + +template +class Class1 { + T a; +public: + Class1() : a() {} +#pragma omp declare reduction(fun : T : temp) // expected-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} +#pragma omp declare reduction(fun1 : T : omp_out++) // expected-note {{previous definition is here}} expected-error {{reduction type cannot be a reference type}} +#pragma omp declare reduction(fun1 : T : omp_out += omp_in) // expected-error {{redefinition of user-defined reduction for type 'T'}} +#pragma omp declare reduction(fun2 : T, T : omp_out++) // expected-error {{reduction type cannot be a reference type}} expected-error {{redefinition of user-defined reduction for type 'T'}} expected-note {{previous definition is here}} +#pragma omp declare reduction(foo : T : omp_out += this->a) // expected-error {{invalid use of 'this' outside of a non-static member function}} +}; + +Class1 e; // expected-note {{in instantiation of template class 'Class1' requested here}} + +template +class Class2 : public Class1 { +#pragma omp declare reduction(fun : T : omp_out += omp_in) +}; + +#pragma omp declare reduction(fun222 : long : omp_out += omp_in) // expected-note {{previous definition is here}} +#pragma omp declare reduction(fun222 : long : omp_out += omp_in) // expected-error {{redefinition of user-defined reduction for type 'long'}} +#pragma omp declare reduction(fun1 : long : omp_out += omp_in) initializer // expected-error {{expected '(' after 'initializer'}} +#pragma omp declare reduction(fun2 : long : omp_out += omp_in) initializer { // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun3 : long : omp_out += omp_in) initializer[ // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun4 : long : omp_out += omp_in) initializer() // expected-error {{expected expression}} +#pragma omp declare reduction(fun5 : long : omp_out += omp_in) initializer(temp) // expected-error {{only 'omp_priv' or 'omp_orig' variables are allowed in initializer expression}} +#pragma omp declare reduction(fun6 : long : omp_out += omp_in) initializer(omp_orig // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun7 : long : omp_out += omp_in) initializer(omp_priv Class1 < int > ()) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun77 : long : omp_out += omp_in) initializer(omp_priv Class2 < int > ()) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun8 : long : omp_out += omp_in) initializer(omp_priv 23) // expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp declare reduction(fun88 : long : omp_out += omp_in) initializer(omp_priv 23)) // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} +#pragma omp declare reduction(fun9 : long : omp_out += omp_priv) initializer(omp_in = 23) // expected-error {{use of undeclared identifier 'omp_priv'; did you mean 'omp_in'?}} expected-note {{'omp_in' is an implicit parameter}} +#pragma omp declare reduction(fun10 : long : omp_out += omp_in) initializer(omp_priv = 23) + +template +T fun(T arg) { +#pragma omp declare reduction(red : T : omp_out++) + { +#pragma omp declare reduction(red : T : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red : T : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'T'}} +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = 23) + } + return arg; +} + +template +T foo(T arg) { + { +#pragma omp declare reduction(red : T : omp_out++) +#pragma omp declare reduction(red1 : T : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red1 : int : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'int'}} + } + { +#pragma omp declare reduction(red1 : int : omp_out++) // expected-note {{previous definition is here}} +#pragma omp declare reduction(red : T : omp_out++) +#pragma omp declare reduction(red1 : T : omp_out++) // expected-error {{redefinition of user-defined reduction for type 'int'}} + } + return arg; +} + +#pragma omp declare reduction(foo : int : ({int a = omp_in; a = a * 2; omp_out += a; })) +int main() { + Class1 c1; + int i; + // TODO: Add support for scoped reduction identifiers + // #pragma omp parallel reduction (::fun : c1) + { + } + // #pragma omp parallel reduction (::Class1::fun : c1) + { + } + // #pragma omp parallel reduction (::Class2::fun : i) + { + } + return fun(15) + foo(15); // expected-note {{in instantiation of function template specialization 'foo' requested here}} +} Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -5109,6 +5109,7 @@ case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPDeclareReduction: case Decl::ObjCTypeParam: return C;