ABataev created this revision.
ABataev added a reviewer: rsmith.
ABataev added subscribers: cfe-commits, hfinkel, fraggamuffin, ejstotzer.

Add parsing, sema analysis and serialization/deserialization for 'declare 
reduction' construct.
User-defined reductions are defined as
```
#pragma omp declare reduction( reduction-identifier : typename-list : combiner 
) [initializer ( initializer-expr )]
```
These custom reductions may be used in 'reduction' clauses of OpenMP 
constructs. The combiner specifies how partial results can be combined into a 
single value. The
combiner can use the special variable identifiers omp_in and omp_out that are 
of the type of the variables being reduced with this reduction-identifier. Each 
of them will
denote one of the values to be combined before executing the combiner. It is 
assumed that the special omp_out identifier will refer to the storage that 
holds the resulting
combined value after executing the combiner.
As the initializer-expr value of a user-defined reduction is not known a priori 
the initializer-clause can be used to specify one. Then the contents of the 
initializer-clause
will be used as the initializer for private copies of reduction list items 
where the omp_priv identifier will refer to the storage to be initialized. The 
special identifier
omp_orig can also appear in the initializer-clause and it will refer to the 
storage of the original variable to be reduced.

http://reviews.llvm.org/D11182

Files:
  include/clang/AST/DataRecursiveASTVisitor.h
  include/clang/AST/DeclBase.h
  include/clang/AST/DeclOpenMP.h
  include/clang/AST/RecursiveASTVisitor.h
  include/clang/Basic/DeclNodes.td
  include/clang/Basic/DiagnosticParseKinds.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/OpenMPKinds.def
  include/clang/Parse/Parser.h
  include/clang/Sema/Sema.h
  include/clang/Serialization/ASTBitCodes.h
  lib/AST/ASTContext.cpp
  lib/AST/Decl.cpp
  lib/AST/DeclBase.cpp
  lib/AST/DeclOpenMP.cpp
  lib/AST/DeclPrinter.cpp
  lib/AST/ItaniumMangle.cpp
  lib/AST/MicrosoftMangle.cpp
  lib/Basic/OpenMPKinds.cpp
  lib/CodeGen/CGDecl.cpp
  lib/CodeGen/CodeGenModule.cpp
  lib/CodeGen/CodeGenModule.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Parse/ParseOpenMP.cpp
  lib/Parse/Parser.cpp
  lib/Sema/SemaLookup.cpp
  lib/Sema/SemaOpenMP.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  lib/Serialization/ASTCommon.cpp
  lib/Serialization/ASTReaderDecl.cpp
  lib/Serialization/ASTWriterDecl.cpp
  test/OpenMP/declare_reduction_ast_print.c
  test/OpenMP/declare_reduction_ast_print.cpp
  test/OpenMP/declare_reduction_messages.c
  test/OpenMP/declare_reduction_messages.cpp
  tools/libclang/CIndex.cpp

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) {
@@ -1544,6 +1545,19 @@
   Code = serialization::DECL_OMP_THREADPRIVATE;
 }
 
+void ASTDeclWriter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) {
+  Record.push_back(D->reductions_size());
+  VisitDecl(D);
+  Writer.AddDeclarationName(D->getDeclName(), Record);
+  for (auto &&Data : D->reductions()) {
+    Writer.AddTypeRef(Data.ReductionType, Record);
+    Writer.AddSourceRange(Data.TyRange, Record);
+    Writer.AddStmt(Data.Combiner);
+    Writer.AddStmt(Data.Initializer);
+  }
+  Code = serialization::DECL_OMP_DECLARE_REDUCTION;
+}
+
 //===----------------------------------------------------------------------===//
 // ASTWriter Implementation
 //===----------------------------------------------------------------------===//
Index: lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- lib/Serialization/ASTReaderDecl.cpp
+++ lib/Serialization/ASTReaderDecl.cpp
@@ -373,6 +373,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
@@ -2364,6 +2365,24 @@
   D->setVars(Vars);
 }
 
+void ASTDeclReader::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) {
+  VisitDecl(D);
+  D->setDeclName(Reader.ReadDeclarationName(F, Record, Idx));
+  unsigned NumReductions = D->reductions_size();
+  SmallVector<std::pair<QualType, SourceRange>, 16> ReductionTypes;
+  SmallVector<std::pair<Expr *, Expr *>, 16> CombinersInitializers;
+  ReductionTypes.reserve(NumReductions);
+  CombinersInitializers.reserve(NumReductions);
+  for (unsigned i = 0; i != NumReductions; ++i) {
+    ReductionTypes.push_back(
+        std::make_pair(Reader.readType(F, Record, Idx),
+                       Reader.ReadSourceRange(F, Record, Idx)));
+    CombinersInitializers.push_back(
+        std::make_pair(Reader.ReadExpr(F), Reader.ReadExpr(F)));
+  }
+  D->addReductions(ReductionTypes, CombinersInitializers);
+}
+
 //===----------------------------------------------------------------------===//
 // Attribute Reading
 //===----------------------------------------------------------------------===//
@@ -2413,7 +2432,8 @@
       isa<ObjCProtocolDecl>(D) || 
       isa<ObjCImplDecl>(D) ||
       isa<ImportDecl>(D) ||
-      isa<OMPThreadPrivateDecl>(D))
+      isa<OMPThreadPrivateDecl>(D) ||
+      isa<OMPDeclareReductionDecl>(D))
     return true;
   if (VarDecl *Var = dyn_cast<VarDecl>(D))
     return Var->isFileVarDecl() &&
@@ -3300,6 +3320,9 @@
   case DECL_OMP_THREADPRIVATE:
     D = OMPThreadPrivateDecl::CreateDeserialized(Context, ID, Record[Idx++]);
     break;
+  case DECL_OMP_DECLARE_REDUCTION:
+    D = OMPDeclareReductionDecl::CreateDeserialized(Context, ID, Record[Idx++]);
+    break;
   case DECL_EMPTY:
     D = EmptyDecl::CreateDeserialized(Context, ID);
     break;
Index: lib/Serialization/ASTCommon.cpp
===================================================================
--- lib/Serialization/ASTCommon.cpp
+++ lib/Serialization/ASTCommon.cpp
@@ -215,6 +215,7 @@
   case Decl::ClassScopeFunctionSpecialization:
   case Decl::Import:
   case Decl::OMPThreadPrivate:
