Author: Zahira Ammarguellat
Date: 2026-10-26T04:30:54-04:00
New Revision: c2d9a6f71ebd7587a53bb50905ff657141a3ad6b

URL: 
https://github.com/llvm/llvm-project/commit/c2d9a6f71ebd7587a53bb50905ff657141a3ad6b
DIFF: 
https://github.com/llvm/llvm-project/commit/c2d9a6f71ebd7587a53bb50905ff657141a3ad6b.diff

LOG: [OpenMP] Add runtime selection for metadirective with non-constant 
conditions. (#192455)

This patch implements runtime evaluation and selection for `OpenMP`
metadirectives when user conditions are non-constant expressions.

Added: 
    clang/test/OpenMP/metadirective_runtime_messages.cpp
    clang/test/OpenMP/metadirective_user_condition_parse.cpp
    clang/test/OpenMP/metadirective_variant_clauses.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Options/Options.td
    clang/include/clang/Sema/SemaOpenMP.h
    clang/lib/Parse/ParseOpenMP.cpp
    clang/lib/Sema/SemaOpenMP.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index b73af2f88f5b7e..8b6cf3455facae 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1565,6 +1565,9 @@ def err_omp_expected_context_selector
     : Error<"expected valid context selector in %0">;
 def warn_omp_default_deprecated : Warning<"'default' clause for"
   " 'metadirective' is deprecated; use 'otherwise' instead">, 
InGroup<Deprecated>;
+def err_omp_metadirective_standalone_variant : Error<
+  "standalone directive variants are not supported in a 'metadirective' "
+  "with non-constant user conditions">;
 def err_omp_requires_out_inout_depend_type : Error<
   "reserved locator 'omp_all_memory' requires 'out' or 'inout' "
   "dependency types">;

diff  --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index 155d365771b836..b4a57a9c0944b1 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -243,6 +243,7 @@ LANGOPT(OpenMPNoNestedParallelism  , 1, 0, NotCompatible, 
"Assume that no thread
 LANGOPT(OpenMPOffloadMandatory  , 1, 0, NotCompatible, "Assert that offloading 
is mandatory and do not create a host fallback.")
 LANGOPT(OpenMPForceUSM     , 1, 0, NotCompatible, "Enable OpenMP unified 
shared memory mode via compiler.")
 LANGOPT(OpenMPTargetAtomicReduction , 1, 0, NotCompatible, "Use atomic 
operations for OpenMP GPU cross-team reductions where supported.")
+LANGOPT(OpenMPMetadirectiveRuntimeSelection , 1, 0, NotCompatible, "Enable 
runtime selection of metadirective variants with non-constant user conditions.")
 LANGOPT(NoGPULib  , 1, 0, NotCompatible, "Indicate a build without the 
standard GPU libraries.")
 
 LANGOPT(HLSL, 1, 0, NotCompatible, "HLSL")

diff  --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index b760c3a1213460..4419e3cf6f98c5 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -9297,6 +9297,13 @@ def fopenmp_host_ir_file_path : Separate<["-"], 
"fopenmp-host-ir-file-path">,
 
 } // let Visibility = [CC1Option, FC1Option]
 
+def fopenmp_metadirective_runtime_selection
+    : Flag<["-"], "fopenmp-metadirective-runtime-selection">,
+      Visibility<[CC1Option]>,
+      HelpText<"Enable runtime selection of metadirective variants with "
+               "non-constant user conditions.">,
+      MarshallingInfoFlag<LangOpts<"OpenMPMetadirectiveRuntimeSelection">>;
+
 
//===----------------------------------------------------------------------===//
 // SYCL Options
 
//===----------------------------------------------------------------------===//

diff  --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 36c43cd2be0a25..89cc9364c6f12e 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 require runtime
+  /// selection. \p Variants holds one complete directive (or the plain
+  /// associated statement) per entry of \p TraitInfos.
+  StmtResult ActOnOpenMPMetaDirective(SourceLocation StartLoc,
+                                      SourceLocation EndLoc,
+                                      ArrayRef<OMPTraitInfo *> TraitInfos,
+                                      ArrayRef<Stmt *> Variants);
+
   // OpenMP directives and clauses.
   /// Called on correct id-expression from the '#pragma omp
   /// threadprivate'.

diff  --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 95c26f35fff03a..149ca7d9c0d594 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2614,10 +2614,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();
 
@@ -2693,8 +2694,10 @@ StmtResult 
Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       TI.getAsVariantMatchInfo(ASTContext, VMI);
 
       VMIs.push_back(VMI);
+      TraitInfos.push_back(&TI);
     }
 
+    SourceLocation EndLoc = Tok.getLocation();
     TPA.Revert();
     // End of the first iteration. Parser is reset to the start of 
metadirective
 
@@ -2712,6 +2715,91 @@ 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 HasNonConstUserCondition =
+        llvm::any_of(TraitInfos, [&](OMPTraitInfo *TI) {
+          return TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) {
+            return !IsScore && E && !E->getIntegerConstantExpr(ASTContext);
+          });
+        });
+
+    if (HasNonConstUserCondition &&
+        getLangOpts().OpenMPMetadirectiveRuntimeSelection) {
+      // OpenMP 5.2, 7.4: without an 'otherwise' clause the effect is as if one
+      // was specified without an associated directive variant. Only
+      // 'otherwise' (or the deprecated 'default') has no context selectors.
+      unsigned NumClauses = TraitInfos.size();
+      if (llvm::none_of(TraitInfos, [](const OMPTraitInfo *TI) {
+            return TI->Sets.empty();
+          }))
+        TraitInfos.push_back(&ASTContext.getNewOMPTraitInfo());
+
+      // Reuse the normal metadirective variant parse per clause so every
+      // variant is a complete directive node with its own body and its own
+      // data-sharing context. Diagnostics in the associated statement are
+      // reported once per variant.
+      SmallVector<Stmt *, 4> Variants;
+      bool HasError = false;
+      for (unsigned I : llvm::seq<unsigned>(TraitInfos.size())) {
+        bool HasDirective = false;
+        if (I < NumClauses) {
+          OpenMPClauseKind CKind = getOpenMPClauseKind(PP.getSpelling(Tok));
+          ConsumeToken();
+          T.consumeOpen();
+          if (CKind == OMPC_when) {
+            // The selectors were parsed into TraitInfos[I] by the first pass;
+            // skip them so their diagnostics are not repeated.
+            SkipUntil(tok::colon, StopBeforeMatch);
+            TryConsumeToken(tok::colon);
+          }
+          HasDirective = Tok.isNot(tok::r_paren) &&
+                         Tok.isNot(tok::annot_pragma_openmp_end);
+        }
+        // Re-parse the directive and body for every variant but the last.
+        std::optional<TentativeParsingAction> VariantTPA;
+        if (I + 1 < TraitInfos.size())
+          VariantTPA.emplace(*this, /*Unannotated=*/true);
+
+        StmtResult Variant;
+        if (HasDirective) {
+          Variant = ParseOpenMPDeclarativeOrExecutableDirective(
+              StmtCtx, /*ReadDirectiveWithinMetadirective=*/true);
+        } else {
+          // No directive variant: skip to the end of the pragma.
+          while (Tok.isNot(tok::annot_pragma_openmp_end))
+            ConsumeAnyToken();
+          ConsumeAnnotationToken();
+        }
+        // No directive or 'nothing': the variant is the associated statement.
+        if (!Variant.isInvalid() && !Variant.isUsable()) {
+          ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
+          Variant = ParseStatement();
+        }
+        // A standalone variant does not take the associated statement while
+        // the others do; reject the mix until Sema models it.
+        if (auto *D = dyn_cast_or_null<OMPExecutableDirective>(Variant.get());
+            D && !D->hasAssociatedStmt()) {
+          Diag(Loc, diag::err_omp_metadirective_standalone_variant);
+          Variant = StmtError();
+        }
+
+        if (VariantTPA) {
+          VariantTPA->Revert();
+          SkipUntil(tok::r_paren, StopBeforeMatch);
+          T.consumeClose();
+        }
+        if (Variant.isInvalid())
+          HasError = true;
+        else
+          Variants.push_back(Variant.get());
+      }
+      if (HasError)
+        return StmtError();
+      return Actions.OpenMP().ActOnOpenMPMetaDirective(Loc, EndLoc, TraitInfos,
+                                                       Variants);
+    }
+
     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 d5dc3266973555..0963d61df2410d 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3843,6 +3843,19 @@ StmtResult 
SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses,
                                     AStmt);
 }
 
+StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(
+    SourceLocation StartLoc, SourceLocation EndLoc,
+    ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<Stmt *> Variants) {
+  assert(TraitInfos.size() == Variants.size() &&
+         "Expected one variant per metadirective clause");
+  // TODO Phase 3: Build OMPMetaDirective AST node holding all variants.
+  // TODO Phase 4: Codegen - generate runtime if-else chain.
+  // Temporary stub: return the first variant until OMPMetaDirective exists.
+  if (Variants.empty())
+    return StmtError();
+  return Variants.front();
+}
+
 OMPRequiresDecl *
 SemaOpenMP::CheckOMPRequiresDecl(SourceLocation Loc,
                                  ArrayRef<OMPClause *> ClauseList) {

diff  --git a/clang/test/OpenMP/metadirective_runtime_messages.cpp 
b/clang/test/OpenMP/metadirective_runtime_messages.cpp
new file mode 100644
index 00000000000000..7419fbe010e19a
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_runtime_messages.cpp
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -fsyntax-only %s
+
+// Body diagnostics are reported once per variant.
+void body_error_per_variant(int flag) {
+#pragma omp metadirective \
+  when(user={condition(flag)}: parallel) \
+  otherwise(single)
+  {
+    undeclared = 1; // expected-error 2 {{use of undeclared identifier 
'undeclared'}}
+  }
+}
+
+// Without 'otherwise', the implicit plain-statement variant is parsed too.
+void body_error_no_otherwise(int flag) {
+#pragma omp metadirective \
+  when(user={condition(flag)}: parallel)
+  {
+    undeclared2 = 1; // expected-error 2 {{use of undeclared identifier 
'undeclared2'}}
+  }
+}
+
+// Variant-specific errors are reported for the variant they apply to.
+void variant_specific_errors(int flag) {
+#pragma omp metadirective \
+  when(user={condition(flag)}: for) \
+  otherwise(single)
+  for (int i = 0; i < 10; ++i) {
+#pragma omp barrier // expected-error {{region cannot be closely nested inside 
'for' region}} expected-error {{region cannot be closely nested inside 'single' 
region}}
+  }
+}
+
+// A ':' inside the condition (ternary) does not end the selectors.
+void ternary_in_condition(int a, int b) {
+#pragma omp metadirective \
+  when(user={condition(a ? b : 0)}: parallel) \
+  otherwise(single)
+  {
+  }
+}
+
+// A 'nothing' variant still consumes the associated statement.
+void nothing_variant(int flag) {
+#pragma omp metadirective \
+  when(user={condition(flag)}: nothing) \
+  otherwise(single)
+  {
+    undeclared3 = 1; // expected-error 2 {{use of undeclared identifier 
'undeclared3'}}
+  }
+}
+
+// Each variant resolves names in the associated statement again.
+void local_decl(int flag) {
+#pragma omp metadirective \
+  when(user={condition(flag)}: parallel) \
+  otherwise(single)
+  {
+    struct L { int v; };
+    L l;
+    l.v = 1;
+  }
+}
+
+// Standalone directive variants are rejected for now.
+void standalone_variant(int flag) {
+  // expected-error@+1 {{standalone directive variants are not supported in a 
'metadirective' with non-constant user conditions}}
+#pragma omp metadirective \
+  when(user={condition(flag)}: barrier) \
+  otherwise(single)
+  {
+  }
+}

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 00000000000000..b517c00c2e1680
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_user_condition_parse.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -fsyntax-only %s
+// 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_
diff erent_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;
+    }
+  }
+}
+

