https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/192455
>From 7bed434c1a9f52203764e5e8c9166a5548e6e751 Mon Sep 17 00:00:00 2001 From: Ammarguellat <[email protected]> Date: Mon, 6 Jul 2026 13:42:58 -0700 Subject: [PATCH 1/4] Addressed review comments --- clang/include/clang/AST/OpenMPClause.h | 13 ++ clang/include/clang/AST/StmtOpenMP.h | 68 ++++-- clang/include/clang/AST/TextNodeDumper.h | 1 + clang/include/clang/Parse/Parser.h | 25 ++- clang/include/clang/Sema/SemaOpenMP.h | 14 ++ clang/lib/AST/StmtOpenMP.cpp | 58 ++++- clang/lib/AST/TextNodeDumper.cpp | 11 + clang/lib/CodeGen/CGStmtOpenMP.cpp | 11 +- clang/lib/Parse/ParseOpenMP.cpp | 193 +++++++++++++--- clang/lib/Sema/SemaOpenMP.cpp | 210 ++++++++++++++++++ clang/lib/Serialization/ASTReaderStmt.cpp | 21 +- clang/lib/Serialization/ASTWriterStmt.cpp | 15 ++ ...tadirective_runtime_user_condition_ast.cpp | 35 +++ ...rective_runtime_user_condition_codegen.cpp | 140 ++++++++++++ 14 files changed, 750 insertions(+), 65 deletions(-) create mode 100644 clang/test/OpenMP/metadirective_runtime_user_condition_ast.cpp create mode 100644 clang/test/OpenMP/metadirective_runtime_user_condition_codegen.cpp diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 12959fe0c2ff0..e5791347926d8 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -9949,6 +9949,19 @@ class OMPTraitInfo { return false; } + /// Check if this trait info contains any user conditions. + bool hasUserCondition() const { + for (const OMPTraitSet &Set : Sets) { + if (Set.Kind != llvm::omp::TraitSet::user) + continue; + for (const OMPTraitSelector &Selector : Set.Selectors) { + if (Selector.Kind == llvm::omp::TraitSelector::user_condition) + return true; + } + } + return false; + } + /// Print a human readable representation into \p OS. void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const; }; diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index dbc76e7df8ecd..c13b6527a916e 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6374,28 +6374,70 @@ class OMPMaskedDirective final : public OMPExecutableDirective { /// class OMPMetaDirective final : public OMPExecutableDirective { friend class ASTStmtReader; + friend class ASTStmtWriter; friend class OMPExecutableDirective; - Stmt *IfStmt; + unsigned NumVariants; + OpenMPDirectiveKind *DirectiveKinds; + Expr **Conditions; + Stmt **VariantDirectives; - OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc) + OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc, + unsigned NumVariants) : OMPExecutableDirective(OMPMetaDirectiveClass, - llvm::omp::OMPD_metadirective, StartLoc, - EndLoc) {} - explicit OMPMetaDirective() + llvm::omp::OMPD_metadirective, StartLoc, EndLoc), + NumVariants(NumVariants), DirectiveKinds(nullptr), Conditions(nullptr), + VariantDirectives(nullptr) {} + explicit OMPMetaDirective(unsigned NumVariants) : OMPExecutableDirective(OMPMetaDirectiveClass, llvm::omp::OMPD_metadirective, SourceLocation(), - SourceLocation()) {} + SourceLocation()), + NumVariants(NumVariants), DirectiveKinds(nullptr), Conditions(nullptr), + VariantDirectives(nullptr) {} - void setIfStmt(Stmt *S) { IfStmt = S; } + void setIfStmt(Stmt *S) { Data->getChildren()[0] = S; } + void setDirectiveKinds(OpenMPDirectiveKind *DK) { DirectiveKinds = DK; } + void setConditions(Expr **C) { Conditions = C; } + void setVariantDirectives(Stmt **VD) { VariantDirectives = VD; } + static void allocateVariantStorage(const ASTContext &C, OMPMetaDirective *Dir, + unsigned NumVariants); + + OpenMPDirectiveKind *getDirectiveKinds() { return DirectiveKinds; } + Expr **getConditions() { return Conditions; } + Stmt **getVariantDirectives() { return VariantDirectives; } public: - static OMPMetaDirective *Create(const ASTContext &C, SourceLocation StartLoc, - SourceLocation EndLoc, - ArrayRef<OMPClause *> Clauses, - Stmt *AssociatedStmt, Stmt *IfStmt); + static OMPMetaDirective * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Stmt *IfStmt, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, + ArrayRef<Expr *> Conditions, ArrayRef<Stmt *> VariantDirectives); static OMPMetaDirective *CreateEmpty(const ASTContext &C, unsigned NumClauses, - EmptyShell); - Stmt *getIfStmt() const { return IfStmt; } + unsigned NumVariants, EmptyShell); + Stmt *getIfStmt() const { return Data->getChildren()[0]; } + + unsigned getNumVariants() const { return NumVariants; } + + ArrayRef<OpenMPDirectiveKind> getDirectiveKinds() const { + return ArrayRef<OpenMPDirectiveKind>(DirectiveKinds, NumVariants); + } + + ArrayRef<Expr *> getConditions() const { + return ArrayRef<Expr *>(Conditions, NumVariants); + } + + ArrayRef<Stmt *> getVariantDirectives() const { + return ArrayRef<Stmt *>(VariantDirectives, NumVariants); + } + + child_range children() { + if (!Data) + return child_range(child_iterator(), child_iterator()); + return Data->getChildren(); + } + + const_child_range children() const { + return const_cast<OMPMetaDirective *>(this)->children(); + } static bool classof(const Stmt *T) { return T->getStmtClass() == OMPMetaDirectiveClass; diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 41ddd88a8326c..803e69f615ed2 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -374,6 +374,7 @@ class TextNodeDumper void VisitPragmaCommentDecl(const PragmaCommentDecl *D); void VisitPragmaDetectMismatchDecl(const PragmaDetectMismatchDecl *D); void VisitOMPExecutableDirective(const OMPExecutableDirective *D); + void VisitOMPMetaDirective(const OMPMetaDirective *D); void VisitOMPDeclareReductionDecl(const OMPDeclareReductionDecl *D); void VisitOMPRequiresDecl(const OMPRequiresDecl *D); void VisitOMPCapturedExprDecl(const OMPCapturedExprDecl *D); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 163aa483a84e3..f5d4c28b0a1d6 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -6679,8 +6679,11 @@ class Parser : public CodeCompletionHandler { /// \param StmtCtx The context in which we're parsing the directive. /// \param ReadDirectiveWithinMetadirective true if directive is within a /// metadirective and therefore ends on the closing paren. + /// /// \param ConsumeDirectiveOnlyInMetadirective true if we should stop at + /// ')' for runtime metadirective (to parse multiple directives in sequence). StmtResult ParseOpenMPDeclarativeOrExecutableDirective( - ParsedStmtContext StmtCtx, bool ReadDirectiveWithinMetadirective = false); + ParsedStmtContext StmtCtx, bool ReadDirectiveWithinMetadirective = false, + bool ConsumeDirectiveOnlyInMetadirective = false); /// Parses executable directive. /// @@ -6689,10 +6692,12 @@ class Parser : public CodeCompletionHandler { /// \param Loc Source location of the beginning of the directive. /// \param ReadDirectiveWithinMetadirective true if directive is within a /// metadirective and therefore ends on the closing paren. - StmtResult - ParseOpenMPExecutableDirective(ParsedStmtContext StmtCtx, - OpenMPDirectiveKind DKind, SourceLocation Loc, - bool ReadDirectiveWithinMetadirective); + /// /// \param ConsumeDirectiveOnlyInMetadirective true if we should stop at + /// ')' for runtime metadirective (to parse multiple directives in sequence). + StmtResult ParseOpenMPExecutableDirective( + ParsedStmtContext StmtCtx, OpenMPDirectiveKind DKind, SourceLocation Loc, + bool ReadDirectiveWithinMetadirective, + bool ConsumeDirectiveOnlyInMetadirective = false); /// Parses informational directive. /// @@ -6701,9 +6706,17 @@ class Parser : public CodeCompletionHandler { /// \param Loc Source location of the beginning of the directive. /// \param ReadDirectiveWithinMetadirective true if directive is within a /// metadirective and therefore ends on the closing paren. + /// /// \param ConsumeDirectiveOnlyInMetadirective true if parsing only the + /// directive (runtime path), false if also consuming to pragma end + /// (compile-time). StmtResult ParseOpenMPInformationalDirective( ParsedStmtContext StmtCtx, OpenMPDirectiveKind DKind, SourceLocation Loc, - bool ReadDirectiveWithinMetadirective); + bool ReadDirectiveWithinMetadirective, + bool ConsumeDirectiveOnlyInMetadirective = false); + + /// Skip tokens until reaching a closing ')' that matches the current nesting + /// level. Handles nested parentheses correctly. + void skipToMatchingParen(); /// Parses clause of kind \a CKind for directive of a kind \a Kind. /// diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index f689e227866a7..87297a7e11854 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -216,6 +216,20 @@ class SemaOpenMP : public SemaBase { Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc); + /// Called for metadirectives with user conditions that may require + /// runtime selection. + StmtResult ActOnOpenMPMetaDirective( + SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, + ArrayRef<OpenMPClauseKind> ClauseKinds, ArrayRef<Expr *> Conditions, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AStmt, + ArrayRef<Stmt *> VariantDirectives = {}); + + private: + StmtResult createParallelDirectiveForMetadirective(Stmt *Body, + SourceLocation StartLoc, + SourceLocation EndLoc); +public: // OpenMP directives and clauses. /// Called on correct id-expression from the '#pragma omp /// threadprivate'. diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index 9d6b315effb41..1bdf50be71a7e 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -260,23 +260,61 @@ 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); +void OMPMetaDirective::allocateVariantStorage(const ASTContext &C, + OMPMetaDirective *Dir, + unsigned NumVariants) { + if (NumVariants == 0) + return; + + auto *DKs = static_cast<OpenMPDirectiveKind *>(C.Allocate( + NumVariants * sizeof(OpenMPDirectiveKind), alignof(OpenMPDirectiveKind))); + auto **Conds = static_cast<Expr **>( + C.Allocate(NumVariants * sizeof(Expr *), alignof(Expr *))); + + Dir->setDirectiveKinds(DKs); + Dir->setConditions(Conds); +} + +OMPMetaDirective *OMPMetaDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Stmt *IfStmt, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, ArrayRef<Expr *> Conditions, + ArrayRef<Stmt *> VariantDirectives) { + assert(DirectiveKinds.size() == Conditions.size() && + "DirectiveKinds and Conditions must have the same size"); + assert((VariantDirectives.empty() || + DirectiveKinds.size() == VariantDirectives.size()) && + "VariantDirectives, if provided, must match DirectiveKinds size"); + unsigned NumVariants = DirectiveKinds.size(); + auto *Dir = createDirective<OMPMetaDirective>(C, Clauses, AssociatedStmt, + /*NumChildren=*/1, StartLoc, + EndLoc, NumVariants); Dir->setIfStmt(IfStmt); + OMPMetaDirective::allocateVariantStorage(C, Dir, NumVariants); + if (NumVariants > 0) { + std::copy(DirectiveKinds.begin(), DirectiveKinds.end(), + Dir->getDirectiveKinds()); + std::copy(Conditions.begin(), Conditions.end(), Dir->getConditions()); + + if (!VariantDirectives.empty()) { + auto **VDs = static_cast<Stmt **>( + C.Allocate(NumVariants * sizeof(Stmt *), alignof(Stmt *))); + std::copy(VariantDirectives.begin(), VariantDirectives.end(), VDs); + Dir->setVariantDirectives(VDs); + } + } return Dir; } OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses, + unsigned NumVariants, EmptyShell) { - return createEmptyDirective<OMPMetaDirective>(C, NumClauses, - /*HasAssociatedStmt=*/true, - /*NumChildren=*/1); + auto *Dir = createEmptyDirective<OMPMetaDirective>( + C, NumClauses, /*HasAssociatedStmt=*/true, /*NumChildren=*/1, + NumVariants); + OMPMetaDirective::allocateVariantStorage(C, Dir, NumVariants); + return Dir; } OMPParallelDirective *OMPParallelDirective::Create( diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 5d724e9d1d91e..b64a923722b2e 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2657,6 +2657,17 @@ void TextNodeDumper::VisitOMPExecutableDirective( OS << " openmp_standalone_directive"; } +void TextNodeDumper::VisitOMPMetaDirective(const OMPMetaDirective *D) { + unsigned NumVariants = D->getNumVariants(); + if (NumVariants > 0) { + OS << " variants=" << NumVariants; + ArrayRef<OpenMPDirectiveKind> DKs = D->getDirectiveKinds(); + for (unsigned I = 0; I < NumVariants; ++I) { + OS << " [" << I << "]: " << getOpenMPDirectiveName(DKs[I]); + } + } +} + void TextNodeDumper::VisitOMPDeclareReductionDecl( const OMPDeclareReductionDecl *D) { dumpName(D); diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index 18524a7c32b7a..67aee6023d1d1 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -2189,7 +2189,16 @@ void CodeGenFunction::EmitOMPParallelDirective(const OMPParallelDirective &S) { } void CodeGenFunction::EmitOMPMetaDirective(const OMPMetaDirective &S) { - EmitStmt(S.getIfStmt()); + // Runtime metadirectives are transformed to if-else in Sema. + // For compile-time selection, just emit the associated statement (the + // selected variant). + if (Stmt *IfStmt = S.getIfStmt()) { + EmitStmt(IfStmt); + return; + } + + // Compile-time selection: emit the selected variant's statement + EmitStmt(S.getAssociatedStmt()); } namespace { diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 62715da7966d0..fa9dac1d31af9 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -325,6 +325,21 @@ Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) { getCurScope(), DRD, IsCorrect); } +void Parser::skipToMatchingParen() { + int ParenDepth = 0; + while ((Tok.isNot(tok::r_paren) || ParenDepth != 0) && + Tok.isNot(tok::annot_pragma_openmp_end) && Tok.isNot(tok::eof)) { + if (Tok.is(tok::l_paren)) + ParenDepth++; + if (Tok.is(tok::r_paren)) { + if (ParenDepth == 0) + break; + ParenDepth--; + } + ConsumeAnyToken(); + } +} + void Parser::ParseOpenMPReductionInitializerForDecl(VarDecl *OmpPrivParm) { // Parse declarator '=' initializer. // If a '==' or '+=' is found, suggest a fixit to '='. @@ -2285,7 +2300,8 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( StmtResult Parser::ParseOpenMPExecutableDirective( ParsedStmtContext StmtCtx, OpenMPDirectiveKind DKind, SourceLocation Loc, - bool ReadDirectiveWithinMetadirective) { + bool ReadDirectiveWithinMetadirective, + bool ConsumeDirectiveOnlyInMetadirective) { assert(isOpenMPExecutableDirective(DKind) && "Unexpected directive category"); unsigned OMPVersion = Actions.getLangOpts().OpenMP; @@ -2358,9 +2374,15 @@ StmtResult Parser::ParseOpenMPExecutableDirective( // 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; + if (ConsumeDirectiveOnlyInMetadirective) { + // Runtime path: stop at ')' - let the caller handle it. + break; + } else { + // Compile-time path: consume everything to pragma end (old behavior). + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + break; + } } bool HasImplicitClause = false; if (ImplicitClauseAllowed && Tok.is(tok::l_paren)) { @@ -2410,7 +2432,11 @@ StmtResult Parser::ParseOpenMPExecutableDirective( // End location of the directive. EndLoc = Tok.getLocation(); // Consume final annot_pragma_openmp_end. - ConsumeAnnotationToken(); + // For compile-time metadirective path, we consumed up to + // annot_pragma_openmp_end and need to consume it. For runtime path, we + // stopped at ')'. + if (!ReadDirectiveWithinMetadirective || !ConsumeDirectiveOnlyInMetadirective) + ConsumeAnnotationToken(); if (DKind == OMPD_ordered) { // If the depend or doacross clause is specified, the ordered construct @@ -2479,7 +2505,8 @@ StmtResult Parser::ParseOpenMPExecutableDirective( StmtResult Parser::ParseOpenMPInformationalDirective( ParsedStmtContext StmtCtx, OpenMPDirectiveKind DKind, SourceLocation Loc, - bool ReadDirectiveWithinMetadirective) { + bool ReadDirectiveWithinMetadirective, + bool ConsumeDirectiveOnlyInMetadirective) { assert(isOpenMPInformationalDirective(DKind) && "Unexpected directive category"); @@ -2497,9 +2524,15 @@ StmtResult Parser::ParseOpenMPInformationalDirective( while (Tok.isNot(tok::annot_pragma_openmp_end)) { if (ReadDirectiveWithinMetadirective && Tok.is(tok::r_paren)) { - while (Tok.isNot(tok::annot_pragma_openmp_end)) - ConsumeAnyToken(); - break; + if (ConsumeDirectiveOnlyInMetadirective) { + // Runtime path: stop at ')' - let the caller handle it. + break; + } else { + // Compile-time path: consume everything to pragma end. + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + break; + } } OpenMPClauseKind CKind = Tok.isAnnotation() @@ -2518,7 +2551,10 @@ StmtResult Parser::ParseOpenMPInformationalDirective( } SourceLocation EndLoc = Tok.getLocation(); - ConsumeAnnotationToken(); + // For compile-time: consumed up to annot_pragma_openmp_end, need to consume + // it. For runtime: stopped at ')', don't consume anything. + if (!ReadDirectiveWithinMetadirective || !ConsumeDirectiveOnlyInMetadirective) + ConsumeAnnotationToken(); StmtResult AssociatedStmt; if (HasAssociatedStatement) { @@ -2542,7 +2578,8 @@ StmtResult Parser::ParseOpenMPInformationalDirective( } StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( - ParsedStmtContext StmtCtx, bool ReadDirectiveWithinMetadirective) { + ParsedStmtContext StmtCtx, bool ReadDirectiveWithinMetadirective, + bool ConsumeDirectiveOnlyInMetadirective) { if (!ReadDirectiveWithinMetadirective) assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) && "Not an OpenMP directive!"); @@ -2569,7 +2606,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( if (IsExecutable) { Directive = ParseOpenMPExecutableDirective( - StmtCtx, DKind, Loc, ReadDirectiveWithinMetadirective); + StmtCtx, DKind, Loc, ReadDirectiveWithinMetadirective, + ConsumeDirectiveOnlyInMetadirective); assert(!Directive.isUnset() && "Executable directive remained unprocessed"); return Directive; } @@ -2591,6 +2629,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( case OMPD_metadirective: { ConsumeToken(); SmallVector<VariantMatchInfo, 4> VMIs; + SmallVector<OMPTraitInfo *, 4> TraitInfos; // First iteration of parsing all clauses of metadirective. // This iteration only parses and collects all context selector ignoring the @@ -2648,19 +2687,12 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( } // 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(); + skipToMatchingParen(); + if (Tok.is(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::err_omp_expected_punc) + << getOpenMPClauseName(CKind) << 0; + TPA.Commit(); + return Directive; } // Parse ')' if (Tok.is(tok::r_paren)) @@ -2670,6 +2702,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( TI.getAsVariantMatchInfo(ASTContext, VMI); VMIs.push_back(VMI); + TraitInfos.push_back(&TI); } TPA.Revert(); @@ -2689,6 +2722,104 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( // A single match is returned for OpenMP 5.0 int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx); + // Check if we have any user conditions that need runtime or deferred + // evaluation: + // - Value-dependent conditions (template parameters): defer until + // instantiation. + // - Non-constant conditions (runtime variables): create runtime selection. + bool HasNonConstantUserCondition = false; + for (OMPTraitInfo *TI : TraitInfos) { + if (TI && TI->hasUserCondition()) { + // Found a user condition - check if it's non-constant + TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) { + if (!IsScore && E && + (E->isValueDependent() || !E->isEvaluatable(ASTContext))) { + HasNonConstantUserCondition = true; + return true; + } + return false; + }); + if (HasNonConstantUserCondition) + break; + } + } + + // If we have non-constant conditions, collect all variants and send to Sema + // Sema handles both runtime selection and re-evaluation after template + // instantiation. + if (HasNonConstantUserCondition) { + // Collect all variants with their conditions. + SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; + SmallVector<Expr *, 4> Conditions; + SmallVector<OpenMPClauseKind, 4> ClauseKinds; + SmallVector<Stmt *, 4> VariantDirectives; + + while (Tok.isNot(tok::annot_pragma_openmp_end)) { + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + SourceLocation ClauseLoc = ConsumeToken(); + + // Parse '('. + T.consumeOpen(); + + Expr *Condition = nullptr; + if (CKind == OMPC_when) { + OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo(); + parseOMPContextSelectors(ClauseLoc, TI); + + // Extract user condition if present. + TI.anyScoreOrCondition([&](Expr *&E, bool IsScore) { + if (!IsScore && E) { + Condition = E; + return true; + } + return false; + }); + // Parse ':'. + if (Tok.is(tok::colon)) + ConsumeAnyToken(); + } + + // Parse only the directive kind (not the full statement with body). + // The body will be shared across all variants and parsed after the + // metadirective. + OpenMPDirectiveKind DKind = OMPD_unknown; + Stmt *VariantDirective = nullptr; + + if (!Tok.is(tok::r_paren)) { + DKind = parseOpenMPDirectiveKind(*this); + skipToMatchingParen(); + } + + // Parse ')'. + if (Tok.is(tok::r_paren)) + T.consumeClose(); + + DirectiveKinds.push_back(DKind); + Conditions.push_back(Condition); + ClauseKinds.push_back(CKind); + VariantDirectives.push_back(VariantDirective); + } + + SourceLocation EndLoc = Tok.getLocation(); + ConsumeAnnotationToken(); + + StmtResult AssocStmt = ParseStatement(); + if (AssocStmt.isInvalid()) + return StmtError(); + + // For runtime metadirectives, pass empty VariantDirectives. + // The transformation to if-else happens in Sema. + SmallVector<Stmt *, 4> EmptyVariantDirectives(DirectiveKinds.size(), + nullptr); + + // Pass to Sema to build the runtime metadirective. + return Actions.OpenMP().ActOnOpenMPMetaDirective( + Loc, EndLoc, TraitInfos, ClauseKinds, Conditions, DirectiveKinds, + AssocStmt.get(), EmptyVariantDirectives); + } + int Idx = 0; // In OpenMP 5.0 metadirective is either replaced by another directive or // ignored. @@ -2699,15 +2830,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( 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(); - } + skipToMatchingParen(); // Parse ')' if (Tok.is(tok::r_paren)) T.consumeClose(); @@ -2912,7 +3036,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( case OMPD_assume: { ConsumeToken(); Directive = ParseOpenMPInformationalDirective( - StmtCtx, DKind, Loc, ReadDirectiveWithinMetadirective); + StmtCtx, DKind, Loc, ReadDirectiveWithinMetadirective, + ConsumeDirectiveOnlyInMetadirective); assert(!Directive.isUnset() && "Informational directive remains unprocessed"); return Directive; diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 0f45a21c5e461..7223c3d2904c0 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3764,6 +3764,216 @@ StmtResult SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses, AStmt); } +StmtResult SemaOpenMP::createParallelDirectiveForMetadirective( + Stmt *Body, SourceLocation StartLoc, SourceLocation EndLoc) { + DeclarationNameInfo DirName; + DSAStack->push(llvm::omp::OMPD_parallel, DirName, SemaRef.getCurScope(), + StartLoc); + + ActOnOpenMPRegionStart(llvm::omp::OMPD_parallel, SemaRef.getCurScope()); + + StmtResult CapturedBody = ActOnOpenMPRegionEnd(Body, /*Clauses=*/{}); + + if (!CapturedBody.isUsable()) { + DSAStack->pop(); + return StmtError(); + } + + StmtResult ParallelStmt = ActOnOpenMPParallelDirective( + /*Clauses=*/{}, CapturedBody.get(), StartLoc, EndLoc); + + DSAStack->pop(); + + if (!ParallelStmt.isUsable()) + return StmtError(); + + return ParallelStmt; +} + +StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( + SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<OpenMPClauseKind> ClauseKinds, + ArrayRef<Expr *> Conditions, ArrayRef<OpenMPDirectiveKind> DirectiveKinds, + Stmt *AStmt, ArrayRef<Stmt *> VariantDirectives) { + + assert(TraitInfos.size() == ClauseKinds.size() && + ClauseKinds.size() == Conditions.size() && + Conditions.size() == DirectiveKinds.size() && + "Mismatched array sizes for metadirective variants"); + assert((VariantDirectives.empty() || + VariantDirectives.size() == DirectiveKinds.size()) && + "VariantDirectives, if provided, must match DirectiveKinds size"); + + if (TraitInfos.empty()) + return StmtError(); + + ASTContext &Context = getASTContext(); + + // Check if all user conditions are constant. + bool AllConstant = true; + for (Expr *Condition : Conditions) { + if (Condition && !Condition->isEvaluatable(Context)) { + AllConstant = false; + break; + } + } + + if (AllConstant) { + // All conditions are compile-time constant - evaluate and select. + for (unsigned I = 0; I < Conditions.size(); ++I) { + bool ShouldSelect = false; + + if (ClauseKinds[I] == OMPC_when) { + // Evaluate the user condition. + if (Conditions[I]) { + Expr::EvalResult Result; + if (Conditions[I]->EvaluateAsInt(Result, Context)) { + ShouldSelect = Result.Val.getInt().getBoolValue(); + } + } + } else if (ClauseKinds[I] == OMPC_otherwise || + ClauseKinds[I] == OMPC_default) { + // otherwise/default always matches. + ShouldSelect = true; + } + if (ShouldSelect) { + OpenMPDirectiveKind SelectedKind = DirectiveKinds[I]; + + // If it's "nothing" or unknown, just return the statement. + if (SelectedKind == OMPD_unknown || SelectedKind == OMPD_nothing) + return AStmt; + + // Create the selected directive. + if (SelectedKind == OMPD_parallel) { + DeclarationNameInfo DirName; + DSAStack->push(OMPD_parallel, DirName, SemaRef.getCurScope(), + StartLoc); + StmtResult ParallelStmt = + createParallelDirectiveForMetadirective(AStmt, StartLoc, EndLoc); + DSAStack->pop(); + + if (ParallelStmt.isUsable()) + return ParallelStmt; + return StmtError(); + } else { + // TODO: Handle other directive kinds + // For now, just return the statement + return AStmt; + } + } + } + return AStmt; + } + + // At least one condition is runtime - transform to if-else AST. + // This allows normal OpenMP infrastructure to create CapturedStmts. + + // Push a dummy entry onto DSA stack so that ActOnOpenMPRegionStart has + // context. We use the metadirective itself as the dummy entry. + DeclarationNameInfo DirName; + DSAStack->push(OMPD_unknown, DirName, SemaRef.getCurScope(), StartLoc); + + // For now, handle simple case: 2 variants (when + otherwise). + if (DirectiveKinds.size() == 2 && Conditions[0] && !Conditions[1]) { + + // Build: if (condition) { #pragma omp directive1 { body } } else { body }. + Expr *IfCondition = Conditions[0]; + OpenMPDirectiveKind ThenDK = DirectiveKinds[0]; + OpenMPDirectiveKind ElseDK = DirectiveKinds[1]; + + // Check if both branches would execute the body identically (both are + // nothing/unknown). + // In this case, don't create an if-else - just execute the body once. + bool ThenIsNothing = (ThenDK == OMPD_unknown || ThenDK == OMPD_nothing); + bool ElseIsNothing = (ElseDK == OMPD_unknown || ElseDK == OMPD_nothing); + if (ThenIsNothing && ElseIsNothing) { + // Both branches would execute the same body - no need for if-else. + DSAStack->pop(); + return AStmt; + } + SmallVector<Stmt *, 2> CreatedVariantDirectives; + // Create the then-branch directive. + Stmt *ThenStmt = nullptr; + Stmt *ThenDirective = nullptr; + if (ThenDK == OMPD_parallel) { + StmtResult ParallelStmt = + createParallelDirectiveForMetadirective(AStmt, StartLoc, EndLoc); + if (!ParallelStmt.isUsable()) + return StmtError(); + ThenDirective = ParallelStmt.get(); + ThenStmt = ThenDirective; + } else if (ThenDK == OMPD_unknown || ThenDK == OMPD_nothing) { + ThenStmt = AStmt; + ThenDirective = nullptr; + } else { + // TODO: Handle other directive kinds. + ThenStmt = AStmt; + ThenDirective = nullptr; + } + CreatedVariantDirectives.push_back(ThenDirective); + + // Create the else-branch directive. + Stmt *ElseStmt = nullptr; + Stmt *ElseDirective = nullptr; + if (ElseDK == OMPD_unknown || ElseDK == OMPD_nothing) { + ElseStmt = AStmt; + ElseDirective = nullptr; + } else if (ElseDK == OMPD_parallel) { + StmtResult ParallelStmt = + createParallelDirectiveForMetadirective(AStmt, StartLoc, EndLoc); + if (!ParallelStmt.isUsable()) + return StmtError(); + ElseDirective = ParallelStmt.get(); + ElseStmt = ElseDirective; + } else { + // TODO: Handle other directive kinds. + ElseStmt = AStmt; + ElseDirective = nullptr; + } + CreatedVariantDirectives.push_back(ElseDirective); + // Build the IfStmt. + IfStmt *RuntimeIfStmt = + IfStmt::Create(Context, StartLoc, IfStatementKind::Ordinary, + /*Init=*/nullptr, /*Var=*/nullptr, IfCondition, StartLoc, + StartLoc, ThenStmt, StartLoc, ElseStmt); + + // Pop the dummy DSA stack entry. + DSAStack->pop(); + return OMPMetaDirective::Create(Context, StartLoc, EndLoc, /*Clauses=*/{}, + AStmt, RuntimeIfStmt, DirectiveKinds, + Conditions, CreatedVariantDirectives); + } + + // Pop the dummy DSA stack entry if we didn't handle the 2-variant case. + DSAStack->pop(); + // Fallback: At least one condition is runtime - create directive nodes if we + // have CapturedStmts. + SmallVector<Stmt *, 4> CreatedVariantDirectives; + for (unsigned I = 0; I < DirectiveKinds.size(); ++I) { + OpenMPDirectiveKind DK = DirectiveKinds[I]; + + if (DK == OMPD_unknown || DK == OMPD_nothing) { + // For nothing/unknown, no directive node needed. + CreatedVariantDirectives.push_back(nullptr); + } else if (I < VariantDirectives.size() && VariantDirectives[I]) { + // We have a CapturedStmt from the parser - create the directive. + Stmt *CapturedStmt = VariantDirectives[I]; + StmtResult DirectiveStmt = ActOnOpenMPExecutableDirective( + DK, /*DirName=*/{}, /*CancelRegion=*/OMPD_unknown, + /*Clauses=*/{}, CapturedStmt, StartLoc, EndLoc); + + CreatedVariantDirectives.push_back( + DirectiveStmt.isUsable() ? DirectiveStmt.get() : nullptr); + } else { + // No CapturedStmt provided - codegen will handle it + CreatedVariantDirectives.push_back(nullptr); + } + } + return OMPMetaDirective::Create(Context, StartLoc, EndLoc, /*Clauses=*/{}, + AStmt, /*IfStmt=*/nullptr, DirectiveKinds, + Conditions, CreatedVariantDirectives); +} + OMPRequiresDecl * SemaOpenMP::CheckOMPRequiresDecl(SourceLocation Loc, ArrayRef<OMPClause *> ClauseList) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 87cec16a76323..59b08013e946e 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2518,7 +2518,25 @@ void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) { VisitStmt(D); // The NumClauses field was read in ReadStmtFromStream. Record.skipInts(1); + unsigned NumVariants = Record.readInt(); VisitOMPExecutableDirective(D); + + // Read directive kinds and conditions. + SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; + for (unsigned I = 0; I < NumVariants; ++I) + DirectiveKinds.push_back( + static_cast<OpenMPDirectiveKind>(Record.readInt())); + std::copy(DirectiveKinds.begin(), DirectiveKinds.end(), + D->getDirectiveKinds()); + + for (unsigned I = 0; I < NumVariants; ++I) + D->getConditions()[I] = Record.readSubExpr(); + + // Read variant directives if present. + if (D->getVariantDirectives()) { + for (unsigned I = 0; I < NumVariants; ++I) + D->getVariantDirectives()[I] = Record.readSubStmt(); + } } void ASTStmtReader::VisitOMPParallelDirective(OMPParallelDirective *D) { @@ -3692,7 +3710,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case STMT_OMP_META_DIRECTIVE: S = OMPMetaDirective::CreateEmpty( - Context, Record[ASTStmtReader::NumStmtFields], Empty); + Context, Record[ASTStmtReader::NumStmtFields], + Record[ASTStmtReader::NumStmtFields + 1], Empty); break; case STMT_OMP_PARALLEL_DIRECTIVE: diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 70477f4cf4001..b9be4bafe7770 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2531,7 +2531,22 @@ void ASTStmtWriter::VisitOMPLoopDirective(OMPLoopDirective *D) { void ASTStmtWriter::VisitOMPMetaDirective(OMPMetaDirective *D) { VisitStmt(D); Record.push_back(D->getNumClauses()); + Record.push_back(D->getNumVariants()); VisitOMPExecutableDirective(D); + // Write directive kinds and conditions. + const OMPMetaDirective *ConstD = D; + for (auto DK : ConstD->getDirectiveKinds()) + Record.push_back(static_cast<unsigned>(DK)); + + for (auto *E : ConstD->getConditions()) + Record.AddStmt(E); + + // Write variant directives if present. + ArrayRef<Stmt *> Variants = ConstD->getVariantDirectives(); + if (!Variants.empty()) { + for (auto *S : Variants) + Record.AddStmt(S); + } Code = serialization::STMT_OMP_META_DIRECTIVE; } diff --git a/clang/test/OpenMP/metadirective_runtime_user_condition_ast.cpp b/clang/test/OpenMP/metadirective_runtime_user_condition_ast.cpp new file mode 100644 index 0000000000000..51b8ca5be1cce --- /dev/null +++ b/clang/test/OpenMP/metadirective_runtime_user_condition_ast.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \ +// RUN: -ast-dump %s | FileCheck %s + +// expected-no-diagnostics + +// CHECK-LABEL: FunctionDecl {{.*}} test_runtime_condition +// CHECK: OMPMetaDirective {{.*}} variants=2 +// CHECK-NEXT: IfStmt {{.*}} has_else +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'flag' 'int' +// CHECK-NEXT: OMPParallelDirective +// CHECK-NEXT: CapturedStmt +// CHECK-NEXT: CapturedDecl +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl {{.*}} x 'int' +// CHECK: CompoundStmt +// CHECK-NEXT: DeclStmt +void test_runtime_condition(int flag) { + #pragma omp metadirective when(user={condition(flag)}: parallel) otherwise() + { + int x = 0; + } +} + +// CHECK-LABEL: FunctionDecl {{.*}} test_both_nothing +// CHECK-NOT: IfStmt +// CHECK: CompoundStmt +// CHECK: DeclStmt +// CHECK-NEXT: VarDecl {{.*}} x 'int' +void test_both_nothing(int flag) { +#pragma omp metadirective when(user={condition(flag)}: nothing) otherwise() + { + int x = 0; + } +} diff --git a/clang/test/OpenMP/metadirective_runtime_user_condition_codegen.cpp b/clang/test/OpenMP/metadirective_runtime_user_condition_codegen.cpp new file mode 100644 index 0000000000000..c95702b435538 --- /dev/null +++ b/clang/test/OpenMP/metadirective_runtime_user_condition_codegen.cpp @@ -0,0 +1,140 @@ +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \ +// RUN: -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s + +// expected-no-diagnostics + +// Test runtime selection with non-constant user condition +// Runtime metadirectives are transformed to if-else in Sema + +void test_runtime_condition(int flag) { +// CHECK-LABEL: define {{.*}} void @_Z22test_runtime_conditioni(i32 noundef %flag) +// CHECK: %flag.addr = alloca i32, align 4 +// CHECK: %x = alloca i32, align 4 +// CHECK: store i32 %flag, ptr %flag.addr, align 4 +// CHECK: [[LOAD:%.*]] = load i32, ptr %flag.addr, align 4 +// CHECK: [[TOBOOL:%.*]] = icmp ne i32 [[LOAD]], 0 +// CHECK: br i1 [[TOBOOL]], label %if.then, label %if.else +// CHECK: if.then: +// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @{{[0-9]+}}, i32 0, ptr @_Z22test_runtime_conditioni.omp_outlined) +// CHECK: br label %if.end +// CHECK: if.else: +// CHECK: store i32 0, ptr %x, align 4 +// CHECK: br label %if.end +// CHECK: if.end: +// CHECK: ret void + #pragma omp metadirective when(user={condition(flag)}: parallel) otherwise() + { + int x = 0; + } +} +// CHECK: define internal void @_Z22test_runtime_conditioni.omp_outlined(ptr {{.*}}, ptr {{.*}}) + +void test_runtime_condition_two_variants(int flag) { +// CHECK-LABEL: define {{.*}} void @_Z35test_runtime_condition_two_variantsi(i32 noundef %flag) +// CHECK: %flag.addr = alloca i32, align 4 +// CHECK: %i = alloca i32, align 4 +// CHECK: store i32 %flag, ptr %flag.addr, align 4 +// CHECK: [[LOAD:%.*]] = load i32, ptr %flag.addr, align 4 +// CHECK: [[TOBOOL:%.*]] = icmp ne i32 [[LOAD]], 0 +// CHECK: br i1 [[TOBOOL]], label %if.then, label %if.else +// CHECK: if.then: +// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @{{[0-9]+}}, i32 0, ptr @_Z35test_runtime_condition_two_variantsi.omp_outlined) +// CHECK: br label %if.end +// CHECK: if.else: +// CHECK: store i32 0, ptr %i, align 4 +// CHECK: br label %for.cond +// CHECK: for.cond: +// CHECK: [[LOAD_I:%.*]] = load i32, ptr %i, align 4 +// CHECK: [[CMP:%.*]] = icmp slt i32 [[LOAD_I]], 10 +// CHECK: br i1 [[CMP]], label %for.body, label %for.end +// CHECK: for.body: +// CHECK: br label %for.inc +// CHECK: for.inc: +// CHECK: [[LOAD_I2:%.*]] = load i32, ptr %i, align 4 +// CHECK: [[INC:%.*]] = add nsw i32 [[LOAD_I2]], 1 +// CHECK: store i32 [[INC]], ptr %i, align 4 +// CHECK: br label %for.cond +// CHECK: for.end: +// CHECK: br label %if.end +// CHECK: if.end: +// CHECK: ret void + #pragma omp metadirective when(user={condition(flag)}: parallel) otherwise(parallel for) + for (int i = 0; i < 10; i++) + ; +} +// CHECK: define internal void @_Z35test_runtime_condition_two_variantsi.omp_outlined(ptr {{.*}}, ptr {{.*}}) + +// Test with clauses on the parallel directive +void test_runtime_with_clauses(int flag, int n) { +// CHECK-LABEL: define {{.*}}void @_Z25test_runtime_with_clausesii(i32 noundef %flag, i32 noundef %n) +// CHECK: %flag.addr = alloca i32, align 4 +// CHECK: %n.addr = alloca i32, align 4 +// CHECK: store i32 %flag, ptr %flag.addr, align 4 +// CHECK: store i32 %n, ptr %n.addr, align 4 +// CHECK: [[LOAD:%.*]] = load i32, ptr %flag.addr, align 4 +// CHECK: [[TOBOOL:%.*]] = icmp ne i32 [[LOAD]], 0 +// CHECK: br i1 [[TOBOOL]], label %if.then, label %if.else +// CHECK: if.then: +// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call +// CHECK: br label %if.end +// CHECK: if.else: +// CHECK: br label %if.end +// CHECK: if.end: +// CHECK: ret void +#pragma omp metadirective when(user={condition(flag)}: parallel num_threads(n)) otherwise() + { + int x = 0; + } +} +// CHECK: define internal void @_Z25test_runtime_with_clausesii.omp_outlined(ptr {{.*}}, ptr {{.*}}) + + void test_runtime_multiple_stmts(int flag) { +// CHECK-LABEL: define {{.*}}void @_Z27test_runtime_multiple_stmtsi(i32 noundef %flag) +// CHECK: [[LOAD:%.*]] = load i32, ptr %flag.addr, align 4 +// CHECK: [[TOBOOL:%.*]] = icmp ne i32 [[LOAD]], 0 +// CHECK: br i1 [[TOBOOL]], label %if.then, label %if.else +// CHECK: if.then: +// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call +// CHECK: br label %if.end +// CHECK: if.else: +// CHECK: store i32 1, ptr %x +// CHECK: store i32 2, ptr %y +// CHECK: store i32 3, ptr %z +// CHECK: br label %if.end +// CHECK: if.end: +// CHECK: ret void + #pragma omp metadirective when(user={condition(flag)}: parallel) otherwise() + { + int x = 1; + int y = 2; + int z = 3; + } +} +// CHECK:define internal void @_Z27test_runtime_multiple_stmtsi.omp_outlined(ptr {{.*}}, ptr {{.*}}) + +// Test that compile-time constant condition still works (no if-else generated) +void test_compile_time_constant(void) { +// CHECK-LABEL: define {{.*}}void @_Z26test_compile_time_constantv() +// CHECK-NOT: br i1 +// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call +// CHECK: ret void + #pragma omp metadirective when(user={condition(1)}: parallel) otherwise() + { + int x = 0; + } +} +// CHECK: define internal void @_Z26test_compile_time_constantv.omp_outlined(ptr {{.*}}, ptr {{.*}}) + +// Test when both branches are "nothing" - no if-else is generated +void test_runtime_otherwise(int flag) { +// CHECK-LABEL: define {{.*}} @_Z22test_runtime_otherwisei +// CHECK: %flag.addr = alloca i32 +// CHECK: %x = alloca i32 +// CHECK: store i32 %flag, ptr %flag.addr +// CHECK: store i32 0, ptr %x +// CHECK: ret void + #pragma omp metadirective when(user={condition(flag)}: nothing) otherwise() + { + int x = 0; + } +} >From baed54545dac94c0091128d607c9fe9113c11531 Mon Sep 17 00:00:00 2001 From: Ammarguellat <[email protected]> Date: Wed, 15 Jul 2026 08:34:52 -0700 Subject: [PATCH 2/4] Addressed issues raised in previous comments --- clang/include/clang/AST/OpenMPClause.h | 1 + clang/include/clang/AST/StmtOpenMP.h | 87 ++++++++++++++++++----- clang/include/clang/Sema/SemaOpenMP.h | 3 +- clang/lib/AST/StmtOpenMP.cpp | 65 ++++++++++------- clang/lib/Parse/ParseOpenMP.cpp | 77 +++++++++++++++----- clang/lib/Sema/SemaOpenMP.cpp | 29 ++++---- clang/lib/Serialization/ASTReaderStmt.cpp | 6 +- clang/lib/Serialization/ASTWriterStmt.cpp | 2 +- 8 files changed, 187 insertions(+), 83 deletions(-) diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index e5791347926d8..b5486d24f0d9d 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -9995,6 +9995,7 @@ class OMPChildren final friend TrailingObjects; friend class OMPClauseReader; friend class OMPExecutableDirective; + friend class OMPMetaDirective; template <typename T> friend class OMPDeclarativeDirective; /// Numbers of clauses. diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index c13b6527a916e..5e9c6dacc31cd 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6377,33 +6377,81 @@ class OMPMetaDirective final : public OMPExecutableDirective { friend class ASTStmtWriter; friend class OMPExecutableDirective; unsigned NumVariants; - OpenMPDirectiveKind *DirectiveKinds; - Expr **Conditions; - Stmt **VariantDirectives; OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc, unsigned NumVariants) : OMPExecutableDirective(OMPMetaDirectiveClass, llvm::omp::OMPD_metadirective, StartLoc, EndLoc), - NumVariants(NumVariants), DirectiveKinds(nullptr), Conditions(nullptr), - VariantDirectives(nullptr) {} + NumVariants(NumVariants) {} explicit OMPMetaDirective(unsigned NumVariants) : OMPExecutableDirective(OMPMetaDirectiveClass, llvm::omp::OMPD_metadirective, SourceLocation(), SourceLocation()), - NumVariants(NumVariants), DirectiveKinds(nullptr), Conditions(nullptr), - VariantDirectives(nullptr) {} + NumVariants(NumVariants) {} void setIfStmt(Stmt *S) { Data->getChildren()[0] = S; } - void setDirectiveKinds(OpenMPDirectiveKind *DK) { DirectiveKinds = DK; } - void setConditions(Expr **C) { Conditions = C; } - void setVariantDirectives(Stmt **VD) { VariantDirectives = VD; } - static void allocateVariantStorage(const ASTContext &C, OMPMetaDirective *Dir, - unsigned NumVariants); - OpenMPDirectiveKind *getDirectiveKinds() { return DirectiveKinds; } - Expr **getConditions() { return Conditions; } - Stmt **getVariantDirectives() { return VariantDirectives; } + // Helper to calculate the aligned offset to Conditions array. + size_t offsetToConditions() const { + return llvm::alignTo(NumVariants * sizeof(OpenMPDirectiveKind), + alignof(Expr *)); + } + + // Helper to calculate total size needed for trailing arrays. + static size_t sizeOfTrailingArrays(unsigned NumVariants) { + size_t DirectiveKindsSize = NumVariants * sizeof(OpenMPDirectiveKind); + size_t ConditionsSize = NumVariants * sizeof(Expr *); + size_t VariantDirectivesSize = NumVariants * sizeof(Stmt *); + return llvm::alignTo(DirectiveKindsSize, alignof(Expr *)) + ConditionsSize + + VariantDirectivesSize; + } + + // Helper to allocate memory for OMPMetaDirective with trailing arrays and + // OMPChildren. Returns a pair of (main memory, children memory). + static std::pair<void *, void *> allocateMemory(const ASTContext &C, + unsigned NumVariants, + unsigned NumClauses, + unsigned NumChildren); + + // Helper to get the trailing storage for arrays. + // Layout: + // [OMPMetaDirective][DirectiveKinds...][padding][Conditions...][VariantDirectives...] + // Each array is properly aligned for its element type. + OpenMPDirectiveKind *getTrailingDirectiveKinds() { + return reinterpret_cast<OpenMPDirectiveKind *>(this + 1); + } + + const OpenMPDirectiveKind *getTrailingDirectiveKinds() const { + return reinterpret_cast<const OpenMPDirectiveKind *>(this + 1); + } + + Expr **getTrailingConditions() { + return reinterpret_cast<Expr **>( + reinterpret_cast<char *>(getTrailingDirectiveKinds()) + + offsetToConditions()); + } + + const Expr *const *getTrailingConditions() const { + return reinterpret_cast<const Expr *const *>( + reinterpret_cast<const char *>(getTrailingDirectiveKinds()) + + offsetToConditions()); + } + + Stmt **getTrailingVariantDirectives() { + return reinterpret_cast<Stmt **>(getTrailingConditions() + NumVariants); + } + + const Stmt *const *getTrailingVariantDirectives() const { + return reinterpret_cast<const Stmt *const *>(getTrailingConditions() + + NumVariants); + } + + OpenMPDirectiveKind *getDirectiveKinds() { + return getTrailingDirectiveKinds(); + } + Expr **getConditions() { return getTrailingConditions(); } + + Stmt **getVariantDirectives() { return getTrailingVariantDirectives(); } public: static OMPMetaDirective * @@ -6418,15 +6466,18 @@ class OMPMetaDirective final : public OMPExecutableDirective { unsigned getNumVariants() const { return NumVariants; } ArrayRef<OpenMPDirectiveKind> getDirectiveKinds() const { - return ArrayRef<OpenMPDirectiveKind>(DirectiveKinds, NumVariants); + return ArrayRef<OpenMPDirectiveKind>(getTrailingDirectiveKinds(), + NumVariants); } ArrayRef<Expr *> getConditions() const { - return ArrayRef<Expr *>(Conditions, NumVariants); + return ArrayRef<Expr *>(const_cast<Expr **>(getTrailingConditions()), + NumVariants); } ArrayRef<Stmt *> getVariantDirectives() const { - return ArrayRef<Stmt *>(VariantDirectives, NumVariants); + return ArrayRef<Stmt *>(const_cast<Stmt **>(getTrailingVariantDirectives()), + NumVariants); } child_range children() { diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 87297a7e11854..2dabdfd8857ad 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -225,10 +225,11 @@ class SemaOpenMP : public SemaBase { ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AStmt, ArrayRef<Stmt *> VariantDirectives = {}); - private: +private: StmtResult createParallelDirectiveForMetadirective(Stmt *Body, SourceLocation StartLoc, SourceLocation EndLoc); + public: // OpenMP directives and clauses. /// Called on correct id-expression from the '#pragma omp diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index 1bdf50be71a7e..f055b9935e37e 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -260,19 +260,19 @@ void OMPLoopDirective::setFinalsConditions(ArrayRef<Expr *> A) { llvm::copy(A, getFinalsConditions().begin()); } -void OMPMetaDirective::allocateVariantStorage(const ASTContext &C, - OMPMetaDirective *Dir, - unsigned NumVariants) { - if (NumVariants == 0) - return; - - auto *DKs = static_cast<OpenMPDirectiveKind *>(C.Allocate( - NumVariants * sizeof(OpenMPDirectiveKind), alignof(OpenMPDirectiveKind))); - auto **Conds = static_cast<Expr **>( - C.Allocate(NumVariants * sizeof(Expr *), alignof(Expr *))); - - Dir->setDirectiveKinds(DKs); - Dir->setConditions(Conds); +std::pair<void *, void *> +OMPMetaDirective::allocateMemory(const ASTContext &C, unsigned NumVariants, + unsigned NumClauses, unsigned NumChildren) { + size_t ArraysSize = OMPMetaDirective::sizeOfTrailingArrays(NumVariants); + void *Mem = C.Allocate( + sizeof(OMPMetaDirective) + ArraysSize + + llvm::alignTo(OMPChildren::totalSizeToAlloc<OMPClause *, Stmt *>( + NumClauses, NumChildren), + alignof(OMPChildren)), + alignof(OMPMetaDirective)); + void *ChildrenMem = + reinterpret_cast<char *>(Mem) + sizeof(OMPMetaDirective) + ArraysSize; + return {Mem, ChildrenMem}; } OMPMetaDirective *OMPMetaDirective::Create( @@ -286,22 +286,29 @@ OMPMetaDirective *OMPMetaDirective::Create( DirectiveKinds.size() == VariantDirectives.size()) && "VariantDirectives, if provided, must match DirectiveKinds size"); unsigned NumVariants = DirectiveKinds.size(); - auto *Dir = createDirective<OMPMetaDirective>(C, Clauses, AssociatedStmt, - /*NumChildren=*/1, StartLoc, - EndLoc, NumVariants); + + // Allocate memory for OMPMetaDirective with trailing arrays and OMPChildren. + // Layout: + // [OMPMetaDirective][DirectiveKinds...][padding][Conditions...][VariantDirectives...][OMPChildren] + auto [Mem, ChildrenMem] = + allocateMemory(C, NumVariants, Clauses.size(), + /*NumChildren=*/1 + (AssociatedStmt ? 1 : 0)); + auto *Data = OMPChildren::Create(ChildrenMem, Clauses, AssociatedStmt, + /*NumChildren=*/1); + auto *Dir = new (Mem) OMPMetaDirective(StartLoc, EndLoc, NumVariants); + Dir->Data = Data; Dir->setIfStmt(IfStmt); - OMPMetaDirective::allocateVariantStorage(C, Dir, NumVariants); if (NumVariants > 0) { std::copy(DirectiveKinds.begin(), DirectiveKinds.end(), Dir->getDirectiveKinds()); std::copy(Conditions.begin(), Conditions.end(), Dir->getConditions()); - if (!VariantDirectives.empty()) { - auto **VDs = static_cast<Stmt **>( - C.Allocate(NumVariants * sizeof(Stmt *), alignof(Stmt *))); - std::copy(VariantDirectives.begin(), VariantDirectives.end(), VDs); - Dir->setVariantDirectives(VDs); - } + if (!VariantDirectives.empty()) + std::copy(VariantDirectives.begin(), VariantDirectives.end(), + Dir->getVariantDirectives()); + else + // Initialize VariantDirectives to nullptr. + std::fill_n(Dir->getVariantDirectives(), NumVariants, nullptr); } return Dir; } @@ -310,10 +317,14 @@ OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses, unsigned NumVariants, EmptyShell) { - auto *Dir = createEmptyDirective<OMPMetaDirective>( - C, NumClauses, /*HasAssociatedStmt=*/true, /*NumChildren=*/1, - NumVariants); - OMPMetaDirective::allocateVariantStorage(C, Dir, NumVariants); + // Allocate memory for empty OMPMetaDirective. + auto [Mem, ChildrenMem] = + allocateMemory(C, NumVariants, NumClauses, /*NumChildren=*/1 + 1); + auto *Data = + OMPChildren::CreateEmpty(ChildrenMem, NumClauses, + /*HasAssociatedStmt=*/true, /*NumChildren=*/1); + auto *Dir = new (Mem) OMPMetaDirective(NumVariants); + Dir->Data = Data; return Dir; } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index fa9dac1d31af9..57298aae9f55e 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2686,8 +2686,9 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( } } - // Skip Directive for now. We will parse directive in the second iteration - skipToMatchingParen(); + // Skip Directive for now. We will parse directive in the second + // iteration. + skipToMatchingParen(); if (Tok.is(tok::annot_pragma_openmp_end)) { Diag(Tok, diag::err_omp_expected_punc) << getOpenMPClauseName(CKind) << 0; @@ -2722,20 +2723,21 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( // A single match is returned for OpenMP 5.0 int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx); - // Check if we have any user conditions that need runtime or deferred - // evaluation: - // - Value-dependent conditions (template parameters): defer until - // instantiation. - // - Non-constant conditions (runtime variables): create runtime selection. + // Check if we have user conditions that are not simple IntegerLiterals. + // For simple IntegerLiterals (like condition(0) or condition(1)), we can + // evaluate them here. For anything else (variables, templates, complex + // expressions), we need to send to Sema for proper handling. bool HasNonConstantUserCondition = false; for (OMPTraitInfo *TI : TraitInfos) { if (TI && TI->hasUserCondition()) { - // Found a user condition - check if it's non-constant + // Check if all user conditions are simple IntegerLiterals. TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) { - if (!IsScore && E && - (E->isValueDependent() || !E->isEvaluatable(ASTContext))) { - HasNonConstantUserCondition = true; - return true; + if (!IsScore && E) { + // If it's not a simple IntegerLiteral, we need Sema + if (!isa<IntegerLiteral>(E->IgnoreParenImpCasts())) { + HasNonConstantUserCondition = true; + return true; + } } return false; }); @@ -2743,10 +2745,53 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( break; } } - - // If we have non-constant conditions, collect all variants and send to Sema - // Sema handles both runtime selection and re-evaluation after template - // instantiation. + // For simple IntegerLiteral user conditions, re-evaluate BestIdx + // considering them. If the current BestIdx has a false user condition, find + // the next match. + if (!HasNonConstantUserCondition) { + // Helper to evaluate IntegerLiteral user conditions. + // All conditions were verified to be IntegerLiterals above. + auto evaluateUserCondition = [](OMPTraitInfo *TI) -> bool { + bool Result = true; + TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) { + if (!IsScore && E) { + if (auto *IL = dyn_cast<IntegerLiteral>(E->IgnoreParenImpCasts())) { + Result = IL->getValue().getBoolValue(); + return true; + } + llvm_unreachable("All user conditions verified as IntegerLiterals"); + } + return false; + }); + return Result; + }; + if (BestIdx >= 0 && BestIdx < static_cast<int>(TraitInfos.size())) { + OMPTraitInfo *SelectedTI = TraitInfos[BestIdx]; + if (SelectedTI && SelectedTI->hasUserCondition()) { + if (!evaluateUserCondition(SelectedTI)) { + // Current BestIdx has false condition, try to find next match. + BestIdx = -1; + for (int I = 0; I < static_cast<int>(VMIs.size()); ++I) { + if (I < static_cast<int>(TraitInfos.size())) { + OMPTraitInfo *TI = TraitInfos[I]; + if (TI && TI->hasUserCondition()) { + if (evaluateUserCondition(TI)) { + BestIdx = I; + break; + } + } else { + // No user condition or always-true context selector. + BestIdx = I; + break; + } + } + } + } + } + } + } + // If we have non-constant user conditions, collect all variants and send to + // Sema. if (HasNonConstantUserCondition) { // Collect all variants with their conditions. SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 7223c3d2904c0..15835af7545ce 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3796,6 +3796,7 @@ StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( ArrayRef<Expr *> Conditions, ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AStmt, ArrayRef<Stmt *> VariantDirectives) { + assert(!TraitInfos.empty() && "TraitInfos should not be empty"); assert(TraitInfos.size() == ClauseKinds.size() && ClauseKinds.size() == Conditions.size() && Conditions.size() == DirectiveKinds.size() && @@ -3804,9 +3805,6 @@ StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( VariantDirectives.size() == DirectiveKinds.size()) && "VariantDirectives, if provided, must match DirectiveKinds size"); - if (TraitInfos.empty()) - return StmtError(); - ASTContext &Context = getASTContext(); // Check if all user conditions are constant. @@ -3843,23 +3841,20 @@ StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( if (SelectedKind == OMPD_unknown || SelectedKind == OMPD_nothing) return AStmt; - // Create the selected directive. - if (SelectedKind == OMPD_parallel) { - DeclarationNameInfo DirName; - DSAStack->push(OMPD_parallel, DirName, SemaRef.getCurScope(), - StartLoc); - StmtResult ParallelStmt = - createParallelDirectiveForMetadirective(AStmt, StartLoc, EndLoc); + // Create the selected directive - need to create CapturedStmt first. + DeclarationNameInfo DirName; + DSAStack->push(SelectedKind, DirName, SemaRef.getCurScope(), StartLoc); + ActOnOpenMPRegionStart(SelectedKind, SemaRef.getCurScope()); + StmtResult CapturedBody = ActOnOpenMPRegionEnd(AStmt, /*Clauses=*/{}); + if (!CapturedBody.isUsable()) { DSAStack->pop(); - - if (ParallelStmt.isUsable()) - return ParallelStmt; return StmtError(); - } else { - // TODO: Handle other directive kinds - // For now, just return the statement - return AStmt; } + StmtResult DirectiveStmt = ActOnOpenMPExecutableDirective( + SelectedKind, DirName, /*CancelRegion=*/OMPD_unknown, + /*Clauses=*/{}, CapturedBody.get(), StartLoc, EndLoc); + DSAStack->pop(); + return DirectiveStmt; } } return AStmt; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 59b08013e946e..edd98c9d0aaa5 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2516,9 +2516,9 @@ void ASTStmtReader::VisitOMPLoopDirective(OMPLoopDirective *D) { void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) { VisitStmt(D); - // The NumClauses field was read in ReadStmtFromStream. - Record.skipInts(1); - unsigned NumVariants = Record.readInt(); + // The NumClauses and NumVariants fields were read in ReadStmtFromStream. + Record.skipInts(2); + unsigned NumVariants = D->getNumVariants(); VisitOMPExecutableDirective(D); // Read directive kinds and conditions. diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index b9be4bafe7770..b1f972c4d7118 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2534,7 +2534,7 @@ void ASTStmtWriter::VisitOMPMetaDirective(OMPMetaDirective *D) { Record.push_back(D->getNumVariants()); VisitOMPExecutableDirective(D); // Write directive kinds and conditions. - const OMPMetaDirective *ConstD = D; + const OMPMetaDirective *ConstD = D; for (auto DK : ConstD->getDirectiveKinds()) Record.push_back(static_cast<unsigned>(DK)); >From 6a6cf37f1e4c289d39bb3f82c41c6c950bb89dc3 Mon Sep 17 00:00:00 2001 From: Ammarguellat <[email protected]> Date: Fri, 17 Jul 2026 14:24:30 -0700 Subject: [PATCH 3/4] Changed raw pointers to Mutable --- clang/include/clang/AST/StmtOpenMP.h | 63 +++++++++++++---------- clang/lib/AST/StmtOpenMP.cpp | 9 ++-- clang/lib/Serialization/ASTReaderStmt.cpp | 4 +- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index 5e9c6dacc31cd..7142fdde36ca5 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6417,41 +6417,53 @@ class OMPMetaDirective final : public OMPExecutableDirective { // Layout: // [OMPMetaDirective][DirectiveKinds...][padding][Conditions...][VariantDirectives...] // Each array is properly aligned for its element type. - OpenMPDirectiveKind *getTrailingDirectiveKinds() { - return reinterpret_cast<OpenMPDirectiveKind *>(this + 1); + MutableArrayRef<OpenMPDirectiveKind> getTrailingDirectiveKinds() { + return MutableArrayRef<OpenMPDirectiveKind>( + reinterpret_cast<OpenMPDirectiveKind *>(this + 1), NumVariants); } - const OpenMPDirectiveKind *getTrailingDirectiveKinds() const { - return reinterpret_cast<const OpenMPDirectiveKind *>(this + 1); + const ArrayRef<OpenMPDirectiveKind> getTrailingDirectiveKinds() const { + return ArrayRef<OpenMPDirectiveKind>( + reinterpret_cast<const OpenMPDirectiveKind *>(this + 1), NumVariants); } - Expr **getTrailingConditions() { - return reinterpret_cast<Expr **>( - reinterpret_cast<char *>(getTrailingDirectiveKinds()) + - offsetToConditions()); + MutableArrayRef<Expr *> getTrailingConditions() { + return MutableArrayRef<Expr *>( + reinterpret_cast<Expr **>(reinterpret_cast<char *>(this + 1) + + offsetToConditions()), + NumVariants); } - const Expr *const *getTrailingConditions() const { - return reinterpret_cast<const Expr *const *>( - reinterpret_cast<const char *>(getTrailingDirectiveKinds()) + - offsetToConditions()); + ArrayRef<Expr *> getTrailingConditions() const { + return ArrayRef<Expr *>( + reinterpret_cast<Expr *const *>( + reinterpret_cast<const char *>(this + 1) + offsetToConditions()), + NumVariants); } - Stmt **getTrailingVariantDirectives() { - return reinterpret_cast<Stmt **>(getTrailingConditions() + NumVariants); + MutableArrayRef<Stmt *> getTrailingVariantDirectives() { + auto Conditions = getTrailingConditions(); + return MutableArrayRef<Stmt *>( + reinterpret_cast<Stmt **>(Conditions.data() + NumVariants), + NumVariants); } - const Stmt *const *getTrailingVariantDirectives() const { - return reinterpret_cast<const Stmt *const *>(getTrailingConditions() + - NumVariants); + ArrayRef<Stmt *> getTrailingVariantDirectives() const { + auto Conditions = getTrailingConditions(); + return ArrayRef<Stmt *>( + reinterpret_cast<Stmt *const *>(Conditions.data() + NumVariants), + NumVariants); } - OpenMPDirectiveKind *getDirectiveKinds() { + MutableArrayRef<OpenMPDirectiveKind> getDirectiveKinds() { return getTrailingDirectiveKinds(); } - Expr **getConditions() { return getTrailingConditions(); } - Stmt **getVariantDirectives() { return getTrailingVariantDirectives(); } + MutableArrayRef<Expr *> getConditions() { return getTrailingConditions(); } + + MutableArrayRef<Stmt *> getVariantDirectives() { + return getTrailingVariantDirectives(); + } public: static OMPMetaDirective * @@ -6466,18 +6478,13 @@ class OMPMetaDirective final : public OMPExecutableDirective { unsigned getNumVariants() const { return NumVariants; } ArrayRef<OpenMPDirectiveKind> getDirectiveKinds() const { - return ArrayRef<OpenMPDirectiveKind>(getTrailingDirectiveKinds(), - NumVariants); + return getTrailingDirectiveKinds(); } - ArrayRef<Expr *> getConditions() const { - return ArrayRef<Expr *>(const_cast<Expr **>(getTrailingConditions()), - NumVariants); - } + ArrayRef<Expr *> getConditions() const { return getTrailingConditions(); } ArrayRef<Stmt *> getVariantDirectives() const { - return ArrayRef<Stmt *>(const_cast<Stmt **>(getTrailingVariantDirectives()), - NumVariants); + return getTrailingVariantDirectives(); } child_range children() { diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index f055b9935e37e..b37dc5179fc27 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -300,15 +300,16 @@ OMPMetaDirective *OMPMetaDirective::Create( Dir->setIfStmt(IfStmt); if (NumVariants > 0) { std::copy(DirectiveKinds.begin(), DirectiveKinds.end(), - Dir->getDirectiveKinds()); - std::copy(Conditions.begin(), Conditions.end(), Dir->getConditions()); + Dir->getDirectiveKinds().begin()); + std::copy(Conditions.begin(), Conditions.end(), + Dir->getConditions().begin()); if (!VariantDirectives.empty()) std::copy(VariantDirectives.begin(), VariantDirectives.end(), - Dir->getVariantDirectives()); + Dir->getVariantDirectives().begin()); else // Initialize VariantDirectives to nullptr. - std::fill_n(Dir->getVariantDirectives(), NumVariants, nullptr); + std::fill_n(Dir->getVariantDirectives().data(), NumVariants, nullptr); } return Dir; } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index edd98c9d0aaa5..a38a809793244 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2527,13 +2527,13 @@ void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) { DirectiveKinds.push_back( static_cast<OpenMPDirectiveKind>(Record.readInt())); std::copy(DirectiveKinds.begin(), DirectiveKinds.end(), - D->getDirectiveKinds()); + D->getDirectiveKinds().begin()); for (unsigned I = 0; I < NumVariants; ++I) D->getConditions()[I] = Record.readSubExpr(); // Read variant directives if present. - if (D->getVariantDirectives()) { + if (!D->getVariantDirectives().empty()) { for (unsigned I = 0; I < NumVariants; ++I) D->getVariantDirectives()[I] = Record.readSubStmt(); } >From 7c709ef21ce9f41f0dda0a16a63ad9834ca0dc6c Mon Sep 17 00:00:00 2001 From: Ammarguellat <[email protected]> Date: Mon, 27 Jul 2026 06:42:12 -0700 Subject: [PATCH 4/4] Applied suggestions and removed sema analysis from parser --- clang/include/clang/AST/StmtOpenMP.h | 2 +- clang/lib/Parse/ParseOpenMP.cpp | 81 +++------------------ clang/lib/Sema/SemaOpenMP.cpp | 4 +- clang/test/OpenMP/metadirective_default.cpp | 66 ++++++++--------- 4 files changed, 45 insertions(+), 108 deletions(-) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index 7142fdde36ca5..ae4aa12c21cbe 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -6422,7 +6422,7 @@ class OMPMetaDirective final : public OMPExecutableDirective { reinterpret_cast<OpenMPDirectiveKind *>(this + 1), NumVariants); } - const ArrayRef<OpenMPDirectiveKind> getTrailingDirectiveKinds() const { + ArrayRef<OpenMPDirectiveKind> getTrailingDirectiveKinds() const { return ArrayRef<OpenMPDirectiveKind>( reinterpret_cast<const OpenMPDirectiveKind *>(this + 1), NumVariants); } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 57298aae9f55e..ace5e6d4c162b 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2527,12 +2527,11 @@ StmtResult Parser::ParseOpenMPInformationalDirective( if (ConsumeDirectiveOnlyInMetadirective) { // Runtime path: stop at ')' - let the caller handle it. break; - } else { - // Compile-time path: consume everything to pragma end. - while (Tok.isNot(tok::annot_pragma_openmp_end)) - ConsumeAnyToken(); - break; } + // Compile-time path: consume everything to pragma end. + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + break; } OpenMPClauseKind CKind = Tok.isAnnotation() @@ -2723,76 +2722,16 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( // A single match is returned for OpenMP 5.0 int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx); - // Check if we have user conditions that are not simple IntegerLiterals. - // For simple IntegerLiterals (like condition(0) or condition(1)), we can - // evaluate them here. For anything else (variables, templates, complex - // expressions), we need to send to Sema for proper handling. - bool HasNonConstantUserCondition = false; + // Check if we have any user conditions. + bool HasUserCondition = false; for (OMPTraitInfo *TI : TraitInfos) { if (TI && TI->hasUserCondition()) { - // Check if all user conditions are simple IntegerLiterals. - TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) { - if (!IsScore && E) { - // If it's not a simple IntegerLiteral, we need Sema - if (!isa<IntegerLiteral>(E->IgnoreParenImpCasts())) { - HasNonConstantUserCondition = true; - return true; - } - } - return false; - }); - if (HasNonConstantUserCondition) - break; - } - } - // For simple IntegerLiteral user conditions, re-evaluate BestIdx - // considering them. If the current BestIdx has a false user condition, find - // the next match. - if (!HasNonConstantUserCondition) { - // Helper to evaluate IntegerLiteral user conditions. - // All conditions were verified to be IntegerLiterals above. - auto evaluateUserCondition = [](OMPTraitInfo *TI) -> bool { - bool Result = true; - TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) { - if (!IsScore && E) { - if (auto *IL = dyn_cast<IntegerLiteral>(E->IgnoreParenImpCasts())) { - Result = IL->getValue().getBoolValue(); - return true; - } - llvm_unreachable("All user conditions verified as IntegerLiterals"); - } - return false; - }); - return Result; - }; - if (BestIdx >= 0 && BestIdx < static_cast<int>(TraitInfos.size())) { - OMPTraitInfo *SelectedTI = TraitInfos[BestIdx]; - if (SelectedTI && SelectedTI->hasUserCondition()) { - if (!evaluateUserCondition(SelectedTI)) { - // Current BestIdx has false condition, try to find next match. - BestIdx = -1; - for (int I = 0; I < static_cast<int>(VMIs.size()); ++I) { - if (I < static_cast<int>(TraitInfos.size())) { - OMPTraitInfo *TI = TraitInfos[I]; - if (TI && TI->hasUserCondition()) { - if (evaluateUserCondition(TI)) { - BestIdx = I; - break; - } - } else { - // No user condition or always-true context selector. - BestIdx = I; - break; - } - } - } - } - } + HasUserCondition = true; + break; } } - // If we have non-constant user conditions, collect all variants and send to - // Sema. - if (HasNonConstantUserCondition) { + // If we have user conditions, collect all variants. + if (HasUserCondition) { // Collect all variants with their conditions. SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; SmallVector<Expr *, 4> Conditions; diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 15835af7545ce..9f128dde374d4 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3893,8 +3893,10 @@ StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( if (ThenDK == OMPD_parallel) { StmtResult ParallelStmt = createParallelDirectiveForMetadirective(AStmt, StartLoc, EndLoc); - if (!ParallelStmt.isUsable()) + if (!ParallelStmt.isUsable()) { + DSAStack->pop(); return StmtError(); + } ThenDirective = ParallelStmt.get(); ThenStmt = ThenDirective; } else if (ThenDK == OMPD_unknown || ThenDK == OMPD_nothing) { diff --git a/clang/test/OpenMP/metadirective_default.cpp b/clang/test/OpenMP/metadirective_default.cpp index ab269f41b9380..25cfd9b88b8ce 100644 --- a/clang/test/OpenMP/metadirective_default.cpp +++ b/clang/test/OpenMP/metadirective_default.cpp @@ -39,7 +39,7 @@ void func1() { // CHECK-NEXT: store i32 [[INC]], ptr [[I]], align 4 // CHECK-NEXT: br label %[[FOR_COND]], !llvm.loop [[LOOP3:![0-9]+]] // CHECK: [[FOR_END]]: -// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @1, i32 0, ptr @_Z5func1v.omp_outlined) +// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1:[0-9]+]], i32 0, ptr @_Z5func1v.omp_outlined) // CHECK-NEXT: ret void // CHECK-NEXT: } @@ -49,24 +49,22 @@ void func1() { // CHECK-NEXT: entry // CHECK-NEXT: [[GLOB_TID__ADDR:%.*]] = alloca ptr, align 8 // CHECK-NEXT: [[BOUND_TID__ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OMP_IV:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TMP:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_LB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_UB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_STRIDE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_IS_LAST:%.*]] = alloca i32, align 4 // CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 // CHECK-NEXT: store ptr [[DOTGLOBAL_TID_]], ptr [[GLOB_TID__ADDR]], align 8 // CHECK-NEXT: store ptr [[DOTBOUND_TID_]], ptr [[BOUND_TID__ADDR]], align 8 -// CHECK-NEXT: store i32 0, ptr [[I]], align 4 -// CHECK-NEXT: br label %for.cond -// CHECK:for.cond: -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I]], align 4 -// CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 100 -// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] -// CHECK:for.body: -// CHECK-NEXT: br label [[FOR_INC:%.*]] -// CHECK:for.inc: -// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[I]], align 4 -// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP1]], 1 -// CHECK-NEXT: store i32 [[INC]], ptr [[I]], align 4 -// CHECK-NEXT: br label [[FOR_COND:%.*]] -// CHECK:for.end: -// CHECK-NEXT: ret void +// CHECK-NEXT: store i32 0, ptr [[OMP_LB]], align 4 +// CHECK-NEXT: store i32 99, ptr [[OMP_UB]], align 4 +// CHECK-NEXT: store i32 1, ptr [[OMP_STRIDE]], align 4 +// CHECK-NEXT: store i32 0, ptr [[OMP_IS_LAST]], align 4 +// CHECK: call void @__kmpc_for_static_init_4( +// CHECK: call void @__kmpc_for_static_fini( +// CHECK-NEXT: ret void // CHECK-NEXT:} void func2() { @@ -102,9 +100,9 @@ void func2() { // CHECK-NEXT: store i32 99, ptr [[OMP_UB:%.*]], align 4 // CHECK-NEXT: store i32 1, ptr [[OMP_STRIDE:%.*]], align 4 // CHECK-NEXT: store i32 0, ptr [[OMP_IS_LAST:%.*]], align 4 -// CHECK-NEXT: [[TID_PTR:%.*]] = load ptr, ptr [[GLOBAL_TID_ADDR:%.*]], align 8 +// CHECK-NEXT: [[TID_PTR:%.*]] = load ptr, ptr [[GLOB_TID__ADDR]], align 8 // CHECK-NEXT: [[TID_VAL:%.*]] = load i32, ptr [[TID_PTR]], align 4 -// CHECK-NEXT: call void @__kmpc_for_static_init_4(ptr @2, i32 [[TID_VAL]], i32 34, ptr [[OMP_IS_LAST]], ptr [[OMP_LB]], ptr [[OMP_UB]], ptr [[OMP_STRIDE]], i32 1, i32 1) +// CHECK-NEXT: call void @__kmpc_for_static_init_4(ptr @[[GLOB_INIT:[0-9]+]], i32 [[TID_VAL]], i32 34, ptr [[OMP_IS_LAST]], ptr [[OMP_LB]], ptr [[OMP_UB]], ptr [[OMP_STRIDE]], i32 1, i32 1) // CHECK-NEXT: [[UB_VAL:%.*]] = load i32, ptr [[OMP_UB]], align 4 // CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[UB_VAL]], 99 // CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] @@ -148,7 +146,7 @@ void func2() { // CHECK-NEXT: br label [[OMP_LOOP_EXIT:%.*]] // CHECK:omp.loop.exit: -// CHECK-NEXT: call void @__kmpc_for_static_fini(ptr @2, i32 [[TID:%.*]]) +// CHECK-NEXT: call void @__kmpc_for_static_fini(ptr @[[GLOB_INIT]], i32 [[TID_VAL]]) // CHECK-NEXT: ret void // CHECK-NEXT: } @@ -165,7 +163,7 @@ void func3() { // CHECK-LABEL: define dso_local void @_Z5func3v() // CHECK: entry -// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @1, i32 0, ptr @_Z5func3v.omp_outlined) +// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB3:[0-9]+]], i32 0, ptr @_Z5func3v.omp_outlined) // CHECK-NEXT: ret void // CHECK-NEXT: } @@ -175,23 +173,21 @@ void func3() { // CHECK-NEXT: entry // CHECK-NEXT: [[GLOB_TID__ADDR:%.*]] = alloca ptr, align 8 // CHECK-NEXT: [[BOUND_TID__ADDR:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK: [[OMP_IV:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TMP:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_LB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_UB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_STRIDE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[OMP_IS_LAST:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 // CHECK-NEXT: store ptr [[DOTGLOBAL_TID_]], ptr [[GLOB_TID__ADDR]], align 8 // CHECK-NEXT: store ptr [[DOTBOUND_TID_]], ptr [[BOUND_TID__ADDR]], align 8 -// CHECK-NEXT: store i32 0, ptr [[I]], align 4 -// CHECK-NEXT: br label %for.cond -// CHECK:for.cond: -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I]], align 4 -// CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 100 -// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] -// CHECK:for.body: -// CHECK-NEXT: br label [[FOR_INC:%.*]] -// CHECK:for.inc: -// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[I]], align 4 -// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP1]], 1 -// CHECK-NEXT: store i32 [[INC]], ptr [[I]], align 4 -// CHECK-NEXT: br label [[FOR_COND:%.*]] -// CHECK:for.end: +// CHECK: store i32 0, ptr [[OMP_LB]], align 4 +// CHECK-NEXT: store i32 99, ptr [[OMP_UB]], align 4 +// CHECK-NEXT: store i32 1, ptr [[OMP_STRIDE]], align 4 +// CHECK-NEXT: store i32 0, ptr [[OMP_IS_LAST]], align 4 +// CHECK: call void @__kmpc_for_static_init_4( +// CHECK: call void @__kmpc_for_static_fini( // CHECK-NEXT: ret void // CHECK-NEXT:} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
