cchen updated this revision to Diff 372032.
cchen added a comment.
Herald added a reviewer: sscalpone.

[OpenMP 5.0] metadirective

This patch supports OpenMP 5.0 metadirective features.
It is implemented keeping the OpenMP 5.1 features like dynamic user
condition in mind.

A new function, getBestWhenMatchForContext, is defined in
llvm/Frontend/OpenMP/OMPContext.h

Currently this function return the index of the when clause with the highest
score from the ones applicable in the Context.
But this function is declared with an array which can be used in OpenMP 5.1
implementation to select all the valid when clauses which can be resolved in
runtime.
Currently this array is set to null by default and its implementation is left
for future.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D91944/new/

https://reviews.llvm.org/D91944

Files:
  clang/include/clang-c/Index.h
  clang/include/clang/AST/RecursiveASTVisitor.h
  clang/include/clang/AST/StmtOpenMP.h
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/StmtNodes.td
  clang/include/clang/Sema/Sema.h
  clang/include/clang/Serialization/ASTBitCodes.h
  clang/lib/AST/OpenMPClause.cpp
  clang/lib/AST/StmtOpenMP.cpp
  clang/lib/AST/StmtPrinter.cpp
  clang/lib/AST/StmtProfile.cpp
  clang/lib/Basic/OpenMPKinds.cpp
  clang/lib/CodeGen/CGOpenMPRuntime.cpp
  clang/lib/CodeGen/CGStmt.cpp
  clang/lib/CodeGen/CGStmtOpenMP.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/Parse/ParseOpenMP.cpp
  clang/lib/Sema/SemaExceptionSpec.cpp
  clang/lib/Sema/SemaOpenMP.cpp
  clang/lib/Sema/TreeTransform.h
  clang/lib/Serialization/ASTReaderStmt.cpp
  clang/lib/Serialization/ASTWriterStmt.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/test/OpenMP/metadirective_ast_print.c
  clang/test/OpenMP/metadirective_device_kind_codegen.c
  clang/test/OpenMP/metadirective_device_kind_codegen.cpp
  clang/test/OpenMP/metadirective_empty.cpp
  clang/test/OpenMP/metadirective_implementation_codegen.c
  clang/test/OpenMP/metadirective_implementation_codegen.cpp
  clang/test/OpenMP/metadirective_messages.cpp
  clang/tools/libclang/CIndex.cpp
  clang/tools/libclang/CXCursor.cpp
  llvm/include/llvm/Frontend/OpenMP/OMP.td
  metadirective-finish.diff

