[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