+  case Decl::OMPDeclareReduction:
     return false;
 
   // These indirectly derive from Redeclarable<T> but are not actually
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -653,7 +653,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/Parse/ParseOpenMP.cpp
===================================================================
--- lib/Parse/ParseOpenMP.cpp
+++ lib/Parse/ParseOpenMP.cpp
@@ -32,6 +32,8 @@
   const OpenMPDirectiveKind F[][3] = {
       {OMPD_unknown /*cancellation*/, OMPD_unknown /*point*/,
        OMPD_cancellation_point},
+      {OMPD_unknown /*declare*/, OMPD_unknown /*reduction*/,
+       OMPD_declare_reduction},
       {OMPD_for, OMPD_simd, OMPD_for_simd},
       {OMPD_parallel, OMPD_for, OMPD_parallel_for},
       {OMPD_parallel_for, OMPD_simd, OMPD_parallel_for_simd},
@@ -43,25 +45,28 @@
           : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
   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);
       auto SDKind =
           Tok.isAnnotation()
               ? OMPD_unknown
               : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
-      if (!Tok.isAnnotation() && DKind == OMPD_unknown) {
+      if (!Tok.isAnnotation() && SDKind == OMPD_unknown)
         TokenMatched =
-            (i == 0) && !P.getPreprocessor().getSpelling(Tok).compare("point");
-      } else {
+            ((i == 0) &&
+             !P.getPreprocessor().getSpelling(Tok).compare("point")) ||
+            ((i == 1) &&
+             !P.getPreprocessor().getSpelling(Tok).compare("reduction"));
+      else
         TokenMatched = SDKind == F[i][1] && SDKind != OMPD_unknown;
-      }
       if (TokenMatched) {
         P.ConsumeToken();
         DKind = F[i][2];
@@ -71,12 +76,260 @@
   return DKind;
 }
 
+/// \brief Parse 'omp declare reduction' construct.
+///
+///       declare-reduction-directive:
+///         annot_pragma_openmp 'declare' 'reduction' '(' <reduction_id> ':'
+///         <type> {',' <type>} ':' <expression> ')' ['initializer' '('
+///         ('omp_priv' '=' <expression>|<function_call>) ')']
+///         annot_pragma_openmp_end
+///
+Parser::DeclGroupPtrTy
+Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) {
+  SourceLocation Loc = Tok.getLocation();
+  bool IsCorrect = true;
+  // 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;
+  switch (Tok.getKind()) {
+  case tok::plus: // '+'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("+"));
+    ConsumeAnyToken();
+    break;
+  case tok::minus: // '-'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("-"));
+    ConsumeAnyToken();
+    break;
+  case tok::star: // '*'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("*"));
+    ConsumeAnyToken();
+    break;
+  case tok::amp: // '&'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("&"));
+    ConsumeAnyToken();
+    break;
+  case tok::pipe: // '|'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("|"));
+    ConsumeAnyToken();
+    break;
+  case tok::caret: // '^'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("^"));
+    ConsumeAnyToken();
+    break;
+  case tok::ampamp: // '&&'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("&&"));
+    ConsumeAnyToken();
+    break;
+  case tok::pipepipe: // '||'
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        &Actions.Context.Idents.get("||"));
+    ConsumeAnyToken();
+    break;
+  case tok::identifier: // identifier
+    Name = Actions.getASTContext().DeclarationNames.getIdentifier(
+        Tok.getIdentifierInfo());
+    ConsumeAnyToken();
+    break;
+  default:
+    IsCorrect = false;
+    Diag(Tok.getLocation(), diag::err_omp_expected_reduction_identifier);
+    SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
+              StopBeforeMatch);
+    break;
+  }
+  if (!IsCorrect && Tok.is(tok::annot_pragma_openmp_end))
+    return DeclGroupPtrTy();
+
+  // Consume ':'.
+  if (Tok.is(tok::colon)) {
+    ConsumeAnyToken();
+  } else {
+    Diag(Tok.getLocation(), diag::err_expected) << "':'";
+    IsCorrect = false;
+  }
+
+  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<std::pair<QualType, SourceRange>, 8> ReductionTypes;
+  SmallVector<std::pair<Expr *, Expr *>, 8> CombinersInitializers;
+  bool IsCommaFound = false;
+  bool FunctionsCorrect = true;
+  // Parse list of types until ':' token.
+  while (Tok.isNot(tok::colon) && Tok.isNot(tok::annot_pragma_openmp_end)) {
+    ColonProtectionRAIIObject ColonRAII(*this);
+    IsCommaFound = false;
+    SourceRange Range;
+    TypeResult TR = ParseTypeName(&Range, Declarator::PrototypeContext, AS);
+    if (TR.isUsable()) {
+      QualType ReductionType = Sema::GetTypeFromParser(TR.get());
+      if (!ReductionType.isNull() &&
+          Actions.isOpenMPDeclareReductionTypeAllowed(Range, ReductionType,
+                                                      ReductionTypes))
+        ReductionTypes.push_back(std::make_pair(ReductionType, Range));
+      else
+        FunctionsCorrect = false;
+    } else {
+      SkipUntil(tok::comma, tok::colon, tok::annot_pragma_openmp_end,
+                StopBeforeMatch);
+      FunctionsCorrect = false;
+    }
+
+    // Consume ','.
+    if (Tok.is(tok::comma)) {
+      ConsumeAnyToken();
+      IsCommaFound = true;
+    } else if (Tok.isNot(tok::colon) &&
+               Tok.isNot(tok::annot_pragma_openmp_end)) {
+      Diag(Tok.getLocation(), diag::err_expected) << "','";
+      IsCorrect = false;
+    }
+  }
+
+  if (IsCommaFound) {
+    Diag(Tok.getLocation(), diag::err_expected_type);
+    IsCorrect = false;
+    if (Tok.is(tok::annot_pragma_openmp_end))
+      return DeclGroupPtrTy();
+  }
+
+  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 (Tok.is(tok::colon))
+    ConsumeAnyToken();
+  else {
+    Diag(Tok.getLocation(), diag::err_expected) << "':'";
+    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(), Loc, Name,
+      ReductionTypes.size(), AS);
+
+  // Parse <combiner> expression and then parse initializer if any for each
+  // correct type.
+  unsigned i = 0, e = ReductionTypes.size();
+  for (auto &&Data : ReductionTypes) {
+    TentativeParsingAction TPA(*this);
+    ParseScope OMPDRScope(this, Scope::FnScope | Scope::DeclScope);
+    Actions.InitOpenMPDeclareReductionCombiner(getCurScope(), Loc, DRD,
+                                               Data.first);
+    // Parse <combiner> expression.
+    ExprResult CombinerResult =
+        Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+    Actions.FinishOpenMPDeclareReductionCombiner(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 <initializer> expression.
+      if (Tok.isAnyIdentifier() &&
+          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);
+        Actions.InitOpenMPDeclareReductionInitializer(getCurScope(), Loc, DRD,
+                                                      Data.first);
+        // Parse expression.
+        InitializerResult =
+            Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+        IsCorrect = !Actions.FinishOpenMPDeclareReductionInitializer(
+                        InitializerResult.get()) &&
+                    IsCorrect;
+        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();
+  }
+  if (IsCorrect)
+    Actions.ActOnOpenMPDeclareReductionDirectiveEnd(
+        getCurScope(), Actions.getCurLexicalContext(), DRD, ReductionTypes,
+        CombinersInitializers);
+  else
+    Actions.ActOnOpenMPDeclareReductionDirectiveError(DRD);
+  return 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' '(' <reduction_id> ':'
+///         <type> {',' <type>} ':' <expression> ')' ['initializer' '('
+///         ('omp_priv' '=' <expression>|<function_call>) ')']
+///         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);
 
@@ -100,6 +353,21 @@
       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);
+        SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+      }
+      // Skip the last annot_pragma_openmp_end.
+      ConsumeToken();
+      return Res;
+    }
+    break;
   case OMPD_unknown:
     Diag(Tok, diag::err_omp_unknown_directive);
     break;