Index: metadirective-finish.diff
===================================================================
--- /dev/null
+++ metadirective-finish.diff
@@ -0,0 +1,1300 @@
+diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
+index 26844d1c74f3..f7cfa0559ad6 100644
+--- a/clang/include/clang-c/Index.h
++++ b/clang/include/clang-c/Index.h
+@@ -2592,7 +2592,11 @@ enum CXCursorKind {
+    */
+   CXCursor_OMPUnrollDirective = 293,
+ 
+-  CXCursor_LastStmt = CXCursor_OMPUnrollDirective,
++  /** OpenMP metadirective directive.
++   */
++  CXCursor_OMPMetaDirective = 294,
++
++  CXCursor_LastStmt = CXCursor_OMPMetaDirective,
+ 
+   /**
+    * Cursor that represents the translation unit itself.
+diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
+index 9bfa5b9c2326..9b261e8540da 100644
+--- a/clang/include/clang/AST/RecursiveASTVisitor.h
++++ b/clang/include/clang/AST/RecursiveASTVisitor.h
+@@ -2842,6 +2842,9 @@ RecursiveASTVisitor<Derived>::TraverseOMPLoopDirective(OMPLoopDirective *S) {
+   return TraverseOMPExecutableDirective(S);
+ }
+ 
++DEF_TRAVERSE_STMT(OMPMetaDirective,
++                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
++
+ DEF_TRAVERSE_STMT(OMPParallelDirective,
+                   { TRY_TO(TraverseOMPExecutableDirective(S)); })
+ 
+diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
+index cd5fa2b94c31..c9124563a6e1 100644
+--- a/clang/include/clang/AST/StmtOpenMP.h
++++ b/clang/include/clang/AST/StmtOpenMP.h
+@@ -5379,6 +5379,44 @@ public:
+   }
+ };
+ 
++/// This represents '#pragma omp metadirective' directive.
++///
++/// \code
++/// #pragma omp metadirective when(user={condition(N>10)}: parallel for)
++/// \endcode
++/// In this example directive '#pragma omp metadirective' has clauses 'when'
++/// with a dynamic user condition to check if a variable 'N > 10'
++///
++class OMPMetaDirective final : public OMPExecutableDirective {
++  friend class ASTStmtReader;
++  friend class OMPExecutableDirective;
++  Stmt *IfStmt;
++
++  OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc)
++      : OMPExecutableDirective(OMPMetaDirectiveClass,
++                               llvm::omp::OMPD_metadirective, StartLoc,
++                               EndLoc) {}
++  explicit OMPMetaDirective()
++      : OMPExecutableDirective(OMPMetaDirectiveClass,
++                               llvm::omp::OMPD_metadirective, SourceLocation(),
++                               SourceLocation()) {}
++
++public:
++  static OMPMetaDirective *Create(const ASTContext &C, SourceLocation StartLoc,
++                                  SourceLocation EndLoc,
++                                  ArrayRef<OMPClause *> Clauses,
++                                  Stmt *AssociatedStmt, Stmt *IfStmt);
++  static OMPMetaDirective *CreateEmpty(const ASTContext &C, unsigned NumClauses,
++                                       EmptyShell);
++
++  void setIfStmt(Stmt *stmt) { IfStmt = stmt; }
++  Stmt *getIfStmt() const { return IfStmt; }
++
++  static bool classof(const Stmt *T) {
++    return T->getStmtClass() == OMPMetaDirectiveClass;
++  }
++};
++
+ } // end namespace clang
+ 
+ #endif
+diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
+index 6c6c397513a6..834f29fa028a 100644
+--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
++++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
+@@ -1436,6 +1436,9 @@ def warn_omp51_compat_attributes : Warning<
+   "specifying OpenMP directives with [[]] is incompatible with OpenMP "
+   "standards before OpenMP 5.1">,
+   InGroup<OpenMPPre51Compat>, DefaultIgnore;
++def err_omp_expected_colon : Error<"missing ':' in %0">;
++def err_omp_expected_context_selector
++    : Error<"expected valid context selector in %0">;
+ 
+ // Pragma loop support.
+ def err_pragma_loop_missing_argument : Error<
+diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
+index bebaf8fc9f0b..50c2ccc6e292 100644
+--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
++++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
+@@ -10787,6 +10787,8 @@ def err_omp_dispatch_statement_call
+ def err_omp_unroll_full_variable_trip_count : Error<
+   "loop to be fully unrolled must have a constant trip count">;
+ def note_omp_directive_here : Note<"'%0' directive found here">;
++def err_omp_instantiation_not_supported
++    : Error<"instantiation of '%0' not supported yet">;
+ } // end of OpenMP category
+ 
+ let CategoryName = "Related Result Type Issue" in {
+diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
+index 508f1fddf1b3..c5540a16056f 100644
+--- a/clang/include/clang/Basic/StmtNodes.td
++++ b/clang/include/clang/Basic/StmtNodes.td
+@@ -219,6 +219,7 @@ def AsTypeExpr : StmtNode<Expr>;
+ // OpenMP Directives.
+ def OMPCanonicalLoop : StmtNode<Stmt>;
+ def OMPExecutableDirective : StmtNode<Stmt, 1>;
++def OMPMetaDirective : StmtNode<OMPExecutableDirective>;
+ def OMPLoopBasedDirective : StmtNode<OMPExecutableDirective, 1>;
+ def OMPLoopDirective : StmtNode<OMPLoopBasedDirective, 1>;
+ def OMPParallelDirective : StmtNode<OMPExecutableDirective>;
+diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
+index d5c685ee9791..b38fc4b3f77e 100644
+--- a/clang/include/clang/Sema/Sema.h
++++ b/clang/include/clang/Sema/Sema.h
+@@ -10436,6 +10436,12 @@ public:
+   /// \param Init First part of the for loop.
+   void ActOnOpenMPLoopInitialization(SourceLocation ForLoc, Stmt *Init);
+ 
++  /// Called on well-formed '\#pragma omp metadirective' after parsing
++  /// of the  associated statement.
++  StmtResult ActOnOpenMPMetaDirective(ArrayRef<OMPClause *> Clauses,
++                                      Stmt *AStmt, SourceLocation StartLoc,
++                                      SourceLocation EndLoc);
++
+   // OpenMP directives and clauses.
+   /// Called on correct id-expression from the '#pragma omp
+   /// threadprivate'.
+@@ -10998,6 +11004,10 @@ public:
+                                      SourceLocation StartLoc,
+                                      SourceLocation LParenLoc,
+                                      SourceLocation EndLoc);
++  /// Called on well-formed 'when' clause.
++  OMPClause *ActOnOpenMPWhenClause(OMPTraitInfo &TI, SourceLocation StartLoc,
++                                   SourceLocation LParenLoc,
++                                   SourceLocation EndLoc);
+   /// Called on well-formed 'default' clause.
+   OMPClause *ActOnOpenMPDefaultClause(llvm::omp::DefaultKind Kind,
+                                       SourceLocation KindLoc,
+diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
+index 027a981df22c..e767f81a32d8 100644
+--- a/clang/include/clang/Serialization/ASTBitCodes.h
++++ b/clang/include/clang/Serialization/ASTBitCodes.h
+@@ -1890,6 +1890,7 @@ enum StmtCode {
+   STMT_SEH_TRY,                     // SEHTryStmt
+ 
+   // OpenMP directives
++  STMT_OMP_META_DIRECTIVE,
+   STMT_OMP_CANONICAL_LOOP,
+   STMT_OMP_PARALLEL_DIRECTIVE,
+   STMT_OMP_SIMD_DIRECTIVE,
+diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
+index 50f40395a197..0083a734d8b9 100644
+--- a/clang/lib/AST/OpenMPClause.cpp
++++ b/clang/lib/AST/OpenMPClause.cpp
+@@ -160,6 +160,7 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) {
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+     break;
+   default:
+     break;
+@@ -257,6 +258,7 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C)
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+     break;
+   default:
+     break;
+diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
+index b0ef2f49ba04..f461d9b65042 100644
+--- a/clang/lib/AST/StmtOpenMP.cpp
++++ b/clang/lib/AST/StmtOpenMP.cpp
+@@ -253,6 +253,25 @@ void OMPLoopDirective::setFinalsConditions(ArrayRef<Expr *> A) {
+   llvm::copy(A, getFinalsConditions().begin());
+ }
+ 
++OMPMetaDirective *OMPMetaDirective::Create(const ASTContext &C,
++                                           SourceLocation StartLoc,
++                                           SourceLocation EndLoc,
++                                           ArrayRef<OMPClause *> Clauses,
++                                           Stmt *AssociatedStmt, Stmt *IfStmt) {
++  auto *Dir = createDirective<OMPMetaDirective>(
++      C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc);
++  Dir->setIfStmt(IfStmt);
++  return Dir;
++}
++
++OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C,
++                                                unsigned NumClauses,
++                                                EmptyShell) {
++  return createEmptyDirective<OMPMetaDirective>(C, NumClauses,
++                                                /*HasAssociatedStmt=*/true,
++                                                /*NumChildren=*/1);
++}
++
+ OMPParallelDirective *OMPParallelDirective::Create(
+     const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+     ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Expr *TaskRedRef,
+diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
+index 45b15171aa97..4457497df6b7 100644
+--- a/clang/lib/AST/StmtPrinter.cpp
++++ b/clang/lib/AST/StmtPrinter.cpp
+@@ -654,6 +654,11 @@ void StmtPrinter::PrintOMPExecutableDirective(OMPExecutableDirective *S,
+     PrintStmt(S->getRawStmt());
+ }
+ 
++void StmtPrinter::VisitOMPMetaDirective(OMPMetaDirective *Node) {
++  Indent() << "#pragma omp metadirective";
++  PrintOMPExecutableDirective(Node);
++}
++
+ void StmtPrinter::VisitOMPParallelDirective(OMPParallelDirective *Node) {
+   Indent() << "#pragma omp parallel";
+   PrintOMPExecutableDirective(Node);
+diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
+index ed000c2467fa..1afa3773c711 100644
+--- a/clang/lib/AST/StmtProfile.cpp
++++ b/clang/lib/AST/StmtProfile.cpp
+@@ -903,6 +903,10 @@ void StmtProfiler::VisitOMPLoopDirective(const OMPLoopDirective *S) {
+   VisitOMPLoopBasedDirective(S);
+ }
+ 
++void StmtProfiler::VisitOMPMetaDirective(const OMPMetaDirective *S) {
++  VisitOMPExecutableDirective(S);
++}
++
+ void StmtProfiler::VisitOMPParallelDirective(const OMPParallelDirective *S) {
+   VisitOMPExecutableDirective(S);
+ }
+diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
+index 84579c0f0ae2..c86c0958fef4 100644
+--- a/clang/lib/Basic/OpenMPKinds.cpp
++++ b/clang/lib/Basic/OpenMPKinds.cpp
+@@ -185,6 +185,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str,
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+     break;
+   default:
+     break;
+@@ -428,6 +429,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+     break;
+   default:
+     break;
+@@ -591,6 +593,9 @@ void clang::getOpenMPCaptureRegions(
+     OpenMPDirectiveKind DKind) {
+   assert(unsigned(DKind) < llvm::omp::Directive_enumSize);
+   switch (DKind) {
++  case OMPD_metadirective:
++    CaptureRegions.push_back(OMPD_metadirective);
++    break;
+   case OMPD_parallel:
+   case OMPD_parallel_for:
+   case OMPD_parallel_for_simd:
+diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+index 6390a84219d4..63db859003f1 100644
+--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
++++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+@@ -6740,6 +6740,7 @@ const Expr *CGOpenMPRuntime::getNumTeamsExprForTargetDirective(
+   case OMPD_parallel_master_taskloop:
+   case OMPD_parallel_master_taskloop_simd:
+   case OMPD_requires:
++  case OMPD_metadirective:
+   case OMPD_unknown:
+     break;
+   default:
+@@ -7214,6 +7215,7 @@ llvm::Value *CGOpenMPRuntime::emitNumThreadsForTargetDirective(
+   case OMPD_parallel_master_taskloop:
+   case OMPD_parallel_master_taskloop_simd:
+   case OMPD_requires:
++  case OMPD_metadirective:
+   case OMPD_unknown:
+     break;
+   default:
+@@ -9851,6 +9853,7 @@ getNestedDistributeDirective(ASTContext &Ctx, const OMPExecutableDirective &D) {
+     case OMPD_parallel_master_taskloop:
+     case OMPD_parallel_master_taskloop_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+     case OMPD_unknown:
+     default:
+       llvm_unreachable("Unexpected directive.");
+@@ -10701,6 +10704,7 @@ void CGOpenMPRuntime::scanForTargetRegionsFunctions(const Stmt *S,
+     case OMPD_parallel_master_taskloop:
+     case OMPD_parallel_master_taskloop_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+     case OMPD_unknown:
+     default:
+       llvm_unreachable("Unknown target directive for OpenMP device codegen.");
+@@ -11382,6 +11386,7 @@ void CGOpenMPRuntime::emitTargetDataStandAloneCall(
+     case OMPD_target_parallel_for:
+     case OMPD_target_parallel_for_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+     case OMPD_unknown:
+     default:
+       llvm_unreachable("Unexpected standalone target data directive.");
+diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
+index 4ff1f7b3a85b..08a4a6751083 100644
+--- a/clang/lib/CodeGen/CGStmt.cpp
++++ b/clang/lib/CodeGen/CGStmt.cpp
+@@ -196,6 +196,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
+   case Stmt::SEHTryStmtClass:
+     EmitSEHTryStmt(cast<SEHTryStmt>(*S));
+     break;
++  case Stmt::OMPMetaDirectiveClass:
++    EmitOMPMetaDirective(cast<OMPMetaDirective>(*S));
++    break;
+   case Stmt::OMPCanonicalLoopClass:
+     EmitOMPCanonicalLoop(cast<OMPCanonicalLoop>(S));
+     break;
+diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp
+index b96515093a7f..fe47955f6c6f 100644
+--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
++++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
+@@ -1784,6 +1784,10 @@ void CodeGenFunction::EmitOMPParallelDirective(const OMPParallelDirective &S) {
+   checkForLastprivateConditionalUpdate(*this, S);
+ }
+ 
++void CodeGenFunction::EmitOMPMetaDirective(const OMPMetaDirective &S) {
++  EmitStmt(S.getIfStmt());
++}
++
+ namespace {
+ /// RAII to handle scopes for loop transformation directives.
+ class OMPTransformDirectiveScopeRAII {
+@@ -5862,6 +5866,7 @@ static void emitOMPAtomicExpr(CodeGenFunction &CGF, OpenMPClauseKind Kind,
+   case OMPC_novariants:
+   case OMPC_nocontext:
+   case OMPC_filter:
++  case OMPC_when:
+     llvm_unreachable("Clause is not allowed in 'omp atomic'.");
+   }
+ }
+diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
+index 1f05877ed6f9..3656e9120d70 100644
+--- a/clang/lib/CodeGen/CodeGenFunction.h
++++ b/clang/lib/CodeGen/CodeGenFunction.h
+@@ -3442,6 +3442,7 @@ public:
+                                        const RegionCodeGenTy &BodyGen,
+                                        OMPTargetDataInfo &InputInfo);
+ 
++  void EmitOMPMetaDirective(const OMPMetaDirective &S);
+   void EmitOMPParallelDirective(const OMPParallelDirective &S);
+   void EmitOMPSimdDirective(const OMPSimdDirective &S);
+   void EmitOMPTileDirective(const OMPTileDirective &S);
+diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
+index a45168d4c1b1..159a39bdf46d 100644
+--- a/clang/lib/Parse/ParseOpenMP.cpp
++++ b/clang/lib/Parse/ParseOpenMP.cpp
+@@ -2222,6 +2222,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
+   case OMPD_target_teams_distribute_simd:
+   case OMPD_dispatch:
+   case OMPD_masked:
++  case OMPD_metadirective:
+     Diag(Tok, diag::err_omp_unexpected_directive)
+         << 1 << getOpenMPDirectiveName(DKind);
+     break;
+@@ -2276,8 +2277,10 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
+ ///
+ StmtResult
+ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) {
+-  assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
+-         "Not an OpenMP directive!");
++  static bool ReadDirectiveWithinMetadirective = false;
++  if (!ReadDirectiveWithinMetadirective)
++    assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
++           "Not an OpenMP directive!");
+   ParsingOpenMPDirectiveRAII DirScope(*this);
+   ParenBraceBracketBalancer BalancerRAIIObj(*this);
+   SmallVector<OMPClause *, 5> Clauses;
+@@ -2286,8 +2289,14 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) {
+       FirstClauses(llvm::omp::Clause_enumSize + 1);
+   unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope |
+                         Scope::CompoundStmtScope | Scope::OpenMPDirectiveScope;
+-  SourceLocation Loc = ConsumeAnnotationToken(), EndLoc;
++  if (!ReadDirectiveWithinMetadirective)
++    ConsumeAnnotationToken();
++  SourceLocation Loc = Tok.getLocation(), EndLoc;
+   OpenMPDirectiveKind DKind = parseOpenMPDirectiveKind(*this);
++  if (ReadDirectiveWithinMetadirective && DKind == OMPD_unknown) {
++    Diag(Tok, diag::err_omp_unknown_directive);
++    return StmtError();
++  }
+   OpenMPDirectiveKind CancelRegion = OMPD_unknown;
+   // Name of critical directive.
+   DeclarationNameInfo DirName;
+@@ -2295,6 +2304,154 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) {
+   bool HasAssociatedStatement = true;
+ 
+   switch (DKind) {
++  case OMPD_metadirective: {
++    ConsumeToken();
++    SmallVector<VariantMatchInfo, 4> VMIs;
++
++    // First iteration of parsing all clauses of metadirective.
++    // This iteration only parses and collects all context selector ignoring the
++    // associated directives.
++    TentativeParsingAction TPA(*this);
++    ASTContext &ASTContext = Actions.getASTContext();
++
++    BalancedDelimiterTracker T(*this, tok::l_paren,
++                               tok::annot_pragma_openmp_end);
++    while (Tok.isNot(tok::annot_pragma_openmp_end)) {
++      OpenMPClauseKind CKind = Tok.isAnnotation()
++                                   ? OMPC_unknown
++                                   : getOpenMPClauseKind(PP.getSpelling(Tok));
++      SourceLocation Loc = ConsumeToken();
++
++      // Parse '('.
++      if (T.expectAndConsume(diag::err_expected_lparen_after,
++                             getOpenMPClauseName(CKind).data()))
++        return Directive;
++
++      OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
++      if (CKind == OMPC_when) {
++        // parse and get OMPTraitInfo to pass to the When clause
++        parseOMPContextSelectors(Loc, TI);
++        for (OMPTraitSet T : TI.Sets) {
++          llvm::errs() << "TraitSet: " << llvm::omp::getOpenMPContextTraitSetName(T.Kind) << "\n";
++          for (OMPTraitSelector S: T.Selectors) {
++              llvm::errs() << "TraitSelector: " << llvm::omp::getOpenMPContextTraitSelectorName(S.Kind) << "\n";
++              for (OMPTraitProperty P : S.Properties) {
++                  StringRef Raw;
++                  llvm::errs() << "TraitProperty: " << llvm::omp::getOpenMPContextTraitPropertyName(P.Kind, Raw) << "\n";
++              }
++          }
++        }
++        if (TI.Sets.size() == 0) {
++          Diag(Tok, diag::err_omp_expected_context_selector) << "when clause";
++          TPA.Commit();
++          return Directive;
++        }
++
++        // Parse ':'
++        if (Tok.is(tok::colon))
++          ConsumeAnyToken();
++        else {
++          Diag(Tok, diag::err_omp_expected_colon) << "when clause";
++          TPA.Commit();
++          return Directive;
++        }
++      }
++      // Skip Directive for now. We will parse directive in the second iteration
++      int paren = 0;
++      while (Tok.isNot(tok::r_paren) || paren != 0) {
++        if (Tok.is(tok::l_paren))
++          paren++;
++        if (Tok.is(tok::r_paren))
++          paren--;
++        if (Tok.is(tok::annot_pragma_openmp_end)) {
++          Diag(Tok, diag::err_omp_expected_punc)
++              << getOpenMPClauseName(CKind) << 0;
++          TPA.Commit();
++          return Directive;
++        } 
++        ConsumeAnyToken();
++      }
++      // Parse ')'
++      if (Tok.is(tok::r_paren))
++        T.consumeClose();
++
++      VariantMatchInfo VMI;
++      TI.getAsVariantMatchInfo(ASTContext, VMI);
++
++      VMIs.push_back(VMI);
++    }
++
++    TPA.Revert();
++    // End of the first iteration. Parser is reset to the start of metadirective
++
++    TargetOMPContext OMPCtx(ASTContext, nullptr, nullptr);
++
++    // A single match is returned for OpenMP 5.0
++    int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx);
++
++    int idx = 0;
++    // In OpenMP 5.0 metadirective is either replaced by another directive or
++    // ignored.
++    // TODO: In OpenMP 5.1 generate multiple directives based upon the matches
++    // found by getBestWhenMatchForContext.
++    while (Tok.isNot(tok::annot_pragma_openmp_end)) {
++      // OpenMP 5.0 implementation - Skip to the best index found.
++      if (idx++ != BestIdx) {
++        ConsumeToken();  // Consume clause name
++        T.consumeOpen(); // Consume '('
++        int paren = 0;
++        // Skip everything inside the clause
++        while (Tok.isNot(tok::r_paren) || paren != 0) {
++          if (Tok.is(tok::l_paren))
++            paren++;
++          if (Tok.is(tok::r_paren))
++            paren--;
++          ConsumeAnyToken();
++        }
++        // Parse ')'
++        if (Tok.is(tok::r_paren))
++          T.consumeClose();
++        continue;
++      }
++
++      OpenMPClauseKind CKind = Tok.isAnnotation()
++                                   ? OMPC_unknown
++                                   : getOpenMPClauseKind(PP.getSpelling(Tok));
++      SourceLocation Loc = ConsumeToken();
++
++      // Parse '('.
++      T.consumeOpen();
++
++      // Skip ContextSelectors for when clause
++      if (CKind == OMPC_when) {
++        OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
++        // parse and skip the ContextSelectors
++        parseOMPContextSelectors(Loc, TI);
++        for (auto T : TI.Sets) {
++          llvm::errs() << "[2429] T.Kind: "
++                       << llvm::omp::getOpenMPContextTraitSetName(T.Kind)
++                       << "\n";
++        }
++
++        // Parse ':'
++        ConsumeAnyToken();
++      }
++
++      // If no directive is passed, skip in OpenMP 5.0.
++      // TODO: Generate nothing directive from OpenMP 5.1.
++      if (Tok.is(tok::r_paren)) {
++        SkipUntil(tok::annot_pragma_openmp_end);
++        break;
++      }
++
++      // Parse Directive
++      ReadDirectiveWithinMetadirective = true;
++      Directive = ParseOpenMPDeclarativeOrExecutableDirective(StmtCtx);
++      ReadDirectiveWithinMetadirective = false;
++      break;
++    }
++    break;
++  }
+   case OMPD_threadprivate: {
+     // FIXME: Should this be permitted in C++?
+     if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
+@@ -2486,6 +2643,13 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) {
+     Actions.StartOpenMPDSABlock(DKind, DirName, Actions.getCurScope(), Loc);
+ 
+     while (Tok.isNot(tok::annot_pragma_openmp_end)) {
++      // If we are parsing for a directive within a metadirective, the directive
++      // ends with a ')'.
++      if (ReadDirectiveWithinMetadirective && Tok.is(tok::r_paren)) {
++        while (Tok.isNot(tok::annot_pragma_openmp_end))
++          ConsumeAnyToken();
++        break;
++      }
+       bool HasImplicitClause = false;
+       if (ImplicitClauseAllowed && Tok.is(tok::l_paren)) {
+         HasImplicitClause = true;
+@@ -2601,7 +2765,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) {
+     SkipUntil(tok::annot_pragma_openmp_end);
+     break;
+   }
+-  return Directive;
++    return Directive;
+ }
+ 
+ // Parses simple list:
+diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
+index 0d40b47b24da..f32bb0d298ba 100644
+--- a/clang/lib/Sema/SemaExceptionSpec.cpp
++++ b/clang/lib/Sema/SemaExceptionSpec.cpp
+@@ -1496,6 +1496,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
+   case Stmt::OMPInteropDirectiveClass:
+   case Stmt::OMPDispatchDirectiveClass:
+   case Stmt::OMPMaskedDirectiveClass:
++  case Stmt::OMPMetaDirectiveClass:
+   case Stmt::ReturnStmtClass:
+   case Stmt::SEHExceptStmtClass:
+   case Stmt::SEHFinallyStmtClass:
+diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
+index b6e43d32dc9b..44dec3a83045 100644
+--- a/clang/lib/Sema/SemaOpenMP.cpp
++++ b/clang/lib/Sema/SemaOpenMP.cpp
+@@ -4279,6 +4279,7 @@ void Sema::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope) {
+   case OMPD_declare_variant:
+   case OMPD_begin_declare_variant:
+   case OMPD_end_declare_variant:
++  case OMPD_metadirective:
+     llvm_unreachable("OpenMP Directive is not allowed");
+   case OMPD_unknown:
+   default:
+@@ -6311,6 +6312,7 @@ StmtResult Sema::ActOnOpenMPExecutableDirective(
+       case OMPC_atomic_default_mem_order:
+       case OMPC_device_type:
+       case OMPC_match:
++      case OMPC_when:
+       default:
+         llvm_unreachable("Unexpected clause");
+       }
+@@ -13248,6 +13250,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr,
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+   default:
+     llvm_unreachable("Clause is not allowed.");
+   }
+@@ -13404,6 +13407,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_atomic:
+     case OMPD_teams_distribute:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with if-clause");
+     case OMPD_unknown:
+     default:
+@@ -13486,6 +13490,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_teams_distribute:
+     case OMPD_teams_distribute_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with num_threads-clause");
+     case OMPD_unknown:
+     default:
+@@ -13566,6 +13571,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_atomic:
+     case OMPD_distribute_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with num_teams-clause");
+     case OMPD_unknown:
+     default:
+@@ -13646,6 +13652,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_atomic:
+     case OMPD_distribute_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with thread_limit-clause");
+     case OMPD_unknown:
+     default:
+@@ -13726,6 +13733,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_distribute_simd:
+     case OMPD_target_teams:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with schedule clause");
+     case OMPD_unknown:
+     default:
+@@ -13806,6 +13814,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_atomic:
+     case OMPD_target_teams:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with dist_schedule clause");
+     case OMPD_unknown:
+     default:
+@@ -13888,6 +13897,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_atomic:
+     case OMPD_distribute_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with device-clause");
+     case OMPD_unknown:
+     default:
+@@ -13970,6 +13980,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+     case OMPD_atomic:
+     case OMPD_distribute_simd:
+     case OMPD_requires:
++    case OMPD_metadirective:
+       llvm_unreachable("Unexpected OpenMP directive with grainsize-clause");
+     case OMPD_unknown:
+     default:
+@@ -13989,6 +14000,15 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause(
+   case OMPC_filter:
+     // Do not capture filter-clause expressions.
+     break;
++  case OMPC_when:
++    if (DKind == OMPD_metadirective) {
++      CaptureRegion = OMPD_metadirective;
++    } else if (DKind == OMPD_unknown) {
++      llvm_unreachable("Unknown OpenMP directive");
++    } else {
++      llvm_unreachable("Unexpected OpenMP directive with when clause");
++    }
++    break;
+   case OMPC_firstprivate:
+   case OMPC_lastprivate:
+   case OMPC_reduction:
+@@ -14516,6 +14536,7 @@ OMPClause *Sema::ActOnOpenMPSimpleClause(
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+   default:
+     llvm_unreachable("Clause is not allowed.");
+   }
+@@ -14808,6 +14829,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprWithArgClause(
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+   default:
+     llvm_unreachable("Clause is not allowed.");
+   }
+@@ -15057,6 +15079,7 @@ OMPClause *Sema::ActOnOpenMPClause(OpenMPClauseKind Kind,
+   case OMPC_exclusive:
+   case OMPC_uses_allocators:
+   case OMPC_affinity:
++  case OMPC_when:
+   default:
+     llvm_unreachable("Clause is not allowed.");
+   }
+@@ -15599,6 +15622,7 @@ OMPClause *Sema::ActOnOpenMPVarListClause(
+   case OMPC_nocontext:
+   case OMPC_detach:
+   case OMPC_uses_allocators:
++  case OMPC_when:
+   default:
+     llvm_unreachable("Clause is not allowed.");
+   }
+diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
+index 8841b2e00d40..c54bff3d843f 100644
+--- a/clang/lib/Sema/TreeTransform.h
++++ b/clang/lib/Sema/TreeTransform.h
+@@ -8504,6 +8504,15 @@ StmtResult TreeTransform<Derived>::TransformOMPExecutableDirective(
+       AssociatedStmt.get(), D->getBeginLoc(), D->getEndLoc());
+ }
+ 
++template <typename Derived>
++StmtResult
++TreeTransform<Derived>::TransformOMPMetaDirective(OMPMetaDirective *D) {
++  // TODO: Fix This
++  SemaRef.Diag(D->getBeginLoc(), diag::err_omp_instantiation_not_supported)
++      << getOpenMPDirectiveName(D->getDirectiveKind());
++  return StmtError();
++}
++
+ template <typename Derived>
+ StmtResult
+ TreeTransform<Derived>::TransformOMPParallelDirective(OMPParallelDirective *D) {
+diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
+index b100f946f558..ecdae0d17766 100644
+--- a/clang/lib/Serialization/ASTReaderStmt.cpp
++++ b/clang/lib/Serialization/ASTReaderStmt.cpp
+@@ -2307,6 +2307,13 @@ void ASTStmtReader::VisitOMPLoopDirective(OMPLoopDirective *D) {
+   VisitOMPLoopBasedDirective(D);
+ }
+ 
++void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) {
++  VisitStmt(D);
++  // The NumClauses field was read in ReadStmtFromStream.
++  Record.skipInts(1);
++  VisitOMPExecutableDirective(D);
++}
++
+ void ASTStmtReader::VisitOMPParallelDirective(OMPParallelDirective *D) {
+   VisitStmt(D);
+   VisitOMPExecutableDirective(D);
+@@ -3183,6 +3190,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
+       S = OMPCanonicalLoop::createEmpty(Context);
+       break;
+ 
++    case STMT_OMP_META_DIRECTIVE:
++      S = OMPMetaDirective::CreateEmpty(
++          Context, Record[ASTStmtReader::NumStmtFields], Empty);
++      break;
++
+     case STMT_OMP_PARALLEL_DIRECTIVE:
+       S =
+         OMPParallelDirective::CreateEmpty(Context,
+diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
+index 2bb5e4f3563d..9ee4d0cafe45 100644
+--- a/clang/lib/Serialization/ASTWriterStmt.cpp
++++ b/clang/lib/Serialization/ASTWriterStmt.cpp
+@@ -2205,6 +2205,13 @@ void ASTStmtWriter::VisitOMPLoopDirective(OMPLoopDirective *D) {
+   VisitOMPLoopBasedDirective(D);
+ }
+ 
++void ASTStmtWriter::VisitOMPMetaDirective(OMPMetaDirective *D) {
++  VisitStmt(D);
++  Record.push_back(D->getNumClauses());
++  VisitOMPExecutableDirective(D);
++  Code = serialization::STMT_OMP_META_DIRECTIVE;
++}
++
+ void ASTStmtWriter::VisitOMPParallelDirective(OMPParallelDirective *D) {
+   VisitStmt(D);
+   VisitOMPExecutableDirective(D);
+diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+index 66332d3b848c..cfaace45ec36 100644
+--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
++++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+@@ -1299,6 +1299,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
+     case Stmt::OMPMaskedDirectiveClass:
+     case Stmt::CapturedStmtClass:
+     case Stmt::OMPUnrollDirectiveClass: {
++    case Stmt::OMPMetaDirectiveClass:
+       const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
+       Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+       break;
+diff --git a/clang/test/OpenMP/metadirective_ast_print.c b/clang/test/OpenMP/metadirective_ast_print.c
+new file mode 100644
+index 000000000000..91a33181d1b2
+--- /dev/null
++++ b/clang/test/OpenMP/metadirective_ast_print.c
+@@ -0,0 +1,66 @@
++// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -ast-print %s -o - | FileCheck %s
++// expected-no-diagnostics
++
++void bar(void);
++
++#define N 10
++void foo(void) {
++#pragma omp metadirective when(device = {kind(cpu)} \
++                               : parallel) default()
++  bar();
++#pragma omp metadirective when(implementation = {vendor(score(0)  \
++                                                        : llvm)}, \
++                               device = {kind(cpu)}               \
++                               : parallel) default(target teams)
++  bar();
++#pragma omp metadirective when(device = {kind(gpu)}                                 \
++                               : target teams) when(implementation = {vendor(llvm)} \
++                                                    : parallel) default()
++  bar();
++#pragma omp metadirective default(target) when(implementation = {vendor(score(5)  \
++                                                                        : llvm)}, \
++                                               device = {kind(cpu, host)}         \
++                                               : parallel)
++  bar();
++#pragma omp metadirective when(user = {condition(N > 10)}                 \
++                               : target) when(user = {condition(N == 10)} \
++                                              : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(host)} \
++                               : parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(implementation = {extension(match_all)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(implementation = {extension(match_any)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(implementation = {extension(match_none)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++}
++
++// CHECK: void bar();
++// CHECK: void foo()
++// CHECK-NEXT: #pragma omp parallel
++// CHECK-NEXT: bar()
++// CHECK-NEXT: #pragma omp parallel
++// CHECK-NEXT: bar()
++// CHECK-NEXT: #pragma omp parallel
++// CHECK-NEXT: bar()
++// CHECK-NEXT: #pragma omp parallel
++// CHECK-NEXT: bar()
++// CHECK-NEXT: #pragma omp parallel
++// CHECK-NEXT: bar()
++// CHECK-NEXT: #pragma omp parallel for
++// CHECK-NEXT: for (int i = 0; i < 100; i++)
++// CHECK: #pragma omp parallel
++// CHECK-NEXT: for (int i = 0; i < 100; i++)
++// CHECK: #pragma omp parallel for
++// CHECK-NEXT: for (int i = 0; i < 100; i++)
++// CHECK: #pragma omp parallel
++// CHECK-NEXT: for (int i = 0; i < 100; i++)
+diff --git a/clang/test/OpenMP/metadirective_device_kind_codegen.c b/clang/test/OpenMP/metadirective_device_kind_codegen.c
+new file mode 100644
+index 000000000000..6bcd3ec6f6c4
+--- /dev/null
++++ b/clang/test/OpenMP/metadirective_device_kind_codegen.c
+@@ -0,0 +1,72 @@
++// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c -triple aarch64-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// expected-no-diagnostics
++
++void bar();
++
++void foo() {
++#pragma omp metadirective when(device = {kind(any)} \
++                               : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(host, cpu)} \
++                               : parallel for num_threads(4))
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(device = {kind(host)} \
++                               : parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(device = {kind(nohost, gpu)} \
++                               :) when(device = {kind(cpu)} \
++                                       : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(any, cpu)} \
++                               : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(any, host)} \
++                               : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(gpu)} \
++                               : target parallel for) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++}
++
++// CHECK: void @foo()
++// CHECK: ...) @__kmpc_fork_call(
++// CHECK-NEXT: @__kmpc_push_num_threads
++// CHECK-COUNT-6: ...) @__kmpc_fork_call(
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined.(
++// CHECK: @bar
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..1(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..2(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..3(
++// CHECK: @bar
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..4(
++// CHECK: @bar
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..5(
++// CHECK: @bar
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..6(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
+diff --git a/clang/test/OpenMP/metadirective_device_kind_codegen.cpp b/clang/test/OpenMP/metadirective_device_kind_codegen.cpp
+new file mode 100644
+index 000000000000..b51ae8773094
+--- /dev/null
++++ b/clang/test/OpenMP/metadirective_device_kind_codegen.cpp
+@@ -0,0 +1,73 @@
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux -emit-llvm %s -fexceptions -fcxx-exceptions -o - -fsanitize-address-use-after-scope | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple aarch64-unknown-linux -emit-llvm %s -fexceptions -fcxx-exceptions -o - -fsanitize-address-use-after-scope | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple ppc64le-unknown-linux -emit-llvm %s -fexceptions -fcxx-exceptions -o - -fsanitize-address-use-after-scope | FileCheck %s
++
++// expected-no-diagnostics
++
++void bar();
++
++void foo() {
++#pragma omp metadirective when(device = {kind(any)} \
++                               : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(host, cpu)} \
++                               : parallel for num_threads(4))
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(device = {kind(host)} \
++                               : parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(device = {kind(nohost, gpu)} \
++                               :) when(device = {kind(cpu)} \
++                                       : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(any, cpu)} \
++                               : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(any, host)} \
++                               : parallel)
++  bar();
++#pragma omp metadirective when(device = {kind(gpu)} \
++                               : target parallel for) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++}
++
++// CHECK: void @_Z3foov()
++// CHECK: ...) @__kmpc_fork_call(
++// CHECK-NEXT: @__kmpc_push_num_threads
++// CHECK-COUNT-6: ...) @__kmpc_fork_call(
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined.(
++// CHECK: void @_Z3barv()
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..1(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..2(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..3(
++// CHECK: void @_Z3barv()
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..4(
++// CHECK: void @_Z3barv()
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..5(
++// CHECK: void @_Z3barv()
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..6(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
+diff --git a/clang/test/OpenMP/metadirective_empty.cpp b/clang/test/OpenMP/metadirective_empty.cpp
+new file mode 100644
+index 000000000000..095929157b34
+--- /dev/null
++++ b/clang/test/OpenMP/metadirective_empty.cpp
+@@ -0,0 +1,31 @@
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s -o - | FileCheck %s
++// expected-no-diagnostics
++#define N 1000
++void func() {
++  // Test where a valid when clause contains empty directive.
++  // The directive will be ignored and code for a serial for loop will be generated.
++#pragma omp metadirective when(implementation = {vendor(llvm)} \
++                               :) default(parallel for)
++  for (int i = 0; i < N; i++)
++    ;
++}
++
++// CHECK: void @_Z4funcv() #0 {
++// CHECK: entry:
++// CHECK:   %i = alloca i32, align 4
++// CHECK:   store i32 0, i32* %i, align 4
++// CHECK:   br label %for.cond
++// CHECK: for.cond:
++// CHECK:   %0 = load i32, i32* %i, align 4
++// CHECK:   %cmp = icmp slt i32 %0, 1000
++// CHECK:   br i1 %cmp, label %for.body, label %for.end
++// CHECK: for.body:
++// CHECK:   br label %for.inc
++// CHECK: for.inc:
++// CHECK:   %1 = load i32, i32* %i, align 4
++// CHECK:   %inc = add nsw i32 %1, 1
++// CHECK:   store i32 %inc, i32* %i, align 4
++// CHECK:   br label %for.cond, !llvm.loop
++// CHECK: for.end:
++// CHECK:   ret void
++// CHECK: }
+diff --git a/clang/test/OpenMP/metadirective_implementation_codegen.c b/clang/test/OpenMP/metadirective_implementation_codegen.c
+new file mode 100644
+index 000000000000..409e4ab48c52
+--- /dev/null
++++ b/clang/test/OpenMP/metadirective_implementation_codegen.c
+@@ -0,0 +1,67 @@
++// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c -triple aarch64-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// expected-no-diagnostics
++
++void bar();
++
++void foo() {
++#pragma omp metadirective when(implementation = {vendor(score(0)  \
++                                                        : llvm)}, \
++                               device = {kind(cpu)}               \
++                               : parallel) default(target teams)
++  bar();
++#pragma omp metadirective when(device = {kind(gpu)}                                 \
++                               : target teams) when(implementation = {vendor(llvm)} \
++                                                    : parallel) default()
++  bar();
++#pragma omp metadirective default(target) when(implementation = {vendor(score(5)  \
++                                                                        : llvm)}, \
++                                               device = {kind(cpu, host)}         \
++                                               : parallel)
++  bar();
++#pragma omp metadirective when(implementation = {extension(match_all)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(implementation = {extension(match_any)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(implementation = {extension(match_none)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++}
++
++// CHECK: void @foo()
++// CHECK-COUNT-6: ...) @__kmpc_fork_call(
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined.(
++// CHECK: @bar
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..1(
++// CHECK: @bar
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..2(
++// CHECK: @bar
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..3(
++// NO-CHECK: call void @__kmpc_for_static_init
++// NO-CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..4(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..5(
++// NO-CHECK: call void @__kmpc_for_static_init
++// NO-CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
+diff --git a/clang/test/OpenMP/metadirective_implementation_codegen.cpp b/clang/test/OpenMP/metadirective_implementation_codegen.cpp
+new file mode 100644
+index 000000000000..30b34b8d489a
+--- /dev/null
++++ b/clang/test/OpenMP/metadirective_implementation_codegen.cpp
+@@ -0,0 +1,67 @@
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple aarch64-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s
++// expected-no-diagnostics
++
++void bar();
++
++void foo() {
++#pragma omp metadirective when(implementation = {vendor(score(0)  \
++                                                        : llvm)}, \
++                               device = {kind(cpu)}               \
++                               : parallel) default(target teams)
++  bar();
++#pragma omp metadirective when(device = {kind(gpu)}                                 \
++                               : target teams) when(implementation = {vendor(llvm)} \
++                                                    : parallel) default()
++  bar();
++#pragma omp metadirective default(target) when(implementation = {vendor(score(5)  \
++                                                                        : llvm)}, \
++                                               device = {kind(cpu, host)}         \
++                                               : parallel)
++  bar();
++#pragma omp metadirective when(implementation = {extension(match_all)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(implementation = {extension(match_any)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++#pragma omp metadirective when(implementation = {extension(match_none)} \
++                               : parallel) default(parallel for)
++  for (int i = 0; i < 100; i++)
++    ;
++}
++
++// CHECK: void @_Z3foov()
++// CHECK-COUNT-6: ...) @__kmpc_fork_call(
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined.(
++// CHECK: @_Z3barv
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..1(
++// CHECK: @_Z3barv
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..2(
++// CHECK: @_Z3barv
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..3(
++// NO-CHECK: call void @__kmpc_for_static_init
++// NO-CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..4(
++// CHECK: call void @__kmpc_for_static_init
++// CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
++
++// CHECK: define internal void @.omp_outlined..5(
++// NO-CHECK: call void @__kmpc_for_static_init
++// NO-CHECK: call void @__kmpc_for_static_fini
++// CHECK: ret void
+diff --git a/clang/test/OpenMP/metadirective_messages.cpp b/clang/test/OpenMP/metadirective_messages.cpp
+new file mode 100644
+index 000000000000..daedddd83e3c
+--- /dev/null
++++ b/clang/test/OpenMP/metadirective_messages.cpp
+@@ -0,0 +1,18 @@
++// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s
++
++void foo() {
++#pragma omp metadirective // expected-error {{expected expression}}
++  ;
++#pragma omp metadirective when() // expected-error {{expected valid context selector in when clause}} expected-error {{expected expression}} expected-warning {{expected identifier or string literal describing a context set; set skipped}} expected-note {{context set options are: 'construct' 'device' 'implementation' 'user'}} expected-note {{the ignored set spans until here}}
++  ;
++#pragma omp metadirective when(device{}) // expected-warning {{expected '=' after the context set name "device"; '=' assumed}} expected-warning {{expected identifier or string literal describing a context selector; selector skipped}} expected-note {{context selector options are: 'kind' 'arch' 'isa'}} expected-note {{the ignored selector spans until here}} expected-error {{expected valid context selector in when clause}} expected-error {{expected expression}} 
++  ;
++#pragma omp metadirective when(device{arch(nvptx)}) // expected-error {{missing ':' in when clause}} expected-error {{expected expression}} expected-warning {{expected '=' after the context set name "device"; '=' assumed}}
++  ;
++#pragma omp metadirective when(device{arch(nvptx)}: ) default() // expected-warning {{expected '=' after the context set name "device"; '=' assumed}}
++  ;
++#pragma omp metadirective when(device = {arch(nvptx)} : ) default(xyz) // expected-error {{expected an OpenMP directive}} expected-error {{use of undeclared identifier 'xyz'}}
++  ;
++#pragma omp metadirective when(device = {arch(nvptx)} : parallel default() // expected-error {{expected ',' or ')' in 'when' clause}} expected-error {{expected expression}}
++  ;
++}
+diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
+index 7b93164ccaa2..96a7cb4f3469 100644
+--- a/clang/tools/libclang/CIndex.cpp
++++ b/clang/tools/libclang/CIndex.cpp
+@@ -5582,6 +5582,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
+     return cxstring::createRef("ModuleImport");
+   case CXCursor_OMPCanonicalLoop:
+     return cxstring::createRef("OMPCanonicalLoop");
++  case CXCursor_OMPMetaDirective:
++    return cxstring::createRef("OMPMetaDirective");
+   case CXCursor_OMPParallelDirective:
+     return cxstring::createRef("OMPParallelDirective");
+   case CXCursor_OMPSimdDirective:
+diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
+index 6fb47300efb8..8d214480e9b8 100644
+--- a/clang/tools/libclang/CXCursor.cpp
++++ b/clang/tools/libclang/CXCursor.cpp
+@@ -643,6 +643,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
+   case Stmt::OMPCanonicalLoopClass:
+     K = CXCursor_OMPCanonicalLoop;
+     break;
++  case Stmt::OMPMetaDirectiveClass:
++    K = CXCursor_OMPMetaDirective;
++    break;
+   case Stmt::OMPParallelDirectiveClass:
+     K = CXCursor_OMPParallelDirective;
+     break;
+diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
+index d5a62107bbb8..520baca4634d 100644
+--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
++++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
+@@ -337,6 +337,7 @@ def OMPC_Filter : Clause<"filter"> {
+   let clangClass = "OMPFilterClause";
+   let flangClass = "ScalarIntExpr";
+ }
++def OMPC_When: Clause<"when"> {}
+ 
+ //===----------------------------------------------------------------------===//
+ // Definition of OpenMP directives
+@@ -1699,6 +1700,10 @@ def OMP_masked : Directive<"masked"> {
+     VersionedClause<OMPC_Filter>
+   ];
+ }
++def OMP_Metadirective : Directive<"metadirective"> {
++  let allowedClauses = [VersionedClause<OMPC_When>];
++  let allowedOnceClauses = [VersionedClause<OMPC_Default>];
++}
+ def OMP_Unknown : Directive<"unknown"> {
+   let isDefault = true;
+ }
Index: llvm/include/llvm/Frontend/OpenMP/OMP.td
===================================================================
--- llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -337,6 +337,7 @@
   let clangClass = "OMPFilterClause";
   let flangClass = "ScalarIntExpr";
 }
+def OMPC_When: Clause<"when"> {}
 
 //===----------------------------------------------------------------------===//
 // Definition of OpenMP directives
@@ -1699,6 +1700,10 @@
     VersionedClause<OMPC_Filter>
   ];
 }
+def OMP_Metadirective : Directive<"metadirective"> {
+  let allowedClauses = [VersionedClause<OMPC_When>];
+  let allowedOnceClauses = [VersionedClause<OMPC_Default>];
+}
 def OMP_Unknown : Directive<"unknown"> {
   let isDefault = true;
 }
Index: clang/tools/libclang/CXCursor.cpp
===================================================================
--- clang/tools/libclang/CXCursor.cpp
+++ clang/tools/libclang/CXCursor.cpp
@@ -643,6 +643,9 @@
   case Stmt::OMPCanonicalLoopClass:
     K = CXCursor_OMPCanonicalLoop;
     break;
+  case Stmt::OMPMetaDirectiveClass:
+    K = CXCursor_OMPMetaDirective;
+    break;
   case Stmt::OMPParallelDirectiveClass:
     K = CXCursor_OMPParallelDirective;
     break;
Index: clang/tools/libclang/CIndex.cpp
===================================================================
--- clang/tools/libclang/CIndex.cpp
+++ clang/tools/libclang/CIndex.cpp
@@ -5582,6 +5582,8 @@
     return cxstring::createRef("ModuleImport");
   case CXCursor_OMPCanonicalLoop:
     return cxstring::createRef("OMPCanonicalLoop");
+  case CXCursor_OMPMetaDirective:
+    return cxstring::createRef("OMPMetaDirective");
   case CXCursor_OMPParallelDirective:
     return cxstring::createRef("OMPParallelDirective");
   case CXCursor_OMPSimdDirective:
Index: clang/test/OpenMP/metadirective_messages.cpp
===================================================================
--- /dev/null
+++ clang/test/OpenMP/metadirective_messages.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s
+
+void foo() {
+#pragma omp metadirective // expected-error {{expected expression}}
+  ;
+#pragma omp metadirective when() // expected-error {{expected valid context selector in when clause}} expected-error {{expected expression}} expected-warning {{expected identifier or string literal describing a context set; set skipped}} expected-note {{context set options are: 'construct' 'device' 'implementation' 'user'}} expected-note {{the ignored set spans until here}}
+  ;
+#pragma omp metadirective when(device{}) // expected-warning {{expected '=' after the context set name "device"; '=' assumed}} expected-warning {{expected identifier or string literal describing a context selector; selector skipped}} expected-note {{context selector options are: 'kind' 'arch' 'isa'}} expected-note {{the ignored selector spans until here}} expected-error {{expected valid context selector in when clause}} expected-error {{expected expression}} 
+  ;
+#pragma omp metadirective when(device{arch(nvptx)}) // expected-error {{missing ':' in when clause}} expected-error {{expected expression}} expected-warning {{expected '=' after the context set name "device"; '=' assumed}}
+  ;
+#pragma omp metadirective when(device{arch(nvptx)}: ) default() // expected-warning {{expected '=' after the context set name "device"; '=' assumed}}
+  ;
+#pragma omp metadirective when(device = {arch(nvptx)} : ) default(xyz) // expected-error {{expected an OpenMP directive}} expected-error {{use of undeclared identifier 'xyz'}}
+  ;
+#pragma omp metadirective when(device = {arch(nvptx)} : parallel default() // expected-error {{expected ',' or ')' in 'when' clause}} expected-error {{expected expression}}
+  ;
+}
Index: clang/test/OpenMP/metadirective_implementation_codegen.cpp
===================================================================
--- /dev/null
+++ clang/test/OpenMP/metadirective_implementation_codegen.cpp
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple aarch64-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+void bar();
+
+void foo() {
+#pragma omp metadirective when(implementation = {vendor(score(0)  \
+                                                        : llvm)}, \
+                               device = {kind(cpu)}               \
+                               : parallel) default(target teams)
+  bar();
+#pragma omp metadirective when(device = {kind(gpu)}                                 \
+                               : target teams) when(implementation = {vendor(llvm)} \
+                                                    : parallel) default()
+  bar();
+#pragma omp metadirective default(target) when(implementation = {vendor(score(5)  \
+                                                                        : llvm)}, \
+                                               device = {kind(cpu, host)}         \
+                                               : parallel)
+  bar();
+#pragma omp metadirective when(implementation = {extension(match_all)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(implementation = {extension(match_any)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(implementation = {extension(match_none)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+}
+
+// CHECK: void @_Z3foov()
+// CHECK-COUNT-6: ...) @__kmpc_fork_call(
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined.(
+// CHECK: @_Z3barv
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..1(
+// CHECK: @_Z3barv
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..2(
+// CHECK: @_Z3barv
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..3(
+// NO-CHECK: call void @__kmpc_for_static_init
+// NO-CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..4(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..5(
+// NO-CHECK: call void @__kmpc_for_static_init
+// NO-CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
Index: clang/test/OpenMP/metadirective_implementation_codegen.c
===================================================================
--- /dev/null
+++ clang/test/OpenMP/metadirective_implementation_codegen.c
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c -triple aarch64-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+void bar();
+
+void foo() {
+#pragma omp metadirective when(implementation = {vendor(score(0)  \
+                                                        : llvm)}, \
+                               device = {kind(cpu)}               \
+                               : parallel) default(target teams)
+  bar();
+#pragma omp metadirective when(device = {kind(gpu)}                                 \
+                               : target teams) when(implementation = {vendor(llvm)} \
+                                                    : parallel) default()
+  bar();
+#pragma omp metadirective default(target) when(implementation = {vendor(score(5)  \
+                                                                        : llvm)}, \
+                                               device = {kind(cpu, host)}         \
+                                               : parallel)
+  bar();
+#pragma omp metadirective when(implementation = {extension(match_all)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(implementation = {extension(match_any)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(implementation = {extension(match_none)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+}
+
+// CHECK: void @foo()
+// CHECK-COUNT-6: ...) @__kmpc_fork_call(
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined.(
+// CHECK: @bar
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..1(
+// CHECK: @bar
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..2(
+// CHECK: @bar
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..3(
+// NO-CHECK: call void @__kmpc_for_static_init
+// NO-CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..4(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..5(
+// NO-CHECK: call void @__kmpc_for_static_init
+// NO-CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
Index: clang/test/OpenMP/metadirective_empty.cpp
===================================================================
--- /dev/null
+++ clang/test/OpenMP/metadirective_empty.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+#define N 1000
+void func() {
+  // Test where a valid when clause contains empty directive.
+  // The directive will be ignored and code for a serial for loop will be generated.
+#pragma omp metadirective when(implementation = {vendor(llvm)} \
+                               :) default(parallel for)
+  for (int i = 0; i < N; i++)
+    ;
+}
+
+// CHECK: void @_Z4funcv() #0 {
+// CHECK: entry:
+// CHECK:   %i = alloca i32, align 4
+// CHECK:   store i32 0, i32* %i, align 4
+// CHECK:   br label %for.cond
+// CHECK: for.cond:
+// CHECK:   %0 = load i32, i32* %i, align 4
+// CHECK:   %cmp = icmp slt i32 %0, 1000
+// CHECK:   br i1 %cmp, label %for.body, label %for.end
+// CHECK: for.body:
+// CHECK:   br label %for.inc
+// CHECK: for.inc:
+// CHECK:   %1 = load i32, i32* %i, align 4
+// CHECK:   %inc = add nsw i32 %1, 1
+// CHECK:   store i32 %inc, i32* %i, align 4
+// CHECK:   br label %for.cond, !llvm.loop
+// CHECK: for.end:
+// CHECK:   ret void
+// CHECK: }
Index: clang/test/OpenMP/metadirective_device_kind_codegen.cpp
===================================================================
--- /dev/null
+++ clang/test/OpenMP/metadirective_device_kind_codegen.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux -emit-llvm %s -fexceptions -fcxx-exceptions -o - -fsanitize-address-use-after-scope | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple aarch64-unknown-linux -emit-llvm %s -fexceptions -fcxx-exceptions -o - -fsanitize-address-use-after-scope | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple ppc64le-unknown-linux -emit-llvm %s -fexceptions -fcxx-exceptions -o - -fsanitize-address-use-after-scope | FileCheck %s
+
+// expected-no-diagnostics
+
+void bar();
+
+void foo() {
+#pragma omp metadirective when(device = {kind(any)} \
+                               : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(host, cpu)} \
+                               : parallel for num_threads(4))
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(device = {kind(host)} \
+                               : parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(device = {kind(nohost, gpu)} \
+                               :) when(device = {kind(cpu)} \
+                                       : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(any, cpu)} \
+                               : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(any, host)} \
+                               : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(gpu)} \
+                               : target parallel for) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+}
+
+// CHECK: void @_Z3foov()
+// CHECK: ...) @__kmpc_fork_call(
+// CHECK-NEXT: @__kmpc_push_num_threads
+// CHECK-COUNT-6: ...) @__kmpc_fork_call(
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined.(
+// CHECK: void @_Z3barv()
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..1(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..2(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..3(
+// CHECK: void @_Z3barv()
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..4(
+// CHECK: void @_Z3barv()
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..5(
+// CHECK: void @_Z3barv()
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..6(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
Index: clang/test/OpenMP/metadirective_device_kind_codegen.c
===================================================================
--- /dev/null
+++ clang/test/OpenMP/metadirective_device_kind_codegen.c
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c -triple aarch64-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -x c -triple ppc64le-unknown-linux -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+void bar();
+
+void foo() {
+#pragma omp metadirective when(device = {kind(any)} \
+                               : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(host, cpu)} \
+                               : parallel for num_threads(4))
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(device = {kind(host)} \
+                               : parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(device = {kind(nohost, gpu)} \
+                               :) when(device = {kind(cpu)} \
+                                       : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(any, cpu)} \
+                               : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(any, host)} \
+                               : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(gpu)} \
+                               : target parallel for) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+}
+
+// CHECK: void @foo()
+// CHECK: ...) @__kmpc_fork_call(
+// CHECK-NEXT: @__kmpc_push_num_threads
+// CHECK-COUNT-6: ...) @__kmpc_fork_call(
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined.(
+// CHECK: @bar
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..1(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..2(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..3(
+// CHECK: @bar
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..4(
+// CHECK: @bar
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..5(
+// CHECK: @bar
+// CHECK: ret void
+
+// CHECK: define internal void @.omp_outlined..6(
+// CHECK: call void @__kmpc_for_static_init
+// CHECK: call void @__kmpc_for_static_fini
+// CHECK: ret void
Index: clang/test/OpenMP/metadirective_ast_print.c
===================================================================
--- /dev/null
+++ clang/test/OpenMP/metadirective_ast_print.c
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -ast-print %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+void bar(void);
+
+#define N 10
+void foo(void) {
+#pragma omp metadirective when(device = {kind(cpu)} \
+                               : parallel) default()
+  bar();
+#pragma omp metadirective when(implementation = {vendor(score(0)  \
+                                                        : llvm)}, \
+                               device = {kind(cpu)}               \
+                               : parallel) default(target teams)
+  bar();
+#pragma omp metadirective when(device = {kind(gpu)}                                 \
+                               : target teams) when(implementation = {vendor(llvm)} \
+                                                    : parallel) default()
+  bar();
+#pragma omp metadirective default(target) when(implementation = {vendor(score(5)  \
+                                                                        : llvm)}, \
+                                               device = {kind(cpu, host)}         \
+                                               : parallel)
+  bar();
+#pragma omp metadirective when(user = {condition(N > 10)}                 \
+                               : target) when(user = {condition(N == 10)} \
+                                              : parallel)
+  bar();
+#pragma omp metadirective when(device = {kind(host)} \
+                               : parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(implementation = {extension(match_all)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(implementation = {extension(match_any)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+#pragma omp metadirective when(implementation = {extension(match_none)} \
+                               : parallel) default(parallel for)
+  for (int i = 0; i < 100; i++)
+    ;
+}
+
+// CHECK: void bar();
+// CHECK: void foo()
+// CHECK-NEXT: #pragma omp parallel
+// CHECK-NEXT: bar()
+// CHECK-NEXT: #pragma omp parallel
+// CHECK-NEXT: bar()
+// CHECK-NEXT: #pragma omp parallel
+// CHECK-NEXT: bar()
+// CHECK-NEXT: #pragma omp parallel
+// CHECK-NEXT: bar()
+// CHECK-NEXT: #pragma omp parallel
+// CHECK-NEXT: bar()
+// CHECK-NEXT: #pragma omp parallel for
+// CHECK-NEXT: for (int i = 0; i < 100; i++)
+// CHECK: #pragma omp parallel
+// CHECK-NEXT: for (int i = 0; i < 100; i++)
+// CHECK: #pragma omp parallel for
+// CHECK-NEXT: for (int i = 0; i < 100; i++)
+// CHECK: #pragma omp parallel
+// CHECK-NEXT: for (int i = 0; i < 100; i++)
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1299,6 +1299,7 @@
     case Stmt::OMPMaskedDirectiveClass:
     case Stmt::CapturedStmtClass:
     case Stmt::OMPUnrollDirectiveClass: {
+    case Stmt::OMPMetaDirectiveClass:
       const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
       Engine.addAbortedBlock(node, currBldrCtx->getBlock());
       break;
Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2205,6 +2205,13 @@
   VisitOMPLoopBasedDirective(D);
 }
 
+void ASTStmtWriter::VisitOMPMetaDirective(OMPMetaDirective *D) {
+  VisitStmt(D);
+  Record.push_back(D->getNumClauses());
+  VisitOMPExecutableDirective(D);
+  Code = serialization::STMT_OMP_META_DIRECTIVE;
+}
+
 void ASTStmtWriter::VisitOMPParallelDirective(OMPParallelDirective *D) {
   VisitStmt(D);
   VisitOMPExecutableDirective(D);
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2307,6 +2307,13 @@
   VisitOMPLoopBasedDirective(D);
 }
 
+void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) {
+  VisitStmt(D);
+  // The NumClauses field was read in ReadStmtFromStream.
+  Record.skipInts(1);
+  VisitOMPExecutableDirective(D);
+}
+
 void ASTStmtReader::VisitOMPParallelDirective(OMPParallelDirective *D) {
   VisitStmt(D);
   VisitOMPExecutableDirective(D);
@@ -3183,6 +3190,11 @@
       S = OMPCanonicalLoop::createEmpty(Context);
       break;
 
+    case STMT_OMP_META_DIRECTIVE:
+      S = OMPMetaDirective::CreateEmpty(
+          Context, Record[ASTStmtReader::NumStmtFields], Empty);
+      break;
+
     case STMT_OMP_PARALLEL_DIRECTIVE:
       S =
         OMPParallelDirective::CreateEmpty(Context,
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -8504,6 +8504,15 @@
       AssociatedStmt.get(), D->getBeginLoc(), D->getEndLoc());
 }
 
+template <typename Derived>
+StmtResult
+TreeTransform<Derived>::TransformOMPMetaDirective(OMPMetaDirective *D) {
+  // TODO: Fix This
+  SemaRef.Diag(D->getBeginLoc(), diag::err_omp_instantiation_not_supported)
+      << getOpenMPDirectiveName(D->getDirectiveKind());
+  return StmtError();
+}
+
 template <typename Derived>
 StmtResult
 TreeTransform<Derived>::TransformOMPParallelDirective(OMPParallelDirective *D) {
Index: clang/lib/Sema/SemaOpenMP.cpp
===================================================================
--- clang/lib/Sema/SemaOpenMP.cpp
+++ clang/lib/Sema/SemaOpenMP.cpp
@@ -4279,6 +4279,7 @@
   case OMPD_declare_variant:
   case OMPD_begin_declare_variant:
   case OMPD_end_declare_variant:
+  case OMPD_metadirective:
     llvm_unreachable("OpenMP Directive is not allowed");
   case OMPD_unknown:
   default:
@@ -6325,6 +6326,7 @@
       case OMPC_atomic_default_mem_order:
       case OMPC_device_type:
       case OMPC_match:
+      case OMPC_when:
       default:
         llvm_unreachable("Unexpected clause");
       }
@@ -13262,6 +13264,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
   default:
     llvm_unreachable("Clause is not allowed.");
   }
@@ -13418,6 +13421,7 @@
     case OMPD_atomic:
     case OMPD_teams_distribute:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with if-clause");
     case OMPD_unknown:
     default:
@@ -13500,6 +13504,7 @@
     case OMPD_teams_distribute:
     case OMPD_teams_distribute_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with num_threads-clause");
     case OMPD_unknown:
     default:
@@ -13580,6 +13585,7 @@
     case OMPD_atomic:
     case OMPD_distribute_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with num_teams-clause");
     case OMPD_unknown:
     default:
@@ -13660,6 +13666,7 @@
     case OMPD_atomic:
     case OMPD_distribute_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with thread_limit-clause");
     case OMPD_unknown:
     default:
@@ -13740,6 +13747,7 @@
     case OMPD_distribute_simd:
     case OMPD_target_teams:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with schedule clause");
     case OMPD_unknown:
     default:
@@ -13820,6 +13828,7 @@
     case OMPD_atomic:
     case OMPD_target_teams:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with dist_schedule clause");
     case OMPD_unknown:
     default:
@@ -13902,6 +13911,7 @@
     case OMPD_atomic:
     case OMPD_distribute_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with device-clause");
     case OMPD_unknown:
     default:
@@ -13984,6 +13994,7 @@
     case OMPD_atomic:
     case OMPD_distribute_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
       llvm_unreachable("Unexpected OpenMP directive with grainsize-clause");
     case OMPD_unknown:
     default:
@@ -14003,6 +14014,15 @@
   case OMPC_filter:
     // Do not capture filter-clause expressions.
     break;
+  case OMPC_when:
+    if (DKind == OMPD_metadirective) {
+      CaptureRegion = OMPD_metadirective;
+    } else if (DKind == OMPD_unknown) {
+      llvm_unreachable("Unknown OpenMP directive");
+    } else {
+      llvm_unreachable("Unexpected OpenMP directive with when clause");
+    }
+    break;
   case OMPC_firstprivate:
   case OMPC_lastprivate:
   case OMPC_reduction:
@@ -14530,6 +14550,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
   default:
     llvm_unreachable("Clause is not allowed.");
   }
