[clang] [llvm] [OpenMP] Add runtime selection for metadirective with non-constant conditions. (PR #192455)
Zahira Ammarguellat via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 21 14:05:04 PDT 2026
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 <zahira.ammarguellat at intel.com>
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 <zahira.ammarguellat at intel.com>
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 <zahira.ammarguellat at intel.com>
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);
-}
More information about the llvm-commits
mailing list