@@ -141,6 +409,12 @@
 ///         annot_pragma_openmp 'threadprivate' simple-variable-list
 ///         annot_pragma_openmp_end
 ///
+///       declare-reduction-directive:
+///         annot_pragma_openmp 'declare' 'reduction' '(' <reduction_id> ':'
+///         <type> {',' <type>} ':' <expression> ')' ['initializer' '('
+///         ('omp_priv' '=' <expression>|<function_call>) ')']
+///         annot_pragma_openmp_end
+///
 ///       executable-directive:
 ///         annot_pragma_openmp 'parallel' | 'simd' | 'for' | 'sections' |
 ///         'section' | 'single' | 'master' | 'critical' [ '(' <name> ')' ] |
@@ -186,6 +460,20 @@
     }
     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);
+        SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+      }
+      Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation());
+    }
+    SkipUntil(tok::annot_pragma_openmp_end);
+    break;
   case OMPD_flush:
     if (PP.LookAhead(0).is(tok::l_paren)) {
       FlushHasClause = true;
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -2969,7 +2969,7 @@
       }
 
       if (Tok.is(tok::annot_pragma_openmp)) {
-        ParseOpenMPDeclarativeDirective();
+        ParseOpenMPDeclarativeDirective(CurAS);
         continue;
       }
 
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -3603,6 +3603,11 @@
       continue;
     }
 
+    if (Tok.is(tok::annot_pragma_openmp)) {
+      ParseOpenMPDeclarativeDirective(AS_public);
+      continue;
+    }
+
     if (!Tok.is(tok::at)) {
       auto CFieldCallback = [&](ParsingFieldDeclarator &FD) {
         // Install the declarator into the current TagDecl.
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -20,6 +20,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"
@@ -113,6 +114,9 @@
     return EmitVarDecl(VD);
   }
 
+  case Decl::OMPDeclareReduction:
+    return CGM.EmitOMPDeclareReduction(cast<OMPDeclareReductionDecl>(&D));
+
   case Decl::Typedef:      // typedef int X;
   case Decl::TypeAlias: {  // using X = int; [C++0x]
     const TypedefNameDecl &TD = cast<TypedefNameDecl>(D);
@@ -1802,3 +1806,9 @@
   if (D.hasAttr<AnnotateAttr>())
       EmitVarAnnotations(&D, DeclPtr);
 }
+
+void CodeGenModule::EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D) {
+  llvm_unreachable("Codegen for 'omp declare reduction' is not supported yet.");
+}
+
+
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -3387,6 +3387,10 @@
     break;
   }
 
+  case Decl::OMPDeclareReduction:
+    EmitOMPDeclareReduction(cast<OMPDeclareReductionDecl>(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/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -20,6 +20,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"
@@ -1126,6 +1127,10 @@
   /// \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/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -8317,7 +8317,7 @@
     // We never need to emit an uninstantiated function template.
     if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
       return false;
-  } else if (isa<OMPThreadPrivateDecl>(D))
+  } else if (isa<OMPThreadPrivateDecl>(D) || isa<OMPDeclareReductionDecl>(D))
     return true;
   else
     return false;
Index: lib/AST/DeclBase.cpp
===================================================================
--- lib/AST/DeclBase.cpp
+++ lib/AST/DeclBase.cpp
@@ -598,6 +598,9 @@
     case TemplateTemplateParm:
       return IDNS_Ordinary | IDNS_Tag | IDNS_Type;
 
+    case OMPDeclareReduction:
+      return IDNS_OMPReduction;
+
     // Never have names.
     case Friend:
     case FriendTemplate:
@@ -924,6 +927,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/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"
@@ -68,6 +69,8 @@
   const DeclContext *DC = D->getDeclContext();
   if (const CapturedDecl *CD = dyn_cast<CapturedDecl>(DC))
     return getEffectiveDeclContext(CD);
+  if (auto *DR = dyn_cast<OMPDeclareReductionDecl>(DC))
+      return getEffectiveDeclContext(DR);
 
   if (const auto *VD = dyn_cast<VarDecl>(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"
@@ -59,6 +60,8 @@
   const DeclContext *DC = D->getDeclContext();
   if (const CapturedDecl *CD = dyn_cast<CapturedDecl>(DC))
     return getEffectiveDeclContext(CD);
+  if (auto *DR = dyn_cast<OMPDeclareReductionDecl>(DC))
+    return getEffectiveDeclContext(DR);
 
   return DC;
 }
Index: lib/AST/DeclOpenMP.cpp
===================================================================
--- lib/AST/DeclOpenMP.cpp
+++ lib/AST/DeclOpenMP.cpp
@@ -52,3 +52,53 @@
   std::copy(VL.begin(), VL.end(), Vars);
 }
 
+//===----------------------------------------------------------------------===//
+// OMPDeclareReductionDecl Implementation.
+//===----------------------------------------------------------------------===//
+
+void OMPDeclareReductionDecl::anchor() {}
+
+intptr_t OMPDeclareReductionDecl::getReductionDataOffset() {
+  return llvm::RoundUpToAlignment(sizeof(OMPDeclareReductionDecl),
+                                  llvm::alignOf<ReductionData>());
+}
+
+OMPDeclareReductionDecl *OMPDeclareReductionDecl::Create(ASTContext &C,
+                                                         DeclContext *DC,
+                                                         SourceLocation L,
+                                                         DeclarationName Name,
+                                                         unsigned N) {
+  auto Size = getReductionDataOffset() + N * sizeof(ReductionData);
+
+  OMPDeclareReductionDecl *D =
+      new (C, DC, Size - sizeof(OMPDeclareReductionDecl))
+          OMPDeclareReductionDecl(OMPDeclareReduction, DC, L, Name);
+  D->NumReductions = N;
+  return D;
+}
+
+OMPDeclareReductionDecl *
+OMPDeclareReductionDecl::CreateDeserialized(ASTContext &C, unsigned ID,
+                                            unsigned N) {
+  auto Size = getReductionDataOffset() + N * sizeof(ReductionData);
+
+  OMPDeclareReductionDecl *D =
+      new (C, ID, Size - sizeof(OMPDeclareReductionDecl))
+          OMPDeclareReductionDecl(OMPDeclareReduction, /*DC=*/nullptr,
+                                  SourceLocation(), DeclarationName());
+  D->NumReductions = N;
+  return D;
+}
+
+void OMPDeclareReductionDecl::addReductions(
+    ArrayRef<std::pair<QualType, SourceRange>> ReductionTypes,
+    ArrayRef<std::pair<Expr *, Expr *>> CombinersInitializers) {
+  assert(ReductionTypes.size() == CombinersInitializers.size());
+  ReductionData *Data = reinterpret_cast<ReductionData *>(
+      reinterpret_cast<char *>(this) + getReductionDataOffset());
+  for (unsigned i =0, e = ReductionTypes.size(); i < e; ++i)
+    Data[i] = ReductionData(ReductionTypes[i].first, ReductionTypes[i].second,
+                            CombinersInitializers[i].first,
+                            CombinersInitializers[i].second);
+}
+
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);
@@ -304,7 +305,7 @@
 
     // FIXME: Need to be able to tell the DeclPrinter when
     const char *Terminator = nullptr;