@@ -14822,6 +14843,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
   default:
     llvm_unreachable("Clause is not allowed.");
   }
@@ -15071,6 +15093,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
   default:
     llvm_unreachable("Clause is not allowed.");
   }
@@ -15613,6 +15636,7 @@
   case OMPC_nocontext:
   case OMPC_detach:
   case OMPC_uses_allocators:
+  case OMPC_when:
   default:
     llvm_unreachable("Clause is not allowed.");
   }
Index: clang/lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- clang/lib/Sema/SemaExceptionSpec.cpp
+++ clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1496,6 +1496,7 @@
   case Stmt::OMPInteropDirectiveClass:
   case Stmt::OMPDispatchDirectiveClass:
   case Stmt::OMPMaskedDirectiveClass:
+  case Stmt::OMPMetaDirectiveClass:
   case Stmt::ReturnStmtClass:
   case Stmt::SEHExceptStmtClass:
   case Stmt::SEHFinallyStmtClass:
Index: clang/lib/Parse/ParseOpenMP.cpp
===================================================================
--- clang/lib/Parse/ParseOpenMP.cpp
+++ clang/lib/Parse/ParseOpenMP.cpp
@@ -2224,6 +2224,7 @@
   case OMPD_target_teams_distribute_simd:
   case OMPD_dispatch:
   case OMPD_masked:
