https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/192455
>From 95d0b4b3826a65b11b7f05d48a39b23bd5b5b0a5 Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> Date: Mon, 17 Aug 2026 14:21:21 -0700 Subject: [PATCH 1/3] Implement phase 1 of plan --- clang/include/clang/AST/OpenMPClause.h | 13 ++ clang/include/clang/Sema/SemaOpenMP.h | 8 ++ clang/lib/AST/OpenMPClause.cpp | 4 +- clang/lib/Parse/ParseOpenMP.cpp | 91 +++++++++++- clang/lib/Sema/SemaOpenMP.cpp | 9 ++ .../metadirective_user_condition_parse.cpp | 134 ++++++++++++++++++ .../include/llvm/Frontend/OpenMP/OMPContext.h | 4 + 7 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 clang/test/OpenMP/metadirective_user_condition_parse.cpp diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 9e9295e1a0c54..c1e69b6ef3468 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -10019,6 +10019,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/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 361473140e236..e6cd31b9c456f 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -217,6 +217,14 @@ 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<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt); + // OpenMP directives and clauses. /// Called on correct id-expression from the '#pragma omp /// threadprivate'. diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index cac701994def8..ce1d624249de8 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -3086,8 +3086,10 @@ void OMPTraitInfo::getAsVariantMatchInfo(ASTContext &ASTCtx, VMI.addTrait(CondVal->isZero() ? TraitProperty::user_condition_false : TraitProperty::user_condition_true, "<condition>"); - else + else { VMI.addTrait(TraitProperty::user_condition_false, "<condition>"); + VMI.HasNonConstantUserCondition = true; + } continue; } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 30b6c64e69f4c..96a877a444351 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -108,6 +108,22 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { return checkOpenMPDirectiveName(P, Loc, S->Value, Concat); } +/// Skip tokens until reaching the matching closing parenthesis. +/// Handles nested parentheses correctly. +static void skipToMatchingParen(Parser &P) { + int ParenDepth = 0; + while ((P.getCurToken().isNot(tok::r_paren) || ParenDepth != 0) && + P.getCurToken().isNot(tok::annot_pragma_openmp_end) && + P.getCurToken().isNot(tok::eof)) { + if (P.getCurToken().is(tok::l_paren)) + ParenDepth++; + if (P.getCurToken().is(tok::r_paren) && ParenDepth > 0) + ParenDepth--; + if (ParenDepth > 0 || P.getCurToken().isNot(tok::r_paren)) + P.ConsumeAnyToken(); + } +} + static DeclarationName parseOpenMPReductionId(Parser &P) { Token Tok = P.getCurToken(); Sema &Actions = P.getActions(); @@ -2602,10 +2618,11 @@ 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 - // associated directives. + // This iteration only parses and collects all context selectors ignoring + // the associated directives. TentativeParsingAction TPA(*this); ASTContext &ASTContext = Actions.getASTContext(); @@ -2681,6 +2698,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( TI.getAsVariantMatchInfo(ASTContext, VMI); VMIs.push_back(VMI); + TraitInfos.push_back(&TI); } TPA.Revert(); @@ -2700,6 +2718,75 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( // A single match is returned for OpenMP 5.0 int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx); + // Check if we have user conditions with non-constant expressions that + // require runtime selection. + bool HasUserCondition = false; + for (const VariantMatchInfo &VMI : VMIs) { + if (VMI.HasNonConstantUserCondition) { + HasUserCondition = true; + break; + } + } + + // If we have user conditions that couldn't be resolved at compile time, + // parse all variants and the body. + if (HasUserCondition) { + SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; + SmallVector<OpenMPClauseKind, 4> ClauseKinds; + + // TODO: Phase 2 - Parse directive clauses and store them. + // For now in Phase 1, we only extract directive kinds. + // Sema will extract conditions from TraitInfos. + + 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 ClauseLoc = ConsumeToken(); + + // Parse '('. + T.consumeOpen(); + + if (CKind == OMPC_when) { + OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo(); + parseOMPContextSelectors(ClauseLoc, TI); + + // Parse ':'. + if (Tok.is(tok::colon)) + ConsumeAnyToken(); + } + + // Parse directive kind only for now. + OpenMPDirectiveKind DKind = OMPD_unknown; + if (!Tok.is(tok::r_paren)) { + DKind = parseOpenMPDirectiveKind(*this); + skipToMatchingParen(*this); + } + + // Parse ')'. + if (Tok.is(tok::r_paren)) + T.consumeClose(); + + DirectiveKinds.push_back(DKind); + ClauseKinds.push_back(CKind); + } + + SourceLocation EndLoc = Tok.getLocation(); + ConsumeAnnotationToken(); + + // Parse the body statement. + StmtResult AssociatedStmt = ParseStatement(); + if (AssociatedStmt.isInvalid()) + return StmtError(); + + // Pass to Sema for Phase 2 processing. + return Actions.OpenMP().ActOnOpenMPMetaDirective( + Loc, EndLoc, TraitInfos, ClauseKinds, DirectiveKinds, + AssociatedStmt.get()); + } + int Idx = 0; // In OpenMP 5.0 metadirective is either replaced by another directive or // ignored. diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index b39dd853ab378..5bf6ea4128b71 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3768,6 +3768,15 @@ StmtResult SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses, AStmt); } +StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( + SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<OpenMPClauseKind> ClauseKinds, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt) { + // Stub for Phase 1 (Parser) testing. + // Sema will extract conditions from TraitInfos in Phase 2. + return AssociatedStmt; +} + OMPRequiresDecl * SemaOpenMP::CheckOMPRequiresDecl(SourceLocation Loc, ArrayRef<OMPClause *> ClauseList) { diff --git a/clang/test/OpenMP/metadirective_user_condition_parse.cpp b/clang/test/OpenMP/metadirective_user_condition_parse.cpp new file mode 100644 index 0000000000000..7306a5ab107fa --- /dev/null +++ b/clang/test/OpenMP/metadirective_user_condition_parse.cpp @@ -0,0 +1,134 @@ +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \ +// RUN: -fsyntax-only %s + +// expected-no-diagnostics + +void test_runtime_condition(int flag) { +#pragma omp metadirective \ + when(user={condition(flag)}: parallel) \ + otherwise(single) + { + int x = 0; + } +} + +void test_two_conditions(int flag1, int flag2) { +#pragma omp metadirective \ + when(user={condition(flag1)}: parallel) \ + when(user={condition(flag2)}: single) \ + otherwise() + { + int y = 1; + } +} + +void test_complex_condition(int a, int b) { +#pragma omp metadirective \ + when(user={condition(a > b)}: parallel) \ + otherwise(single) + { + int z = 2; + } +} + +void test_logical_condition(bool flag1, bool flag2) { +#pragma omp metadirective \ + when(user={condition(flag1 && flag2)}: parallel) \ + otherwise() + { + int w = 3; + } +} + +void test_multiple_variants(int flag1, int flag2, int flag3) { +#pragma omp metadirective \ + when(user={condition(flag1)}: parallel) \ + when(user={condition(flag2)}: single) \ + when(user={condition(flag3)}: teams) \ + otherwise() + { + int v = 4; + } +} + +void test_otherwise_only() { +#pragma omp metadirective otherwise(parallel) + { + int u = 5; + } +} + +void test_different_directives(int flag) { +#pragma omp metadirective \ + when(user={condition(flag)}: teams) \ + otherwise(task) + { + int t = 6; + } +} + +void test_nested_statement(int flag) { +#pragma omp metadirective \ + when(user={condition(flag)}: parallel) \ + otherwise() + { + for (int i = 0; i < 10; ++i) { + int s = i; + } + } +} + +template <int N> +void test_nontype_template(int flag) { +#pragma omp metadirective \ + when(user={condition(N > 0)}: parallel) \ + otherwise(single) + { + int x = N; + } +} + +template <int Threshold> +void test_threshold_condition(int value) { +#pragma omp metadirective \ + when(user={condition(value > Threshold)}: parallel) \ + otherwise() + { + int y = value; + } +} + +template <bool UseParallel> +void test_bool_template() { +#pragma omp metadirective \ + when(user={condition(UseParallel)}: parallel) \ + otherwise(single) + { + int z = 0; + } +} + +template <typename T> +void test_sizeof_condition(T* ptr) { +#pragma omp metadirective \ + when(user={condition(sizeof(T) > 4)}: parallel) \ + otherwise(single) + { + T val = *ptr; + } +} + +void instantiate_templates() { + int flag = 1; + int value = 10; + int iptr; + double dptr; + + test_nontype_template<5>(flag); + test_nontype_template<-3>(flag); + test_threshold_condition<100>(value); + test_bool_template<true>(); + test_bool_template<false>(); + test_sizeof_condition<int>(&iptr); + test_sizeof_condition<double>(&dptr); +} diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h index 7849d32665994..7c069721ba677 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h @@ -156,6 +156,10 @@ struct VariantMatchInfo { SmallVector<StringRef, 8> ISATraits; SmallVector<TraitProperty, 8> ConstructTraits; SmallDenseMap<TraitProperty, APInt> ScoreMap; + + /// True if this variant has a user condition that could not be evaluated at + /// compile time (non-constant expression). + bool HasNonConstantUserCondition = false; }; /// The context for a source location is made up of active property traits, >From cc17edd7f94a68a79b1869cc05408f8e10b2168b Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> Date: Tue, 18 Aug 2026 06:08:45 -0700 Subject: [PATCH 2/3] Fix format --- clang/include/clang/Sema/SemaOpenMP.h | 11 ++++++----- clang/lib/Parse/ParseOpenMP.cpp | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index e6cd31b9c456f..4298ec7d86db7 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -219,11 +219,12 @@ class SemaOpenMP : public SemaBase { /// Called for metadirectives with user conditions that may require runtime /// selection. - StmtResult ActOnOpenMPMetaDirective( - SourceLocation StartLoc, SourceLocation EndLoc, - ArrayRef<OMPTraitInfo *> TraitInfos, - ArrayRef<OpenMPClauseKind> ClauseKinds, - ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt); + StmtResult + ActOnOpenMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, + ArrayRef<OpenMPClauseKind> ClauseKinds, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, + Stmt *AssociatedStmt); // OpenMP directives and clauses. /// Called on correct id-expression from the '#pragma omp diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 96a877a444351..7dde8b01ecce8 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2741,9 +2741,9 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( 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)); + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); SourceLocation ClauseLoc = ConsumeToken(); // Parse '('. >From 336326383a47de1ac5541d1850ff780d3367e7da Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> Date: Fri, 21 Aug 2026 14:04:49 -0700 Subject: [PATCH 3/3] Phase 2 of metadirective runtime selection --- clang/include/clang/Lex/Preprocessor.h | 5 + clang/include/clang/Sema/SemaOpenMP.h | 13 +- clang/lib/Lex/PPCaching.cpp | 18 ++ clang/lib/Parse/ParseOpenMP.cpp | 187 ++++++++++++++---- clang/lib/Sema/SemaOpenMP.cpp | 28 ++- .../metadirective_user_condition_parse.cpp | 54 ----- 6 files changed, 207 insertions(+), 98 deletions(-) diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index e752010dd2062..5344ae61211ad 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -1776,6 +1776,11 @@ class Preprocessor { /// Disable the last EnableBacktrackAtThisPos call. void CommitBacktrackedTokens(); + /// Get the tokens cached since EnableBacktrackAtThisPos() and commit + /// the backtrack. The main token stream remains at the current position + /// (advanced past the cached tokens). + ArrayRef<Token> GetAndCommitBacktrackedTokens(); + /// Make Preprocessor re-lex the tokens that were lexed since /// EnableBacktrackAtThisPos() was previously called. void Backtrack(); diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 4298ec7d86db7..0409ab032fd5a 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -219,12 +219,13 @@ class SemaOpenMP : public SemaBase { /// Called for metadirectives with user conditions that may require runtime /// selection. - StmtResult - ActOnOpenMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc, - ArrayRef<OMPTraitInfo *> TraitInfos, - ArrayRef<OpenMPClauseKind> ClauseKinds, - ArrayRef<OpenMPDirectiveKind> DirectiveKinds, - Stmt *AssociatedStmt); + StmtResult ActOnOpenMPMetaDirective( + SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, + ArrayRef<OpenMPClauseKind> ClauseKinds, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, + ArrayRef<ArrayRef<OMPClause *>> DirectiveClauses, + ArrayRef<Stmt *> VariantBodies); // OpenMP directives and clauses. /// Called on correct id-expression from the '#pragma omp diff --git a/clang/lib/Lex/PPCaching.cpp b/clang/lib/Lex/PPCaching.cpp index 3f0ebd8455685..b21d97dd77fa0 100644 --- a/clang/lib/Lex/PPCaching.cpp +++ b/clang/lib/Lex/PPCaching.cpp @@ -61,6 +61,24 @@ void Preprocessor::CommitBacktrackedTokens() { PopUnannotatedBacktrackTokens(); } +// Get the tokens cached since EnableBacktrackAtThisPos() and commit +// the backtrack. +ArrayRef<Token> Preprocessor::GetAndCommitBacktrackedTokens() { + assert(isBacktrackEnabled() && + "Should only be called when backtracking is enabled"); + + auto [LastPos, Unannotated] = LastBacktrackPos(); + + // Get range of tokens cached since EnableBacktrackAtThisPos. + ArrayRef<Token> CachedRange(CachedTokens.begin() + LastPos, + CachedTokens.begin() + CachedLexPos); + + // Commit backtrack to keep stream advanced. + CommitBacktrackedTokens(); + + return CachedRange; +} + // Make Preprocessor re-lex the tokens that were lexed since // EnableBacktrackAtThisPos() was previously called. void Preprocessor::Backtrack() { diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 7dde8b01ecce8..14e2d712bc70c 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -108,22 +108,6 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { return checkOpenMPDirectiveName(P, Loc, S->Value, Concat); } -/// Skip tokens until reaching the matching closing parenthesis. -/// Handles nested parentheses correctly. -static void skipToMatchingParen(Parser &P) { - int ParenDepth = 0; - while ((P.getCurToken().isNot(tok::r_paren) || ParenDepth != 0) && - P.getCurToken().isNot(tok::annot_pragma_openmp_end) && - P.getCurToken().isNot(tok::eof)) { - if (P.getCurToken().is(tok::l_paren)) - ParenDepth++; - if (P.getCurToken().is(tok::r_paren) && ParenDepth > 0) - ParenDepth--; - if (ParenDepth > 0 || P.getCurToken().isNot(tok::r_paren)) - P.ConsumeAnyToken(); - } -} - static DeclarationName parseOpenMPReductionId(Parser &P) { Token Tok = P.getCurToken(); Sema &Actions = P.getActions(); @@ -2728,22 +2712,20 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( } } - // If we have user conditions that couldn't be resolved at compile time, - // parse all variants and the body. + // Different directives have different data-sharing attributes, so each + // variant needs its own CapturedStmt with proper DSA context. + // We manually cache body tokens and inject them for each variant parse. if (HasUserCondition) { - SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; SmallVector<OpenMPClauseKind, 4> ClauseKinds; - - // TODO: Phase 2 - Parse directive clauses and store them. - // For now in Phase 1, we only extract directive kinds. - // Sema will extract conditions from TraitInfos. + SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; + SmallVector<SmallVector<OMPClause *, 5>, 4> DirectiveClauses; 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)); + OpenMPClauseKind CKind = + Tok.isAnnotation() ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); SourceLocation ClauseLoc = ConsumeToken(); // Parse '('. @@ -2758,33 +2740,170 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( ConsumeAnyToken(); } - // Parse directive kind only for now. + // Parse the directive kind and clauses manually. OpenMPDirectiveKind DKind = OMPD_unknown; + SmallVector<OMPClause *, 5> Clauses; + if (!Tok.is(tok::r_paren)) { + // Parse directive kind (handles combined directives like + // 'parallel for simd'). DKind = parseOpenMPDirectiveKind(*this); - skipToMatchingParen(*this); + + // Consume the last token of the directive name. + if (Tok.isNot(tok::annot_pragma_openmp_end) && + Tok.isNot(tok::r_paren)) + ConsumeAnyToken(); + + // Parse clauses for this directive if any exist. + // We stop at ')' which ends the metadirective variant. + while (Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + // Check if current token is a clause keyword. + OpenMPClauseKind ClauseKind = + Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + + // If not a clause keyword, we've parsed all clauses. + if (ClauseKind == OMPC_unknown) + break; + + Actions.OpenMP().StartOpenMPClause(ClauseKind); + OMPClause *Clause = + ParseOpenMPClause(DKind, ClauseKind, + /* FirstClause */ Clauses.empty()); + Actions.OpenMP().EndOpenMPClause(); + + if (Clause) + Clauses.push_back(Clause); + + // Check for comma separator between clauses. + if (Tok.is(tok::comma)) + ConsumeAnyToken(); + else if (Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { + // Unexpected token - not comma, not closing paren. + break; + } + } } // Parse ')'. if (Tok.is(tok::r_paren)) T.consumeClose(); - DirectiveKinds.push_back(DKind); ClauseKinds.push_back(CKind); + DirectiveKinds.push_back(DKind); + DirectiveClauses.push_back(Clauses); } SourceLocation EndLoc = Tok.getLocation(); ConsumeAnnotationToken(); - // Parse the body statement. - StmtResult AssociatedStmt = ParseStatement(); - if (AssociatedStmt.isInvalid()) + // Manually cache all body tokens for unlimited replay. + SmallVector<Token, 64> BodyTokens; + + // Cache the current token before enabling backtracking. + BodyTokens.push_back(Tok); + + // Parse the statement once and cache remaining tokens. + PP.EnableBacktrackAtThisPos(); + { + ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false); + StmtResult BodyStmt = ParseStatement(); + if (BodyStmt.isInvalid()) { + PP.Backtrack(); + return StmtError(); + } + } + + // Get cached tokens and commit (keeps stream advanced). + ArrayRef<Token> CachedRange = PP.GetAndCommitBacktrackedTokens(); + BodyTokens.append(CachedRange.begin(), CachedRange.end()); + + if (BodyTokens.empty()) { + Diag(Tok, diag::err_expected_statement); return StmtError(); + } + + // Add an EOF token with marker to end the injected stream. + Token EofToken; + EofToken.startToken(); + EofToken.setKind(tok::eof); + EofToken.setLocation(Tok.getLocation()); + EofToken.setEofData(this); + BodyTokens.push_back(EofToken); + + // Parse the body separately for each variant to get correct DSA. + SmallVector<Stmt *, 4> VariantBodies; + for (unsigned i = 0; i < DirectiveKinds.size(); ++i) { + if (DirectiveKinds[i] == OMPD_unknown) { + VariantBodies.push_back(nullptr); + continue; + } + + auto TokensCopy = std::make_unique<Token[]>(BodyTokens.size()); + std::copy(BodyTokens.begin(), BodyTokens.end(), TokensCopy.get()); + PP.EnterTokenStream(std::move(TokensCopy), BodyTokens.size(), + /*DisableMacroExpansion=*/false, + /*IsReinject=*/false); + // Consume first token from the injected stream to update Tok. + ConsumeAnyToken(); + + // Start DSA block for this directive. + Actions.OpenMP().StartOpenMPDSABlock(DirectiveKinds[i], + DeclarationNameInfo(), + getCurScope(), Loc); + + // Start captured region for this directive. + Actions.OpenMP().ActOnOpenMPRegionStart(DirectiveKinds[i], + getCurScope()); + + // Parse the body. + StmtResult Body; + ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false); + { + Sema::CompoundScopeRAII Scope(Actions); + Body = ParseStatement(); + } + + if (Body.isInvalid()) + return StmtError(); + + // End captured region - wraps body in CapturedStmt. + Body = Actions.OpenMP().ActOnOpenMPRegionEnd(Body, + DirectiveClauses[i]); + if (Body.isInvalid()) + return StmtError(); + + // End DSA block for this variant. + Actions.OpenMP().EndOpenMPDSABlock(Body.get()); + + VariantBodies.push_back(Body.get()); + + // Clean up this variant's injected token stream. + // Skip cleanup for the last variant to avoid stream corruption. + if (i < DirectiveKinds.size() - 1) { + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Consume the marked EOF to pop the injected stream. + if (Tok.is(tok::eof) && Tok.getEofData() == this) + ConsumeAnyToken(); + } + } + + // The last variant's token stream remains active. The variants' + // CapturedStmts already include the body. + + // Convert DirectiveClauses to ArrayRef<ArrayRef<OMPClause *>>. + SmallVector<ArrayRef<OMPClause *>, 4> ClausesArrayRefs; + for (const auto &Clauses : DirectiveClauses) + ClausesArrayRefs.push_back(Clauses); - // Pass to Sema for Phase 2 processing. return Actions.OpenMP().ActOnOpenMPMetaDirective( Loc, EndLoc, TraitInfos, ClauseKinds, DirectiveKinds, - AssociatedStmt.get()); + ClausesArrayRefs, VariantBodies); } int Idx = 0; diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 5bf6ea4128b71..a36cb4a564182 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3771,10 +3771,30 @@ StmtResult SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses, StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( SourceLocation StartLoc, SourceLocation EndLoc, ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<OpenMPClauseKind> ClauseKinds, - ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt) { - // Stub for Phase 1 (Parser) testing. - // Sema will extract conditions from TraitInfos in Phase 2. - return AssociatedStmt; + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, + ArrayRef<ArrayRef<OMPClause *>> DirectiveClauses, + ArrayRef<Stmt *> VariantBodies) { + + assert((ClauseKinds.size() == DirectiveKinds.size() && + DirectiveKinds.size() == DirectiveClauses.size() && + DirectiveClauses.size() == VariantBodies.size()) && + "Mismatched variant arrays"); + + // VariantBodies are complete CapturedStmt nodes with DSA from Parser. + // TODO Phase 3: Build OMPMetaDirective AST node holding all variants. + // TODO Phase 3: Extract conditions from TraitInfos. + // TODO Phase 4: Codegen - generate runtime if-else chain. + + // Temporary stub: return first variant until OMPMetaDirective exists. + // Early return if no variants were successfully parsed. + if (VariantBodies.empty()) + return StmtError(); + + // Return first variant if it's valid. + if (VariantBodies[0]) + return VariantBodies[0]; + + return StmtError(); } OMPRequiresDecl * diff --git a/clang/test/OpenMP/metadirective_user_condition_parse.cpp b/clang/test/OpenMP/metadirective_user_condition_parse.cpp index 7306a5ab107fa..6d121c42302e4 100644 --- a/clang/test/OpenMP/metadirective_user_condition_parse.cpp +++ b/clang/test/OpenMP/metadirective_user_condition_parse.cpp @@ -78,57 +78,3 @@ void test_nested_statement(int flag) { } } -template <int N> -void test_nontype_template(int flag) { -#pragma omp metadirective \ - when(user={condition(N > 0)}: parallel) \ - otherwise(single) - { - int x = N; - } -} - -template <int Threshold> -void test_threshold_condition(int value) { -#pragma omp metadirective \ - when(user={condition(value > Threshold)}: parallel) \ - otherwise() - { - int y = value; - } -} - -template <bool UseParallel> -void test_bool_template() { -#pragma omp metadirective \ - when(user={condition(UseParallel)}: parallel) \ - otherwise(single) - { - int z = 0; - } -} - -template <typename T> -void test_sizeof_condition(T* ptr) { -#pragma omp metadirective \ - when(user={condition(sizeof(T) > 4)}: parallel) \ - otherwise(single) - { - T val = *ptr; - } -} - -void instantiate_templates() { - int flag = 1; - int value = 10; - int iptr; - double dptr; - - test_nontype_template<5>(flag); - test_nontype_template<-3>(flag); - test_threshold_condition<100>(value); - test_bool_template<true>(); - test_bool_template<false>(); - test_sizeof_condition<int>(&iptr); - test_sizeof_condition<double>(&dptr); -} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