-    if (isa<OMPThreadPrivateDecl>(*D))
+    if (isa<OMPThreadPrivateDecl>(*D) || isa<OMPDeclareReductionDecl>(*D))
       Terminator = nullptr;
     else if (isa<FunctionDecl>(*D) &&
              cast<FunctionDecl>(*D)->isThisDeclarationADefinition())
@@ -1320,3 +1321,23 @@
   }
 }
 
+void DeclPrinter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) {
+  if (!D->isInvalidDecl() && !D->reductions_empty()) {
+    for (auto &&Data : D->reductions()) {
+      Out << "#pragma omp declare reduction (";
+      D->printName(Out);
+      Out << " : ";
+      Data.ReductionType.print(Out, Policy);
+      Out << " : ";
+      Data.Combiner->printPretty(Out, 0, Policy, 0);
+      Out << ")";
+      if (Data.Initializer) {
+        Out << " initializer(";
+        Data.Initializer->printPretty(Out, 0, Policy, 0);
+        Out << ")";
+      }
+      Out << "\n";
+    }
+  }
+}
+
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 are 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/Sema/SemaLookup.cpp
===================================================================
--- lib/Sema/SemaLookup.cpp
+++ lib/Sema/SemaLookup.cpp
@@ -279,6 +279,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
@@ -1827,6 +1831,7 @@
     case LookupNamespaceName:
     case LookupObjCProtocolName:
     case LookupLabel:
+    case LookupOMPReductionName:
       // These lookups will never find a member in a C++ class (or base class).
       return false;
 
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2452,6 +2452,84 @@
   return TD;
 }
 
+Decl *TemplateDeclInstantiator::VisitOMPDeclareReductionDecl(
+    OMPDeclareReductionDecl *D) {
+  SmallVector<std::pair<QualType, SourceRange>, 8> ReductionTypes;
+  SmallVector<std::pair<Expr *, Expr *>, 8> CombinersInitializers;
+  ReductionTypes.reserve(D->reductions_size());
+  CombinersInitializers.reserve(D->reductions_size());
+
+  // Create instantiated copy.
+  auto DRD = SemaRef.ActOnOpenMPDeclareReductionDirectiveStart(
+      /*S=*/nullptr, Owner, D->getLocStart(), D->getDeclName(),
+      D->reductions_size(), D->getAccess());
+  auto *NewDRD = cast<OMPDeclareReductionDecl>(DRD.get().getSingleDecl());
+  if (isDeclWithinFunction(NewDRD))
+    SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewDRD);
+  bool IsCorrect = true;
+  for (auto &&Data : D->reductions()) {
+    // Instantiate type and check if it is allowed.
+    QualType SubstReductionType =
+        SemaRef.SubstType(Data.ReductionType, TemplateArgs,
+                          Data.TyRange.getBegin(), DeclarationName());
+    if (SubstReductionType.isNull() ||
+        !SemaRef.isOpenMPDeclareReductionTypeAllowed(
+            Data.TyRange, SubstReductionType, ReductionTypes)) {
+      IsCorrect = false;
+      continue;
+    }
+    Expr *SubstCombiner = nullptr;
+    Expr *SubstInitializer = nullptr;
+    // Combiners instantiation sequence.
+    if (Data.Combiner) {
+      SemaRef.InitOpenMPDeclareReductionCombiner(
+          /*S=*/nullptr, D->getLocation(), DRD, SubstReductionType);
+      for (auto *Local : D->noload_decls()) {
+        auto Lookup =
+            NewDRD->noload_lookup(cast<NamedDecl>(Local)->getDeclName());
+        if (!Lookup.empty()) {
+          assert(Lookup.size() == 1);
+          SemaRef.CurrentInstantiationScope->InstantiatedLocal(Local,
+                                                               Lookup.front());
+        }
+      }
+      SubstCombiner = SemaRef.SubstExpr(Data.Combiner, TemplateArgs).get();
+      SemaRef.FinishOpenMPDeclareReductionCombiner(SubstCombiner);
+    }
+    // Initializers instantiation sequence.
+    if (Data.Initializer) {
+      SemaRef.InitOpenMPDeclareReductionInitializer(
+          /*S=*/nullptr, D->getLocation(), DRD, SubstReductionType);
+      for (auto *Local : D->noload_decls()) {
+        auto Lookup =
+            NewDRD->noload_lookup(cast<NamedDecl>(Local)->getDeclName());
+        if (!Lookup.empty()) {
+          assert(Lookup.size() == 1);
+          SemaRef.CurrentInstantiationScope->InstantiatedLocal(Local,
+                                                               Lookup.front());
+        }
+      }
+      SubstInitializer =
+          SemaRef.SubstExpr(Data.Initializer, TemplateArgs).get();
+      SemaRef.FinishOpenMPDeclareReductionInitializer(SubstInitializer);
+    }
+    if (!SubstCombiner || (Data.Initializer && !SubstInitializer)) {
+      IsCorrect = false;
+      continue;
+    }
+    ReductionTypes.push_back(std::make_pair(SubstReductionType, Data.TyRange));
+    CombinersInitializers.push_back(
+        std::make_pair(SubstCombiner, SubstInitializer));
+  }
+  if (IsCorrect)
+    SemaRef.ActOnOpenMPDeclareReductionDirectiveEnd(
+        /*S=*/nullptr, Owner, DRD, ReductionTypes, CombinersInitializers);
+  else
+    SemaRef.ActOnOpenMPDeclareReductionDirectiveError(DRD);
+
+  return DRD.get().getSingleDecl();
+}
+
 Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
   return VisitFunctionDecl(D, nullptr);
 }