+  case OMPD_metadirective:
     Diag(Tok, diag::err_omp_unexpected_directive)
         << 1 << getOpenMPDirectiveName(DKind);
     break;
@@ -2278,8 +2279,10 @@
 ///
 StmtResult
 Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) {
-  assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
-         "Not an OpenMP directive!");
+  static bool ReadDirectiveWithinMetadirective = false;
+  if (!ReadDirectiveWithinMetadirective)
+    assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
+           "Not an OpenMP directive!");
   ParsingOpenMPDirectiveRAII DirScope(*this);
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
   SmallVector<OMPClause *, 5> Clauses;
@@ -2288,8 +2291,14 @@
       FirstClauses(llvm::omp::Clause_enumSize + 1);
   unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope |
                         Scope::CompoundStmtScope | Scope::OpenMPDirectiveScope;
-  SourceLocation Loc = ConsumeAnnotationToken(), EndLoc;
+  if (!ReadDirectiveWithinMetadirective)
+    ConsumeAnnotationToken();
+  SourceLocation Loc = Tok.getLocation(), EndLoc;
   OpenMPDirectiveKind DKind = parseOpenMPDirectiveKind(*this);
+  if (ReadDirectiveWithinMetadirective && DKind == OMPD_unknown) {
+    Diag(Tok, diag::err_omp_unknown_directive);
+    return StmtError();
+  }
   OpenMPDirectiveKind CancelRegion = OMPD_unknown;
   // Name of critical directive.
   DeclarationNameInfo DirName;
