diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h --- a/clang/include/clang-c/Index.h +++ b/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. diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/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)); }) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/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 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 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1436,6 +1436,9 @@ "specifying OpenMP directives with [[]] is incompatible with OpenMP " "standards before OpenMP 5.1">, InGroup, 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/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 { diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -219,6 +219,7 @@ // OpenMP Directives. def OMPCanonicalLoop : StmtNode; def OMPExecutableDirective : StmtNode; +def OMPMetaDirective : StmtNode; def OMPLoopBasedDirective : StmtNode; def OMPLoopDirective : StmtNode; def OMPParallelDirective : StmtNode; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/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 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, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/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, diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp --- a/clang/lib/AST/OpenMPClause.cpp +++ b/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; diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/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 Clauses, + Stmt *AssociatedStmt, Stmt *IfStmt) { + auto *Dir = createDirective( + C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc); + Dir->setIfStmt(IfStmt); + return Dir; +} + +OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + EmptyShell) { + return createEmptyDirective(C, NumClauses, + /*HasAssociatedStmt=*/true, + /*NumChildren=*/1); +} + OMPParallelDirective *OMPParallelDirective::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, ArrayRef Clauses, Stmt *AssociatedStmt, Expr *TaskRedRef, diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/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); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp --- a/clang/lib/AST/StmtProfile.cpp +++ b/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); } diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/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: diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/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."); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -196,6 +196,9 @@ case Stmt::SEHTryStmtClass: EmitSEHTryStmt(cast(*S)); break; + case Stmt::OMPMetaDirectiveClass: + EmitOMPMetaDirective(cast(*S)); + break; case Stmt::OMPCanonicalLoopClass: EmitOMPCanonicalLoop(cast(S)); break; diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/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'."); } } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/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); diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/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 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 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: diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/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: diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/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."); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8504,6 +8504,15 @@ AssociatedStmt.get(), D->getBeginLoc(), D->getEndLoc()); } +template +StmtResult +TreeTransform::TransformOMPMetaDirective(OMPMetaDirective *D) { + // TODO: Fix This + SemaRef.Diag(D->getBeginLoc(), diag::err_omp_instantiation_not_supported) + << getOpenMPDirectiveName(D->getDirectiveKind()); + return StmtError(); +} + template StmtResult TreeTransform::TransformOMPParallelDirective(OMPParallelDirective *D) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/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, diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/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); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/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; diff --git a/clang/test/OpenMP/metadirective_ast_print.c b/clang/test/OpenMP/metadirective_ast_print.c new file mode 100644 --- /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 --- /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 --- /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 --- /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 --- /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 --- /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 --- /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 --- a/clang/tools/libclang/CIndex.cpp +++ b/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: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp --- a/clang/tools/libclang/CXCursor.cpp +++ b/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; diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/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 ]; } +def OMP_Metadirective : Directive<"metadirective"> { + let allowedClauses = [VersionedClause]; + let allowedOnceClauses = [VersionedClause]; +} def OMP_Unknown : Directive<"unknown"> { let isDefault = true; } diff --git a/metadirective-finish.diff b/metadirective-finish.diff new file mode 100644 --- /dev/null +++ b/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::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 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, 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; + // OpenMP Directives. + def OMPCanonicalLoop : StmtNode; + def OMPExecutableDirective : StmtNode; ++def OMPMetaDirective : StmtNode; + def OMPLoopBasedDirective : StmtNode; + def OMPLoopDirective : StmtNode; + def OMPParallelDirective : StmtNode; +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 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 A) { + llvm::copy(A, getFinalsConditions().begin()); + } + ++OMPMetaDirective *OMPMetaDirective::Create(const ASTContext &C, ++ SourceLocation StartLoc, ++ SourceLocation EndLoc, ++ ArrayRef Clauses, ++ Stmt *AssociatedStmt, Stmt *IfStmt) { ++ auto *Dir = createDirective( ++ C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc); ++ Dir->setIfStmt(IfStmt); ++ return Dir; ++} ++ ++OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C, ++ unsigned NumClauses, ++ EmptyShell) { ++ return createEmptyDirective(C, NumClauses, ++ /*HasAssociatedStmt=*/true, ++ /*NumChildren=*/1); ++} ++ + OMPParallelDirective *OMPParallelDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef 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 Attrs) { + case Stmt::SEHTryStmtClass: + EmitSEHTryStmt(cast(*S)); + break; ++ case Stmt::OMPMetaDirectiveClass: ++ EmitOMPMetaDirective(cast(*S)); ++ break; + case Stmt::OMPCanonicalLoopClass: + EmitOMPCanonicalLoop(cast(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 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 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::TransformOMPExecutableDirective( + AssociatedStmt.get(), D->getBeginLoc(), D->getEndLoc()); + } + ++template ++StmtResult ++TreeTransform::TransformOMPMetaDirective(OMPMetaDirective *D) { ++ // TODO: Fix This ++ SemaRef.Diag(D->getBeginLoc(), diag::err_omp_instantiation_not_supported) ++ << getOpenMPDirectiveName(D->getDirectiveKind()); ++ return StmtError(); ++} ++ + template + StmtResult + TreeTransform::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 + ]; + } ++def OMP_Metadirective : Directive<"metadirective"> { ++ let allowedClauses = [VersionedClause]; ++ let allowedOnceClauses = [VersionedClause]; ++} + def OMP_Unknown : Directive<"unknown"> { + let isDefault = true; + }