Index: lib/Sema/SemaOpenMP.cpp
===================================================================
--- lib/Sema/SemaOpenMP.cpp
+++ lib/Sema/SemaOpenMP.cpp
@@ -1335,6 +1335,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");
@@ -2125,6 +2126,7 @@
     Res = ActOnOpenMPCancelDirective(StartLoc, EndLoc, CancelRegion);
     break;
   case OMPD_threadprivate:
+  case OMPD_declare_reduction:
     llvm_unreachable("OpenMP Directive is not allowed");
   case OMPD_unknown:
     llvm_unreachable("Unknown OpenMP directive");
@@ -6565,3 +6567,261 @@
                                  DepLoc, ColonLoc, Vars);
 }
 
+bool Sema::isOpenMPDeclareReductionTypeAllowed(
+    SourceRange TyRange, QualType ReductionType,
+    ArrayRef<std::pair<QualType, SourceRange>> RegisteredReductionTypes) {
+  assert(!ReductionType.isNull());
+
+  // [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(TyRange.getBegin(), diag::err_omp_reduction_qualified_type) << TyRange;
+    return false;
+  }
+
+  if (ReductionType->isFunctionType() ||
+      ReductionType->isFunctionNoProtoType() ||
+      ReductionType->isFunctionProtoType() ||
+      ReductionType->isFunctionPointerType() ||
+      ReductionType->isMemberFunctionPointerType()) {
+    Diag(TyRange.getBegin(), diag::err_omp_reduction_function_type) << TyRange;
+    return false;
+  }
+  if (ReductionType->isReferenceType()) {
+    Diag(TyRange.getBegin(), diag::err_omp_reduction_reference_type) << TyRange;
+    return false;
+  }
+  if (ReductionType->isArrayType()) {
+    Diag(TyRange.getBegin(), diag::err_omp_reduction_array_type) << TyRange;
+    return false;
+  }
+
+  bool IsValid = true;
+  for (auto &&Data : RegisteredReductionTypes) {
+    if (Context.hasSameType(ReductionType, Data.first)) {
+      Diag(TyRange.getBegin(), diag::err_omp_reduction_redeclared)
+          << ReductionType << TyRange;
+      Diag(Data.second.getBegin(), diag::note_previous_declaration)
+          << Data.second;
+      IsValid = false;
+      break;
+    }
+  }
+  return IsValid;
+}
+
+Sema::DeclGroupPtrTy Sema::ActOnOpenMPDeclareReductionDirectiveStart(
+    Scope *S, DeclContext *DC, SourceLocation Loc, DeclarationName Name,
+    unsigned NumReductionTypes, AccessSpecifier AS) {
+  auto *DRD = OMPDeclareReductionDecl::Create(Context, DC, Loc, Name,
+                                              NumReductionTypes);
+  DC->addDecl(DRD);
+  DRD->setAccess(AS);
+  Decl *Decls[] = {DRD};
+  return DeclGroupPtrTy::make(
+      DeclGroupRef::Create(Context, Decls, /*NumDecls=*/1));
+}
+
+void Sema::InitOpenMPDeclareReductionCombiner(Scope *S, SourceLocation Loc,
+                                              DeclGroupPtrTy DeclReduction,
+                                              QualType ReductionType) {
+  assert(DeclReduction && DeclReduction.get().isSingleDecl());
+  auto *DRD =
+      cast<OMPDeclareReductionDecl>(DeclReduction.get().getSingleDecl());
+
+  // Enter new function scope.
+  PushFunctionScope();
+
+  if (S)
+    PushDeclContext(S, DRD);
+  else
+    CurContext = DRD;
+
+  PushExpressionEvaluationContext(PotentiallyEvaluated);
+
+  // Create 'T omp_in;' implicit param.
+  auto *OmpInParm = ImplicitParamDecl::Create(
+      Context, DRD, Loc, &Context.Idents.get("omp_in"), ReductionType);
+  // Create 'T &omp_out;' implicit param.
+  auto *OmpOutParm = ImplicitParamDecl::Create(
+      Context, DRD, Loc, &Context.Idents.get("omp_out"),
+      Context.getLValueReferenceType(ReductionType));
+  if (S) {
+    PushOnScopeChains(OmpInParm, S);
+    PushOnScopeChains(OmpOutParm, S);
+  } else {
+    DRD->addDecl(OmpInParm);
+    DRD->addDecl(OmpOutParm);
+  }
+}
+
+namespace {
+class DeclareReductionCombinerInitializerChecker
+    : public ConstStmtVisitor<DeclareReductionCombinerInitializerChecker,
+                              bool> {
+  Sema &SemaRef;
+  bool Combiner;
+
+public:
+  bool VisitDeclRefExpr(const DeclRefExpr *E) {
+    if (auto VD = dyn_cast<VarDecl>(E->getDecl())) {
+      if (!VD->hasLocalStorage()) {
+        SemaRef.Diag(E->getLocStart(),
+                     Combiner ? diag::err_omp_wrong_var_for_combiner
+                              : diag::err_omp_wrong_var_for_initializer)
+            << E->getSourceRange();
+        SemaRef.Diag(VD->getLocation(), diag::note_defined_here)
+            << VD << VD->getSourceRange();
+        return true;
+      }
+    }
+    return false;
+  }
+  bool VisitStmt(const Stmt *S) {
+    for (auto Child : S->children()) {
+      if (Child && Visit(Child))
+        return true;
+    }
+    return false;
+  }
+  explicit DeclareReductionCombinerInitializerChecker(Sema &SemaRef,
+                                                      bool Combiner)
+      : SemaRef(SemaRef), Combiner(Combiner) {}
+};
+} // namespace
+
+bool Sema::FinishOpenMPDeclareReductionCombiner(Expr *Combiner) {
+  DiscardCleanupsInEvaluationContext();
+  PopExpressionEvaluationContext();
+
+  PopDeclContext();
+  PopFunctionScopeInfo();
+
+  // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
+  // Only the variables omp_in and omp_out are allowed in the combiner.
+  DeclareReductionCombinerInitializerChecker CombinerChecker(*this,
+                                                             /*Combiner=*/true);
+  return !Combiner || CombinerChecker.Visit(Combiner);
+}
+
+void Sema::InitOpenMPDeclareReductionInitializer(Scope *S, SourceLocation Loc,
+                                                 DeclGroupPtrTy DeclReduction,
+                                                 QualType ReductionType) {
+  assert(DeclReduction && DeclReduction.get().isSingleDecl());
+  auto *DRD =
+      cast<OMPDeclareReductionDecl>(DeclReduction.get().getSingleDecl());
+
+  // Enter new function scope.
+  PushFunctionScope();
+
+  if (S)
+    PushDeclContext(S, DRD);
+  else
+    CurContext = DRD;
+
+  PushExpressionEvaluationContext(PotentiallyEvaluated);
+
+  // Create 'T omp_orig;' implicit param.
+  auto *OmpOrigParm = ImplicitParamDecl::Create(
+      Context, DRD, Loc, &Context.Idents.get("omp_orig"), ReductionType);
+  // Create 'T &omp_priv;' implicit param.
+  auto *OmpPrivParm = ImplicitParamDecl::Create(
+      Context, DRD, Loc, &Context.Idents.get("omp_priv"),
+      Context.getLValueReferenceType(ReductionType));
+  if (S) {
+    PushOnScopeChains(OmpPrivParm, S);
+    PushOnScopeChains(OmpOrigParm, S);
+  } else {
+    DRD->addDecl(OmpPrivParm);
+    DRD->addDecl(OmpOrigParm);
+  }
+}
+
+bool Sema::FinishOpenMPDeclareReductionInitializer(Expr *Initializer) {
+  DiscardCleanupsInEvaluationContext();
+  PopExpressionEvaluationContext();
+
+  PopDeclContext();
+  PopFunctionScopeInfo();
+
+  // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
+  // Only the variables omp_priv and omp_orig are allowed in the
+  // initializer-clause.
+  DeclareReductionCombinerInitializerChecker InitializerChecker(
+      *this, /*Combiner=*/false);
+  return !Initializer || InitializerChecker.Visit(Initializer);
+}
+
+void Sema::ActOnOpenMPDeclareReductionDirectiveError(
+    DeclGroupPtrTy DeclReduction) {
+  assert(DeclReduction && DeclReduction.get().isSingleDecl());
+  DeclReduction.get().getSingleDecl()->setInvalidDecl();
+}
+
+void Sema::ActOnOpenMPDeclareReductionDirectiveEnd(
+    Scope *S, DeclContext *DC, DeclGroupPtrTy DeclReduction,
+    ArrayRef<std::pair<QualType, SourceRange>> ReductionTypes,
+    ArrayRef<std::pair<Expr *, Expr *>> CombinersInitializers) {
+  assert(DeclReduction && DeclReduction.get().isSingleDecl());
+  assert(!ReductionTypes.empty());
+  assert(ReductionTypes.size() == CombinersInitializers.size());
+  auto *DRD =
+      cast<OMPDeclareReductionDecl>(DeclReduction.get().getSingleDecl());
+  DRD->addReductions(ReductionTypes, CombinersInitializers);
+
+  // [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.
+  for (size_t i = 0, e = ReductionTypes.size() - 1; i < e; ++i) {
+    for (size_t i1 = i + 1, e1 = e + 1; i1 < e1; ++i1) {
+      if (Context.typesAreCompatible(ReductionTypes[i].first,
+                                     ReductionTypes[i1].first,
+                                     /*CompareUnqualified=*/true)) {
+        Diag(ReductionTypes[i1].second.getBegin(),
+             diag::err_omp_declare_reduction_redefinition)
+            << ReductionTypes[i1].first << ReductionTypes[i1].second;
+        Diag(ReductionTypes[i].second.getBegin(),
+             diag::note_previous_definition)
+            << ReductionTypes[i].second;
+        DRD->setInvalidDecl();
+      }
+    }
+  }
+  if (!S)
+    S = getScopeForContext(DC);
+  if (S) {
+    LookupResult Lookup(*this, DRD->getDeclName(), DRD->getLocation(),
+                        LookupOMPReductionName);
+    Lookup.suppressDiagnostics();
+    LookupName(Lookup, S);
+    FilterLookupForScope(Lookup, DC, S, /*ConsiderLinkage=*/false,
+                         /*AllowInlineNamespace=*/false);
+    auto Filter = Lookup.makeFilter();
+    while (Filter.hasNext()) {
+      auto *PrevDecl = cast<OMPDeclareReductionDecl>(Filter.next());
+      if (!PrevDecl->isInvalidDecl() && PrevDecl != DRD)
+        for (auto &&PrevRed : PrevDecl->reductions()) {
+          for (auto &&Red : ReductionTypes) {
+            if (Context.typesAreCompatible(Red.first, PrevRed.ReductionType,
+                                           /*CompareUnqualified=*/true)) {
+              Diag(Red.second.getBegin(),
+                   diag::err_omp_declare_reduction_redefinition)
+                  << Red.first << Red.second;
+              Diag(PrevRed.TyRange.getBegin(), diag::note_previous_definition)
+                  << PrevRed.TyRange;
+              DRD->setInvalidDecl();
+            }
+          }
+        }
+      if (PrevDecl->isInvalidDecl())
+        Filter.erase();
+    }
+    Filter.done();
+    if (!DRD->isInvalidDecl())
+      PushOnScopeChains(DRD, S, /*AddToContext=*/false);
+  }
+}
+
Index: lib/Basic/OpenMPKinds.cpp
===================================================================
--- lib/Basic/OpenMPKinds.cpp
+++ lib/Basic/OpenMPKinds.cpp
@@ -350,6 +350,7 @@
   case OMPD_cancellation_point:
   case OMPD_cancel:
   case OMPD_ordered:
