[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
Tue Aug 18 06:09:02 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/2] 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/2] 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 '('.
More information about the llvm-commits
mailing list