@@ -2297,6 +2306,154 @@
   bool HasAssociatedStatement = true;
 
   switch (DKind) {
+  case OMPD_metadirective: {
+    ConsumeToken();
+    SmallVector<VariantMatchInfo, 4> VMIs;
+
+    // First iteration of parsing all clauses of metadirective.
+    // This iteration only parses and collects all context selector ignoring the
+    // associated directives.
+    TentativeParsingAction TPA(*this);
+    ASTContext &ASTContext = Actions.getASTContext();
+
+    BalancedDelimiterTracker T(*this, tok::l_paren,
+                               tok::annot_pragma_openmp_end);
+    while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+      OpenMPClauseKind CKind = Tok.isAnnotation()
+                                   ? OMPC_unknown
+                                   : getOpenMPClauseKind(PP.getSpelling(Tok));
+      SourceLocation Loc = ConsumeToken();
+
+      // Parse '('.
+      if (T.expectAndConsume(diag::err_expected_lparen_after,
+                             getOpenMPClauseName(CKind).data()))
+        return Directive;
+
+      OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+      if (CKind == OMPC_when) {
+        // parse and get OMPTraitInfo to pass to the When clause
+        parseOMPContextSelectors(Loc, TI);
+        for (OMPTraitSet T : TI.Sets) {
+          llvm::errs() << "TraitSet: " << llvm::omp::getOpenMPContextTraitSetName(T.Kind) << "\n";
+          for (OMPTraitSelector S: T.Selectors) {
+              llvm::errs() << "TraitSelector: " << llvm::omp::getOpenMPContextTraitSelectorName(S.Kind) << "\n";
+              for (OMPTraitProperty P : S.Properties) {
+                  StringRef Raw;
+                  llvm::errs() << "TraitProperty: " << llvm::omp::getOpenMPContextTraitPropertyName(P.Kind, Raw) << "\n";
+              }
+          }
+        }
+        if (TI.Sets.size() == 0) {
+          Diag(Tok, diag::err_omp_expected_context_selector) << "when clause";
+          TPA.Commit();
+          return Directive;
+        }
+
+        // Parse ':'
+        if (Tok.is(tok::colon))
+          ConsumeAnyToken();
+        else {
+          Diag(Tok, diag::err_omp_expected_colon) << "when clause";
+          TPA.Commit();
+          return Directive;
+        }
+      }
+      // Skip Directive for now. We will parse directive in the second iteration
+      int paren = 0;
+      while (Tok.isNot(tok::r_paren) || paren != 0) {
+        if (Tok.is(tok::l_paren))
+          paren++;
+        if (Tok.is(tok::r_paren))
+          paren--;
+        if (Tok.is(tok::annot_pragma_openmp_end)) {
+          Diag(Tok, diag::err_omp_expected_punc)
+              << getOpenMPClauseName(CKind) << 0;
+          TPA.Commit();
+          return Directive;
+        } 
+        ConsumeAnyToken();
+      }
+      // Parse ')'
+      if (Tok.is(tok::r_paren))
+        T.consumeClose();
+
+      VariantMatchInfo VMI;
+      TI.getAsVariantMatchInfo(ASTContext, VMI);
+
+      VMIs.push_back(VMI);
+    }
+
+    TPA.Revert();
+    // End of the first iteration. Parser is reset to the start of metadirective
+
+    TargetOMPContext OMPCtx(ASTContext, nullptr, nullptr);
+
+    // A single match is returned for OpenMP 5.0
+    int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx);
+
+    int idx = 0;
+    // In OpenMP 5.0 metadirective is either replaced by another directive or
+    // ignored.
+    // TODO: In OpenMP 5.1 generate multiple directives based upon the matches
+    // found by getBestWhenMatchForContext.
+    while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+      // OpenMP 5.0 implementation - Skip to the best index found.
+      if (idx++ != BestIdx) {
+        ConsumeToken();  // Consume clause name
+        T.consumeOpen(); // Consume '('
+        int paren = 0;
+        // Skip everything inside the clause
+        while (Tok.isNot(tok::r_paren) || paren != 0) {
+          if (Tok.is(tok::l_paren))
+            paren++;
+          if (Tok.is(tok::r_paren))
+            paren--;
+          ConsumeAnyToken();
+        }
+        // Parse ')'
+        if (Tok.is(tok::r_paren))
+          T.consumeClose();
+        continue;
+      }
+
+      OpenMPClauseKind CKind = Tok.isAnnotation()
+                                   ? OMPC_unknown
+                                   : getOpenMPClauseKind(PP.getSpelling(Tok));
+      SourceLocation Loc = ConsumeToken();
+
+      // Parse '('.
+      T.consumeOpen();
+
+      // Skip ContextSelectors for when clause
+      if (CKind == OMPC_when) {
+        OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+        // parse and skip the ContextSelectors
+        parseOMPContextSelectors(Loc, TI);
+        for (auto T : TI.Sets) {
+          llvm::errs() << "[2429] T.Kind: "
+                       << llvm::omp::getOpenMPContextTraitSetName(T.Kind)
+                       << "\n";
+        }
+
+        // Parse ':'
+        ConsumeAnyToken();
+      }
+
+      // If no directive is passed, skip in OpenMP 5.0.
+      // TODO: Generate nothing directive from OpenMP 5.1.
+      if (Tok.is(tok::r_paren)) {
+        SkipUntil(tok::annot_pragma_openmp_end);
+        break;
+      }
+
+      // Parse Directive
+      ReadDirectiveWithinMetadirective = true;
+      Directive = ParseOpenMPDeclarativeOrExecutableDirective(StmtCtx);
+      ReadDirectiveWithinMetadirective = false;
+      break;
+    }
+    break;
+  }
   case OMPD_threadprivate: {
     // FIXME: Should this be permitted in C++?
     if ((StmtCtx & ParsedStmtContext::AllowDeclarationsInC) ==
@@ -2488,6 +2645,13 @@
     Actions.StartOpenMPDSABlock(DKind, DirName, Actions.getCurScope(), Loc);
 
     while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+      // If we are parsing for a directive within a metadirective, the directive
+      // ends with a ')'.
+      if (ReadDirectiveWithinMetadirective && Tok.is(tok::r_paren)) {
+        while (Tok.isNot(tok::annot_pragma_openmp_end))
+          ConsumeAnyToken();
+        break;
+      }
       bool HasImplicitClause = false;
       if (ImplicitClauseAllowed && Tok.is(tok::l_paren)) {
         HasImplicitClause = true;
@@ -2602,7 +2766,7 @@
     SkipUntil(tok::annot_pragma_openmp_end);
     break;
   }
-  return Directive;
+    return Directive;
 }
 
 // Parses simple list:
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -3464,6 +3464,7 @@
                                        const RegionCodeGenTy &BodyGen,
                                        OMPTargetDataInfo &InputInfo);
 