+  case OMPD_declare_reduction:
     break;
   }
   return false;
Index: tools/libclang/CIndex.cpp
===================================================================
--- tools/libclang/CIndex.cpp
+++ tools/libclang/CIndex.cpp
@@ -5099,6 +5099,7 @@
   case Decl::ClassScopeFunctionSpecialization:
   case Decl::Import:
   case Decl::OMPThreadPrivate:
+  case Decl::OMPDeclareReduction:
   case Decl::ObjCTypeParam:
     return C;
 
Index: include/clang/Serialization/ASTBitCodes.h
===================================================================
--- include/clang/Serialization/ASTBitCodes.h
+++ include/clang/Serialization/ASTBitCodes.h
@@ -1108,6 +1108,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: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -2416,7 +2416,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/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -1463,6 +1463,14 @@
   }
 })
 
+DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, {
+  for (auto &&Data : D->reductions()) {
+    TRY_TO(TraverseType(Data.ReductionType));
+    TRY_TO(TraverseStmt(Data.Combiner));
+    TRY_TO(TraverseStmt(Data.Initializer));
+  }
+})
+
 // A helper method for TemplateDecl's children.
 template <typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseTemplateParameterListHelper(
Index: include/clang/AST/DataRecursiveASTVisitor.h
===================================================================
--- include/clang/AST/DataRecursiveASTVisitor.h
+++ include/clang/AST/DataRecursiveASTVisitor.h
@@ -1389,6 +1389,14 @@
   }
 })
 