diff  --git a/clang/test/OpenMP/metadirective_variant_clauses.cpp 
b/clang/test/OpenMP/metadirective_variant_clauses.cpp
new file mode 100644
index 00000000000000..9a3d0e675a5493
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_variant_clauses.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -fsyntax-only %s
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fsyntax-only %s
+
+// expected-no-diagnostics
+
+void test_variant_clauses_function_scope(int flag) {
+  int x = 0;
+  int y = 0;
+
+#pragma omp metadirective \
+  when(user={condition(flag)}: parallel private(x)) \
+  when(user={condition(!flag)}: parallel shared(y))
+  {
+    x = 1;
+    y = 2;
+  }
+}
+
+void test_nested_metadirective(int flag1, int flag2) {
+  int x = 0;
+
+#pragma omp parallel private(x)
+  {
+#pragma omp metadirective \
+  when(user={condition(flag1)}: for) \
+  when(user={condition(flag2)}: single private(x))
+    for (int i = 0; i < 10; ++i) {
+      x = i;
+    }
+  }
+}
+
+void test_multiple_clauses(int flag) {
+  int x = 0, y = 0, z = 0;
+
+#pragma omp metadirective \
+  when(user={condition(flag)}: parallel private(x) shared(y) firstprivate(z)) \
+  otherwise(single private(x))
+  {
+    x = y + z;
+  }
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to