+  void EmitOMPMetaDirective(const OMPMetaDirective &S);
   void EmitOMPParallelDirective(const OMPParallelDirective &S);
   void EmitOMPSimdDirective(const OMPSimdDirective &S);
   void EmitOMPTileDirective(const OMPTileDirective &S);
Index: clang/lib/CodeGen/CGStmtOpenMP.cpp
===================================================================
--- clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -1784,6 +1784,10 @@
   checkForLastprivateConditionalUpdate(*this, S);
 }
 
+void CodeGenFunction::EmitOMPMetaDirective(const OMPMetaDirective &S) {
+  EmitStmt(S.getIfStmt());
+}
+
 namespace {
 /// RAII to handle scopes for loop transformation directives.
 class OMPTransformDirectiveScopeRAII {
@@ -5986,6 +5990,7 @@
   case OMPC_novariants:
   case OMPC_nocontext:
   case OMPC_filter:
+  case OMPC_when:
     llvm_unreachable("Clause is not allowed in 'omp atomic'.");
   }
 }
Index: clang/lib/CodeGen/CGStmt.cpp
===================================================================
--- clang/lib/CodeGen/CGStmt.cpp
+++ clang/lib/CodeGen/CGStmt.cpp
@@ -196,6 +196,9 @@
   case Stmt::SEHTryStmtClass:
     EmitSEHTryStmt(cast<SEHTryStmt>(*S));
     break;