+DEF_TRAVERSE_DECL(OMPDeclareReductionDecl, {
+  for (auto &&Data : D->reductions()) {
+    TRY_TO(TraverseType(Data.ReductionType));
+    TRY_TO(TraverseStmt(Data.Combiner));
+    TRY_TO(TraverseStmt(Data.Initializer));
+  }
+})
+
 // A helper method for TemplateDecl's children.
 template <typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseTemplateParameterListHelper(
Index: include/clang/AST/DeclOpenMP.h
===================================================================
--- include/clang/AST/DeclOpenMP.h
+++ include/clang/AST/DeclOpenMP.h
@@ -15,7 +15,8 @@
 #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/Type.h"
 #include "llvm/ADT/ArrayRef.h"
 
 namespace clang {
@@ -85,6 +86,98 @@
   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 {
+public:
+  /// \brief A record with details of a single 'declare reduction' construct.
+  struct ReductionData {
+    ReductionData(QualType ReductionType, SourceRange TyRange, Expr *Combiner,
+                  Expr *Initializer)
+        : ReductionType(ReductionType), TyRange(TyRange), Combiner(Combiner),
+          Initializer(Initializer) {}
+    ReductionData() : Combiner(nullptr), Initializer(nullptr) {}
+    QualType ReductionType;
+    SourceRange TyRange;
+    Expr *Combiner;
+    Expr *Initializer;
+  };
+
+private:
+  friend class ASTDeclReader;
+  /// \brief Number of reductions defined within current user-defined reduction
+  /// construct (depends on number of types).
+  unsigned NumReductions;
+
+  virtual void anchor();
+
+  OMPDeclareReductionDecl(Kind DK, DeclContext *DC, SourceLocation L,
+                          DeclarationName Name)
+      : NamedDecl(DK, DC, L, Name), DeclContext(DK), NumReductions(0) {
+    setModulePrivate();
+  }
+
+  /// \brief Get offset to the reduction data storage.
+  static intptr_t getReductionDataOffset();
+
+  /// \brief Get reduction data for the current declare reduction construct.
+  ArrayRef<ReductionData> getReductionData() const {
+    return ArrayRef<ReductionData>(
+        reinterpret_cast<const ReductionData *>(
+            reinterpret_cast<const char *>(this) + getReductionDataOffset()),
+        NumReductions);
+  }
+
+  /// \brief Get reduction data for the current declare reduction construct.
+  llvm::MutableArrayRef<ReductionData> getReductionData() {
+    return llvm::MutableArrayRef<ReductionData>(
+        reinterpret_cast<ReductionData *>(reinterpret_cast<char *>(this) +
+                                          getReductionDataOffset()),
+        NumReductions);
+  }
+
+public:
+  /// \brief Create declare reduction node with \p N internal reductions.
+  static OMPDeclareReductionDecl *Create(ASTContext &C, DeclContext *DC,
+                                         SourceLocation L, DeclarationName Name,
+                                         unsigned N);
+  /// \brief Create deserialized declare reduction node with \p N internal
+  /// reductions.
+  static OMPDeclareReductionDecl *CreateDeserialized(ASTContext &C, unsigned ID,
+                                                     unsigned N);
+
+  /// \brief Add new reduction construct for type \p ReductionTypes with
+  /// combiners and initializers \p CombinersInitializers.
+  void addReductions(ArrayRef<std::pair<QualType, SourceRange>> ReductionTypes,
+                     ArrayRef<std::pair<Expr *, Expr *>> CombinersInitializers);
+
+  typedef llvm::MutableArrayRef<ReductionData> reductions_list;
+  typedef ArrayRef<ReductionData> reductions_const_list;
+
+  unsigned reductions_size() const { return NumReductions; }
+  bool reductions_empty() const { return NumReductions == 0; }
+  reductions_list reductions() { return getReductionData(); }
+  reductions_const_list reductions() const { return getReductionData(); }
+
+  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<DeclContext *>(const_cast<OMPDeclareReductionDecl *>(D));
+  }
+  static OMPDeclareReductionDecl *castFromDeclContext(const DeclContext *DC) {
+    return static_cast<OMPDeclareReductionDecl *>(
+        const_cast<DeclContext *>(DC));
+  }
+};
+
 }  // end namespace clang
 
 #endif
Index: include/clang/AST/DeclBase.h
===================================================================
--- include/clang/AST/DeclBase.h
+++ include/clang/AST/DeclBase.h
@@ -154,7 +154,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
@@ -284,7 +287,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.
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;
@@ -2631,6 +2632,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
   };
@@ -7708,6 +7711,12 @@
   /// is performed.
   bool isOpenMPPrivateVar(VarDecl *VD, unsigned Level);
 
+  /// \brief Check if the specified type is allowed to be used in 'omp declare
+  /// reduction' construct.
+  bool isOpenMPDeclareReductionTypeAllowed(
+      SourceRange TyRange, QualType ReductionType,
+      ArrayRef<std::pair<QualType, SourceRange>> RegisteredReductionTypes);
+
   ExprResult PerformOpenMPImplicitIntegerConversion(SourceLocation OpLoc,
                                                     Expr *Op);
   /// \brief Called on start of new data sharing attribute block.
@@ -7741,6 +7750,29 @@
   OMPThreadPrivateDecl *CheckOMPThreadPrivateDecl(
                                      SourceLocation Loc,
                                      ArrayRef<Expr *> VarList);
+  /// \brief Called on start of '#pragma omp declare reduction'.
+  DeclGroupPtrTy ActOnOpenMPDeclareReductionDirectiveStart(
+      Scope *S, DeclContext *DC, SourceLocation Loc, DeclarationName Name,
+      unsigned NumReductionTypes, AccessSpecifier AS);
+  /// \brief Initialize next declare reduction construct combiner.
+  void InitOpenMPDeclareReductionCombiner(Scope *S, SourceLocation Loc,
+                                          DeclGroupPtrTy DeclReduction,
+                                          QualType ReductionType);
+  /// \brief Finish current declare reduction construct combiner.
+  bool FinishOpenMPDeclareReductionCombiner(Expr *Combiner);
+  /// \brief Initialize next declare reduction construct initializer.
+  void InitOpenMPDeclareReductionInitializer(Scope *S, SourceLocation Loc,
+                                             DeclGroupPtrTy DeclReduction,
+                                             QualType ReductionType);
+  /// \brief Finish current declare reduction construct combiner.
+  bool FinishOpenMPDeclareReductionInitializer(Expr *Initializer);
+  /// \brief Called on bad-formed '#pragma omp declare reduction'.
+  void ActOnOpenMPDeclareReductionDirectiveError(DeclGroupPtrTy DeclReduction);
+  /// \brief Called on well-formed '#pragma omp declare reduction'.
+  void ActOnOpenMPDeclareReductionDirectiveEnd(
+      Scope *S, DeclContext *DC, DeclGroupPtrTy DeclReduction,
+      ArrayRef<std::pair<QualType, SourceRange>> ReductionTypes,
+      ArrayRef<std::pair<Expr *, Expr *>> CombinersInitializers);
 
   /// \brief Initialization of captured region for OpenMP region.
   void ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope);
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -7643,6 +7643,14 @@
   "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_qualified_type : Error<"a type name cannot be qualified with 'const', 'volatile' or 'restrict'">;
+def err_omp_reduction_function_type : Error<"a type name cannot be a function type">;
+def err_omp_reduction_reference_type : Error<"a type name cannot be a reference type">;
+def err_omp_reduction_array_type : Error<"a type name cannot be an array type">;
+def err_omp_reduction_redeclared : Error<"previous declaration with type %0 is found">;
+def err_omp_wrong_var_for_combiner : Error<"only 'omp_in' or 'omp_out' variables are allowed in combiner expression">;
+def err_omp_wrong_var_for_initializer : Error<"only 'omp_priv' or 'omp_orig' variables are allowed in initializer expression">;
+def err_omp_declare_reduction_redefinition : Error<"redefinition of user-defined reduction for type %0">;
 } // end of OpenMP category
 
 let CategoryName = "Related Result Type Issue" in {
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -989,6 +989,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: '+', '-', '*', '&', '|', '^', '&&' and '||'">;
 
 // Pragma loop support.
 def err_pragma_loop_missing_argument : Error<
Index: include/clang/Basic/DeclNodes.td
===================================================================
--- include/clang/Basic/DeclNodes.td
+++ include/clang/Basic/DeclNodes.td
@@ -71,6 +71,7 @@
       def ObjCImplementation : DDecl<ObjCImpl>;
   def ObjCProperty : DDecl<Named>;
   def ObjCCompatibleAlias : DDecl<Named>;
+  def OMPDeclareReduction : DDecl<Named>, DeclContext;
 def LinkageSpec : Decl, DeclContext;
 def ObjCPropertyImpl : Decl;
 def FileScopeAsm : Decl;
Index: include/clang/Basic/OpenMPKinds.def
===================================================================
--- include/clang/Basic/OpenMPKinds.def
+++ include/clang/Basic/OpenMPKinds.def
@@ -99,6 +99,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: test/OpenMP/declare_reduction_ast_print.cpp
===================================================================
--- test/OpenMP/declare_reduction_ast_print.cpp
+++ test/OpenMP/declare_reduction_ast_print.cpp
@@ -0,0 +1,44 @@
+// 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 T>
+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<int> d;
+
+void init(SSS<int> &lhs, SSS<int> 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<int> : omp_out = omp_in) initializer(init(omp_priv, omp_orig))
+
+int main() {
+  int i = 0;
+  SSS<int> sss;
+  // TODO: Add support for scoped reduction identifiers
+  //  #pragma omp parallel reduction(SSS<int>::fun : i)
+  // TODO-CHECK: #pragma omp parallel reduction(SSS<int>::fun: i)
+  {
+    i += 1;
+  }
+  // #pragma omp parallel reduction(::fun:sss)
+  // TODO-CHECK: #pragma omp parallel reduction(::fun: sss)
+  {
+  }
+}
+
+#endif
Index: test/OpenMP/declare_reduction_messages.c
===================================================================
--- test/OpenMP/declare_reduction_messages.c
+++ test/OpenMP/declare_reduction_messages.c
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s
+
+int temp; // expected-note 6 {{'temp' defined 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: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(#                                            // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(/                                            // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(+                                            // expected-error {{expected ':'}}
+#pragma omp declare reduction(for                                          // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(if:                                          // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#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 {{a type name cannot be qualified with 'const', 'volatile' or 'restrict'}}
+#pragma omp declare reduction(- : volatile int:                            // expected-error {{a type name 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-error {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}}
+#pragma omp declare reduction(fun : long(*)(void) : omp_out += omp_in)     // expected-error {{a type name cannot be a function type}}
+#pragma omp declare reduction(fun : long[3] : omp_out += omp_in)           // expected-error {{a type name cannot be an array type}}
+#pragma omp declare reduction(fun23 : long, int, long : omp_out += omp_in) // expected-error {{previous declaration with type 'long' is found}} expected-note {{previous declaration is here}}
+
+#pragma omp declare reduction(fun : 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'}}
+  }
+  return arg;
+}
Index: test/OpenMP/declare_reduction_messages.cpp
===================================================================
--- test/OpenMP/declare_reduction_messages.cpp
+++ test/OpenMP/declare_reduction_messages.cpp
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -verify -fopenmp -ferror-limit 100 %s
+
+int temp; // expected-note 8 {{'temp' defined 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: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(#                                            // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(/                                            // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(+                                            // expected-error {{expected ':'}}
+#pragma omp declare reduction(operator                                     // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#pragma omp declare reduction(operator:                                    // expected-error {{expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&' and '||'}}
+#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 {{a type name cannot be qualified with 'const', 'volatile' or 'restrict'}}
+#pragma omp declare reduction(- : volatile int:                            // expected-error {{a type name 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 {{only 'omp_in' or 'omp_out' variables are allowed in combiner expression}} expected-error {{expected 'initializer'}}
+#pragma omp declare reduction(fun : long & : omp_out += omp_in)            // expected-error {{a type name cannot be a reference type}}
+#pragma omp declare reduction(fun : long(*)(void) : omp_out += omp_in)     // expected-error {{a type name cannot be a function type}}
+#pragma omp declare reduction(fun : long[3] : omp_out += omp_in)           // expected-error {{a type name cannot be an array type}}
+#pragma omp declare reduction(fun23 : long, int, long : omp_out += omp_in) // expected-error {{previous declaration with type 'long' is found}} expected-note {{previous declaration is here}}
+
+template <class T>
+class Class1 {
+#pragma omp declare reduction(fun : T : temp)               // expected-error {{a type name cannot be a reference type}} expected-error 2 {{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 {{a type name 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 {{a type name cannot be a reference type}} expected-error {{previous declaration with type 'T' is found}} expected-note {{previous declaration is here}}
+};
+
+Class1<char &> e; // expected-note {{in instantiation of template class 'Class1<char &>' requested here}}
+
+template <class T>
+class Class2 {
+#pragma omp declare reduction(fun : T : omp_out += omp_in)
+};
+
+#pragma omp declare reduction(fun : long : omp_out += omp_in)                                          // expected-note {{previous definition is here}}
+#pragma omp declare reduction(fun : 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(fun7 : 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(fun8 : 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_in) initializer(omp_priv = 23)
+
+template <typename T>
+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'}}
+  }
+  return arg;
+}
+
+int main() {
+  Class1<int> c1; // expected-note {{in instantiation of template class 'Class1<int>' requested here}}
+  int i;
+  // TODO: Add support for scoped reduction identifiers
+  //  #pragma omp parallel reduction (::fun : c1)
+  {
+  }
+  //  #pragma omp parallel reduction (::Class1<int>::fun : c1)
+  {
+  }
+  //  #pragma omp parallel reduction (::Class2<int>::fun : i)
+  {
+  }
+  return fun(15);
+}
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,38 @@
+// 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))
+  return 0;
+}
+// CHECK: }
+
+#endif
_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to