+  case Stmt::OMPMetaDirectiveClass:
+    EmitOMPMetaDirective(cast<OMPMetaDirective>(*S));
+    break;
   case Stmt::OMPCanonicalLoopClass:
     EmitOMPCanonicalLoop(cast<OMPCanonicalLoop>(S));
     break;
Index: clang/lib/CodeGen/CGOpenMPRuntime.cpp
===================================================================
--- clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -6740,6 +6740,7 @@
   case OMPD_parallel_master_taskloop:
   case OMPD_parallel_master_taskloop_simd:
   case OMPD_requires:
+  case OMPD_metadirective:
   case OMPD_unknown:
     break;
   default:
@@ -7214,6 +7215,7 @@
   case OMPD_parallel_master_taskloop:
   case OMPD_parallel_master_taskloop_simd:
   case OMPD_requires:
+  case OMPD_metadirective:
   case OMPD_unknown:
     break;
   default:
@@ -9851,6 +9853,7 @@
     case OMPD_parallel_master_taskloop:
     case OMPD_parallel_master_taskloop_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
     case OMPD_unknown:
     default:
       llvm_unreachable("Unexpected directive.");
@@ -10701,6 +10704,7 @@
     case OMPD_parallel_master_taskloop:
     case OMPD_parallel_master_taskloop_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
     case OMPD_unknown:
     default:
       llvm_unreachable("Unknown target directive for OpenMP device codegen.");
@@ -11382,6 +11386,7 @@
     case OMPD_target_parallel_for:
     case OMPD_target_parallel_for_simd:
     case OMPD_requires:
+    case OMPD_metadirective:
     case OMPD_unknown:
     default:
       llvm_unreachable("Unexpected standalone target data directive.");
Index: clang/lib/Basic/OpenMPKinds.cpp
===================================================================
--- clang/lib/Basic/OpenMPKinds.cpp
+++ clang/lib/Basic/OpenMPKinds.cpp
@@ -185,6 +185,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
     break;
   default:
     break;
@@ -428,6 +429,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
     break;
   default:
     break;
@@ -591,6 +593,9 @@
     OpenMPDirectiveKind DKind) {
   assert(unsigned(DKind) < llvm::omp::Directive_enumSize);
   switch (DKind) {
+  case OMPD_metadirective:
+    CaptureRegions.push_back(OMPD_metadirective);
+    break;
   case OMPD_parallel:
   case OMPD_parallel_for:
   case OMPD_parallel_for_simd:
Index: clang/lib/AST/StmtProfile.cpp
===================================================================
--- clang/lib/AST/StmtProfile.cpp
+++ clang/lib/AST/StmtProfile.cpp
@@ -903,6 +903,10 @@
   VisitOMPLoopBasedDirective(S);
 }
 
+void StmtProfiler::VisitOMPMetaDirective(const OMPMetaDirective *S) {
+  VisitOMPExecutableDirective(S);
+}
+
 void StmtProfiler::VisitOMPParallelDirective(const OMPParallelDirective *S) {
   VisitOMPExecutableDirective(S);
 }
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -654,6 +654,11 @@
     PrintStmt(S->getRawStmt());
 }
 
+void StmtPrinter::VisitOMPMetaDirective(OMPMetaDirective *Node) {
+  Indent() << "#pragma omp metadirective";
+  PrintOMPExecutableDirective(Node);
+}
+
 void StmtPrinter::VisitOMPParallelDirective(OMPParallelDirective *Node) {
   Indent() << "#pragma omp parallel";
   PrintOMPExecutableDirective(Node);
Index: clang/lib/AST/StmtOpenMP.cpp
===================================================================
--- clang/lib/AST/StmtOpenMP.cpp
+++ clang/lib/AST/StmtOpenMP.cpp
@@ -253,6 +253,25 @@
   llvm::copy(A, getFinalsConditions().begin());
 }
 
+OMPMetaDirective *OMPMetaDirective::Create(const ASTContext &C,
+                                           SourceLocation StartLoc,
+                                           SourceLocation EndLoc,
+                                           ArrayRef<OMPClause *> Clauses,
+                                           Stmt *AssociatedStmt, Stmt *IfStmt) {
+  auto *Dir = createDirective<OMPMetaDirective>(
+      C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc);
+  Dir->setIfStmt(IfStmt);
+  return Dir;
+}
+
+OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C,
+                                                unsigned NumClauses,
+                                                EmptyShell) {
+  return createEmptyDirective<OMPMetaDirective>(C, NumClauses,
+                                                /*HasAssociatedStmt=*/true,
+                                                /*NumChildren=*/1);
+}
+
 OMPParallelDirective *OMPParallelDirective::Create(
     const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
     ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Expr *TaskRedRef,
Index: clang/lib/AST/OpenMPClause.cpp
===================================================================
--- clang/lib/AST/OpenMPClause.cpp
+++ clang/lib/AST/OpenMPClause.cpp
@@ -160,6 +160,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
     break;
   default:
     break;
@@ -257,6 +258,7 @@
   case OMPC_exclusive:
   case OMPC_uses_allocators:
   case OMPC_affinity:
+  case OMPC_when:
     break;
   default:
     break;
Index: clang/include/clang/Serialization/ASTBitCodes.h
===================================================================
--- clang/include/clang/Serialization/ASTBitCodes.h
+++ clang/include/clang/Serialization/ASTBitCodes.h
@@ -1893,6 +1893,7 @@
   STMT_SEH_TRY,                     // SEHTryStmt
 
   // OpenMP directives
+  STMT_OMP_META_DIRECTIVE,
   STMT_OMP_CANONICAL_LOOP,
   STMT_OMP_PARALLEL_DIRECTIVE,
   STMT_OMP_SIMD_DIRECTIVE,
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -10440,6 +10440,12 @@
   /// \param Init First part of the for loop.
   void ActOnOpenMPLoopInitialization(SourceLocation ForLoc, Stmt *Init);
 
+  /// Called on well-formed '\#pragma omp metadirective' after parsing
+  /// of the  associated statement.
+  StmtResult ActOnOpenMPMetaDirective(ArrayRef<OMPClause *> Clauses,
+                                      Stmt *AStmt, SourceLocation StartLoc,
+                                      SourceLocation EndLoc);
+
   // OpenMP directives and clauses.
   /// Called on correct id-expression from the '#pragma omp
   /// threadprivate'.
@@ -11007,6 +11013,10 @@
                                      SourceLocation StartLoc,
                                      SourceLocation LParenLoc,
                                      SourceLocation EndLoc);
+  /// Called on well-formed 'when' clause.
+  OMPClause *ActOnOpenMPWhenClause(OMPTraitInfo &TI, SourceLocation StartLoc,
+                                   SourceLocation LParenLoc,
+                                   SourceLocation EndLoc);
   /// Called on well-formed 'default' clause.
   OMPClause *ActOnOpenMPDefaultClause(llvm::omp::DefaultKind Kind,
                                       SourceLocation KindLoc,
Index: clang/include/clang/Basic/StmtNodes.td
===================================================================
--- clang/include/clang/Basic/StmtNodes.td
+++ clang/include/clang/Basic/StmtNodes.td
@@ -219,6 +219,7 @@
 // OpenMP Directives.
 def OMPCanonicalLoop : StmtNode<Stmt>;
 def OMPExecutableDirective : StmtNode<Stmt, 1>;
+def OMPMetaDirective : StmtNode<OMPExecutableDirective>;
 def OMPLoopBasedDirective : StmtNode<OMPExecutableDirective, 1>;
 def OMPLoopDirective : StmtNode<OMPLoopBasedDirective, 1>;
 def OMPParallelDirective : StmtNode<OMPExecutableDirective>;
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10790,6 +10790,8 @@
 def err_omp_unroll_full_variable_trip_count : Error<
   "loop to be fully unrolled must have a constant trip count">;
 def note_omp_directive_here : Note<"'%0' directive found here">;
+def err_omp_instantiation_not_supported
+    : Error<"instantiation of '%0' not supported yet">;
 } // end of OpenMP category
 
 let CategoryName = "Related Result Type Issue" in {
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1436,6 +1436,9 @@
   "specifying OpenMP directives with [[]] is incompatible with OpenMP "
   "standards before OpenMP 5.1">,
   InGroup<OpenMPPre51Compat>, DefaultIgnore;
+def err_omp_expected_colon : Error<"missing ':' in %0">;
+def err_omp_expected_context_selector
+    : Error<"expected valid context selector in %0">;
 
 // Pragma loop support.
 def err_pragma_loop_missing_argument : Error<
Index: clang/include/clang/AST/StmtOpenMP.h
===================================================================
--- clang/include/clang/AST/StmtOpenMP.h
+++ clang/include/clang/AST/StmtOpenMP.h
@@ -5379,6 +5379,44 @@
   }
 };
 
+/// This represents '#pragma omp metadirective' directive.
+///
+/// \code
+/// #pragma omp metadirective when(user={condition(N>10)}: parallel for)
+/// \endcode
+/// In this example directive '#pragma omp metadirective' has clauses 'when'
+/// with a dynamic user condition to check if a variable 'N > 10'
+///
+class OMPMetaDirective final : public OMPExecutableDirective {
+  friend class ASTStmtReader;
+  friend class OMPExecutableDirective;
+  Stmt *IfStmt;
+
+  OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc)
+      : OMPExecutableDirective(OMPMetaDirectiveClass,
+                               llvm::omp::OMPD_metadirective, StartLoc,
+                               EndLoc) {}
+  explicit OMPMetaDirective()
+      : OMPExecutableDirective(OMPMetaDirectiveClass,
+                               llvm::omp::OMPD_metadirective, SourceLocation(),
+                               SourceLocation()) {}
+
+public:
+  static OMPMetaDirective *Create(const ASTContext &C, SourceLocation StartLoc,
+                                  SourceLocation EndLoc,
+                                  ArrayRef<OMPClause *> Clauses,
+                                  Stmt *AssociatedStmt, Stmt *IfStmt);
+  static OMPMetaDirective *CreateEmpty(const ASTContext &C, unsigned NumClauses,
+                                       EmptyShell);
+
+  void setIfStmt(Stmt *stmt) { IfStmt = stmt; }
+  Stmt *getIfStmt() const { return IfStmt; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == OMPMetaDirectiveClass;
+  }
+};
+
 } // end namespace clang
 
 #endif
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2842,6 +2842,9 @@
   return TraverseOMPExecutableDirective(S);
 }
 
+DEF_TRAVERSE_STMT(OMPMetaDirective,
+                  { TRY_TO(TraverseOMPExecutableDirective(S)); })
+
 DEF_TRAVERSE_STMT(OMPParallelDirective,
                   { TRY_TO(TraverseOMPExecutableDirective(S)); })
 
Index: clang/include/clang-c/Index.h
===================================================================
--- clang/include/clang-c/Index.h
+++ clang/include/clang-c/Index.h
@@ -2592,7 +2592,11 @@
    */
   CXCursor_OMPUnrollDirective = 293,
 
-  CXCursor_LastStmt = CXCursor_OMPUnrollDirective,
+  /** OpenMP metadirective directive.
+   */
+  CXCursor_OMPMetaDirective = 294,
+
+  CXCursor_LastStmt = CXCursor_OMPMetaDirective,
 
   /**
    * Cursor that represents the translation unit itself.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to