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

Zahira Ammarguellat via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 29 07:56:28 PDT 2026


https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/192455

>From 34c79fc24c47f7b7c5f8769b48f4d8dc5c007177 Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 16 Apr 2026 06:51:31 -0700
Subject: [PATCH 1/5] [OpenMP]  Add runtime selection for metadirective with
 non-constant user conditions

---
 clang/include/clang/AST/StmtOpenMP.h          |  24 +++
 clang/lib/CodeGen/CGStmtOpenMP.cpp            | 148 ++++++++++++++++++
 clang/lib/Parse/ParseOpenMP.cpp               | 106 +++++++++++++
 clang/lib/Sema/SemaOpenMP.cpp                 |  11 ++
 clang/lib/Sema/SemaStmt.cpp                   |  13 +-
 .../metadirective_user_condition_codegen.cpp  | 134 ++++++++++++++++
 6 files changed, 431 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/OpenMP/metadirective_user_condition_codegen.cpp

diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
index dbc76e7df8ecd..435a060f42281 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6377,6 +6377,14 @@ class OMPMetaDirective final : public OMPExecutableDirective {
   friend class OMPExecutableDirective;
   Stmt *IfStmt;
 
+  // Runtime selection support for non-constant user conditions.
+  // These fields store metadata for generating runtime if-else chains when
+  // user conditions cannot be evaluated at compile time.
+  bool HasNonConstantConditions = false;
+  unsigned NumConditions = 0;
+  Expr **Conditions = nullptr;
+  OpenMPDirectiveKind *DirectiveVariants = nullptr;
+
   OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc)
       : OMPExecutableDirective(OMPMetaDirectiveClass,
                                llvm::omp::OMPD_metadirective, StartLoc,
@@ -6397,6 +6405,22 @@ class OMPMetaDirective final : public OMPExecutableDirective {
                                        EmptyShell);
   Stmt *getIfStmt() const { return IfStmt; }
 
+  void setNonConstantUserConditions(ArrayRef<Expr *> Conds,
+                                     ArrayRef<OpenMPDirectiveKind> Variants) {
+    HasNonConstantConditions = true;
+    NumConditions = Conds.size();
+    Conditions = const_cast<Expr **>(Conds.data());
+    DirectiveVariants = const_cast<OpenMPDirectiveKind *>(Variants.data());
+  }
+
+  bool hasNonConstantConditions() const { return HasNonConstantConditions; }
+  ArrayRef<Expr *> getConditions() const {
+    return llvm::ArrayRef(Conditions, NumConditions);
+  }
+  ArrayRef<OpenMPDirectiveKind> getDirectiveVariants() const {
+    return llvm::ArrayRef(DirectiveVariants, NumConditions);
+  }
+
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == OMPMetaDirectiveClass;
   }
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 59d0e6825a975..604a1762d05cb 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -2158,7 +2158,155 @@ void CodeGenFunction::EmitOMPParallelDirective(const OMPParallelDirective &S) {
   checkForLastprivateConditionalUpdate(*this, S);
 }
 
+/// Emit an OpenMP directive with the given kind and body statement.
+/// Used for runtime metadirective selection.
+static void EmitOMPDirectiveWithBody(CodeGenFunction &CGF,
+                                     OpenMPDirectiveKind DKind,
+                                     const Stmt *Body,
+                                     SourceLocation Loc) {
+  auto &RT = CGF.CGM.getOpenMPRuntime();
+
+  switch (DKind) {
+  case OMPD_parallel: {
+    // Outline the body into a separate function and call __kmpc_fork_call.
+    auto &OMPBuilder = RT.getOMPBuilder();
+    llvm::Module &M = CGF.CGM.getModule();
+    llvm::LLVMContext &Ctx = M.getContext();
+    llvm::Type *Int32PtrTy = llvm::PointerType::getUnqual(Ctx);
+    llvm::FunctionType *FnTy = llvm::FunctionType::get(
+        llvm::Type::getVoidTy(Ctx), {Int32PtrTy, Int32PtrTy}, false);
+    llvm::Function *OutlinedFn = llvm::Function::Create(
+        FnTy, llvm::GlobalValue::InternalLinkage, ".omp_outlined.", &M);
+    OutlinedFn->addFnAttr(llvm::Attribute::NoUnwind);
+    QualType Int32PtrQTy =
+        CGF.getContext().getPointerType(CGF.getContext().IntTy);
+    FunctionArgList Args;
+    ImplicitParamDecl GtidArg(CGF.getContext(), Int32PtrQTy,
+                              ImplicitParamKind::Other);
+    ImplicitParamDecl BtidArg(CGF.getContext(), Int32PtrQTy,
+                              ImplicitParamKind::Other);
+    Args.push_back(&GtidArg);
+    Args.push_back(&BtidArg);
+
+    // Create CGFunctionInfo for the outlined function.
+    const CGFunctionInfo &FnInfo =
+        CGF.CGM.getTypes().arrangeBuiltinFunctionDeclaration(
+            CGF.getContext().VoidTy, Args);
+
+    // Generate code in the outlined function.
+    CodeGenFunction OutlinedCGF(CGF.CGM);
+    OutlinedCGF.StartFunction(GlobalDecl(), CGF.getContext().VoidTy, OutlinedFn,
+                              FnInfo, Args, Loc, Loc);
+
+    // Emit the body in the outlined function.
+    OutlinedCGF.EmitStmt(Body);
+    OutlinedCGF.FinishFunction();
+
+    // Emit call to __kmpc_fork_call(loc, 0, outlined_fn).
+    llvm::Value *RTLoc = RT.emitUpdateLocation(CGF, Loc);
+    llvm::Value *ForkCallArgs[] = {
+        RTLoc,
+        CGF.Builder.getInt32(0),
+        CGF.Builder.CreateBitCast(OutlinedFn,
+                                  llvm::PointerType::getUnqual(Ctx))};
+
+    llvm::FunctionCallee ForkCallFn = OMPBuilder.getOrCreateRuntimeFunction(
+        M, llvm::omp::OMPRTL___kmpc_fork_call);
+    CGF.EmitRuntimeCall(ForkCallFn, ForkCallArgs);
+    break;
+  }
+  case OMPD_single: {
+    auto &&CodeGen = [Body](CodeGenFunction &CGF, PrePostActionTy &Action) {
+      Action.Enter(CGF);
+      CGF.EmitStmt(Body);
+    };
+
+    // Use emitSingleRegion which handles __kmpc_single/__kmpc_end_single.
+    RT.emitSingleRegion(CGF, CodeGen, Loc,
+                        /*CopyprivateVars=*/{}, /*DestExprs=*/{},
+                        /*SrcExprs=*/{}, /*AssignmentOps=*/{});
+    RT.emitBarrierCall(CGF, Loc, OMPD_single);
+    break;
+  }
+  default:
+    // For unsupported directives, just emit the body.
+    CGF.EmitStmt(Body);
+    break;
+  }
+}
+
 void CodeGenFunction::EmitOMPMetaDirective(const OMPMetaDirective &S) {
+  if (S.hasNonConstantConditions()) {
+    // Build runtime if-else chain.
+    ArrayRef<Expr *> Conditions = S.getConditions();
+    ArrayRef<OpenMPDirectiveKind> DirectiveVariants = S.getDirectiveVariants();
+
+    assert(Conditions.size() == DirectiveVariants.size() &&
+           "Mismatch between conditions and directive variants");
+
+    // Build if-else chain from the conditions. Process in reverse order: skip
+    // 'otherwise' clauses and emit conditions from last to first.
+    llvm::BasicBlock *ContBB = createBasicBlock("omp.meta.end");
+
+    // Find the index of the 'otherwise' clause (condition == nullptr).
+    int otherwiseIdx = -1;
+    for (unsigned i = 0; i < Conditions.size(); ++i) {
+      if (!Conditions[i]) {
+        otherwiseIdx = i;
+        break;
+      }
+    }
+
+    // Build if-else chain.
+    for (int i = Conditions.size() - 1; i >= 0; --i) {
+      Expr *Cond = Conditions[i];
+      OpenMPDirectiveKind DKind = DirectiveVariants[i];
+
+      if (!Cond) {
+        // This is the 'otherwise' clause; will be emitted in final else.
+        continue;
+      }
+
+      llvm::BasicBlock *ThenBB = createBasicBlock("omp.meta.then");
+      llvm::BasicBlock *ElseBB = createBasicBlock("omp.meta.else");
+      EmitBranchOnBoolExpr(Cond, ThenBB, ElseBB, 0);
+      EmitBlock(ThenBB);
+      {
+        // Save LocalDeclMap to allow reusing the same AST in multiple branches.
+        llvm::DenseMap<const Decl *, Address> SavedLocalDeclMap = LocalDeclMap;
+
+        LexicalScope Scope(*this, S.getSourceRange());
+        // Emit the OpenMP directive with the body.
+        EmitOMPDirectiveWithBody(*this, DKind, S.getAssociatedStmt(),
+                                 S.getBeginLoc());
+
+        // Restore LocalDeclMap for next branch.
+        LocalDeclMap = SavedLocalDeclMap;
+      }
+      EmitBranch(ContBB);
+
+      // Continue with else block.
+      EmitBlock(ElseBB);
+    }
+    // Emit the 'otherwise' clause in the final else block.
+    if (otherwiseIdx >= 0) {
+      // Save LocalDeclMap to allow reusing the same AST.
+      llvm::DenseMap<const Decl *, Address> SavedLocalDeclMap = LocalDeclMap;
+
+      LexicalScope Scope(*this, S.getSourceRange());
+      OpenMPDirectiveKind DKind = DirectiveVariants[otherwiseIdx];
+      // Emit the OpenMP directive with the body.
+      EmitOMPDirectiveWithBody(*this, DKind, S.getAssociatedStmt(),
+                               S.getBeginLoc());
+
+      // Restore LocalDeclMap.
+      LocalDeclMap = SavedLocalDeclMap;
+    }
+    EmitBranch(ContBB);
+    EmitBlock(ContBB);
+    return;
+  }
+  // Compile-time selection.
   EmitStmt(S.getIfStmt());
 }
 
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 0e92c3fa1b572..f73fe79237d8f 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2583,6 +2583,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
   case OMPD_metadirective: {
     ConsumeToken();
     SmallVector<VariantMatchInfo, 4> VMIs;
+    SmallVector<OMPTraitInfo *, 4> TraitInfos;
+    SmallVector<OpenMPClauseKind, 4> ClauseKinds;
 
     // First iteration of parsing all clauses of metadirective.
     // This iteration only parses and collects all context selector ignoring the
@@ -2662,11 +2664,115 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       TI.getAsVariantMatchInfo(ASTContext, VMI);
 
       VMIs.push_back(VMI);
+      TraitInfos.push_back(&TI);
+      ClauseKinds.push_back(CKind);
     }
 
     TPA.Revert();
     // End of the first iteration. Parser is reset to the start of metadirective
 
+    // Check if we have non-constant user conditions.
+    bool HasNonConstantUserCondition = false;
+    for (unsigned i = 0; i < TraitInfos.size(); ++i) {
+      if (ClauseKinds[i] == OMPC_when) {
+        OMPTraitInfo *TI = TraitInfos[i];
+        TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) {
+          if (!IsScore && E && !E->isIntegerConstantExpr(ASTContext)) {
+            HasNonConstantUserCondition = true;
+            return true;
+          }
+          return false;
+        });
+        if (HasNonConstantUserCondition)
+          break;
+      }
+    }
+    if (HasNonConstantUserCondition) {
+      // Extract directive kinds and conditions.
+      SmallVector<Expr *, 4> Conditions;
+      SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds;
+
+      while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+        OpenMPClauseKind CKind = Tok.isAnnotation()
+                                     ? OMPC_unknown
+                                     : getOpenMPClauseKind(PP.getSpelling(Tok));
+        SourceLocation ClauseLoc = ConsumeToken();
+        T.consumeOpen();
+
+        Expr *Condition = nullptr;
+        if (CKind == OMPC_when) {
+          OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+          parseOMPContextSelectors(ClauseLoc, TI);
+
+          // Extract the user condition expression.
+          TI.anyScoreOrCondition([&](Expr *&E, bool IsScore) {
+            if (!IsScore && E) {
+              Condition = E;
+              return true;
+            }
+            return false;
+          });
+
+          ConsumeAnyToken(); // consume ':'.
+        }
+
+        OpenMPDirectiveKind DKind = OMPD_unknown;
+        if (!Tok.is(tok::r_paren)) {
+          DKind = parseOpenMPDirectiveKind(*this);
+        }
+
+        Conditions.push_back(Condition);
+        DirectiveKinds.push_back(DKind);
+        // Skip to closing paren.
+        int paren = 0;
+        while (Tok.isNot(tok::r_paren) || paren != 0) {
+          if (Tok.is(tok::l_paren))
+            paren++;
+          if (Tok.is(tok::r_paren))
+            paren--;
+          if (Tok.is(tok::annot_pragma_openmp_end))
+            break;
+          ConsumeAnyToken();
+        }
+        if (Tok.is(tok::r_paren))
+          T.consumeClose();
+      }
+      // Consume end token and parse body once.
+      assert(Tok.is(tok::annot_pragma_openmp_end));
+      SourceLocation EndLoc = ConsumeAnnotationToken();
+
+      StmtResult AssociatedStmt = ParseStatement();
+      if (AssociatedStmt.isInvalid())
+        return StmtError();
+
+      // Allocate arrays in ASTContext.
+      Expr **ConditionsArray = nullptr;
+      OpenMPDirectiveKind *DirectiveKindsArray = nullptr;
+      if (!Conditions.empty()) {
+        ConditionsArray = new (ASTContext) Expr *[Conditions.size()];
+        std::copy(Conditions.begin(), Conditions.end(), ConditionsArray);
+
+        DirectiveKindsArray =
+            new (ASTContext) OpenMPDirectiveKind[DirectiveKinds.size()];
+        std::copy(DirectiveKinds.begin(), DirectiveKinds.end(),
+                  DirectiveKindsArray);
+      }
+      // Create OMPMetaDirective with runtime selection metadata.
+      SmallVector<OMPClause *, 1> Clauses;
+      Directive = Actions.OpenMP().ActOnOpenMPMetaDirective(
+          Clauses, AssociatedStmt.get(), Loc, EndLoc);
+
+      if (Directive.isUsable() && !Conditions.empty()) {
+        if (auto *MD = dyn_cast_or_null<OMPMetaDirective>(Directive.get())) {
+          ArrayRef<Expr *> CondsRef(ConditionsArray, Conditions.size());
+          ArrayRef<OpenMPDirectiveKind> KindsRef(DirectiveKindsArray,
+                                                 DirectiveKinds.size());
+          MD->setNonConstantUserConditions(CondsRef, KindsRef);
+        }
+      }
+
+      return Directive;
+    }
     std::function<void(StringRef)> DiagUnknownTrait =
         [this, Loc](StringRef ISATrait) {
           // TODO Track the selector locations in a way that is accessible here
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index ec9b2e68ef6cf..6b890f3e76f2a 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -7953,6 +7953,17 @@ setBranchProtectedScope(Sema &SemaRef, OpenMPDirectiveKind DKind, Stmt *AStmt) {
   return CS;
 }
 
+StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(ArrayRef<OMPClause *> Clauses,
+                                                Stmt *AStmt,
+                                                SourceLocation StartLoc,
+                                                SourceLocation EndLoc) {
+  if (!AStmt)
+    return StmtError();
+
+  return OMPMetaDirective::Create(getASTContext(), StartLoc, EndLoc, Clauses,
+                                  AStmt, /*IfStmt=*/nullptr);
+}
+
 StmtResult
 SemaOpenMP::ActOnOpenMPParallelDirective(ArrayRef<OMPClause *> Clauses,
                                          Stmt *AStmt, SourceLocation StartLoc,
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 531147ef35b08..60d4214bb3c8a 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -4700,11 +4700,14 @@ buildCapturedStmtCaptureList(Sema &S, CapturedRegionScopeInfo *RSI,
         S.OpenMP().setOpenMPCaptureKind(Field, Cap.getVariable(),
                                         RSI->OpenMPLevel);
 
-      Captures.push_back(CapturedStmt::Capture(
-          Cap.getLocation(),
-          Cap.isReferenceCapture() ? CapturedStmt::VCK_ByRef
-                                   : CapturedStmt::VCK_ByCopy,
-          cast<VarDecl>(Cap.getVariable())));
+      ValueDecl *CapVar = Cap.getVariable();
+      if (auto *BD = dyn_cast<BindingDecl>(CapVar))
+        CapVar = cast<VarDecl>(BD->getDecomposedDecl());
+      Captures.push_back(CapturedStmt::Capture(Cap.getLocation(),
+                                               Cap.isReferenceCapture()
+                                                   ? CapturedStmt::VCK_ByRef
+                                                   : CapturedStmt::VCK_ByCopy,
+                                               cast<VarDecl>(CapVar)));
     }
     CaptureInits.push_back(Init.get());
   }
diff --git a/clang/test/OpenMP/metadirective_user_condition_codegen.cpp b/clang/test/OpenMP/metadirective_user_condition_codegen.cpp
new file mode 100644
index 0000000000000..f2a087f28d1f3
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_user_condition_codegen.cpp
@@ -0,0 +1,134 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 \
+// RUN: -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+void bar();
+
+
+// CHECK-LABEL: define dso_local void @_Z27test_runtime_user_conditionb(
+// CHECK-SAME: i1 noundef zeroext [[USE_GPU:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[USE_GPU_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    [[STOREDV:%.*]] = zext i1 [[USE_GPU]] to i8
+// CHECK-NEXT:    store i8 [[STOREDV]], ptr [[USE_GPU_ADDR]], align 1
+// CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[USE_GPU_ADDR]], align 1
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i8 [[TMP0]] to i1
+// CHECK-NEXT:    br i1 [[LOADEDV]], label %[[OMP_META_THEN:.*]], label %[[OMP_META_ELSE:.*]]
+// CHECK:       [[OMP_META_THEN]]:
+// CHECK-NEXT:    call void @_Z3barv()
+// CHECK-NEXT:    br label %[[OMP_META_END:.*]]
+// CHECK:       [[OMP_META_ELSE]]:
+// CHECK-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1:[0-9]+]], i32 0, ptr @.omp_outlined.)
+// CHECK-NEXT:    br label %[[OMP_META_END]]
+// CHECK:       [[OMP_META_END]]:
+// CHECK-NEXT:    ret void
+//
+void test_runtime_user_condition(bool use_gpu) {
+  #pragma omp metadirective \
+      when(user={condition(use_gpu)}: target) \
+      otherwise(parallel)
+  {
+    bar();
+  }
+}
+
+
+// CHECK-LABEL: define dso_local void @_Z35test_runtime_user_condition_integeri(
+// CHECK-SAME: i32 noundef [[THRESHOLD:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[THRESHOLD_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[I:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[THRESHOLD]], ptr [[THRESHOLD_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[THRESHOLD_ADDR]], align 4
+// CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[TMP0]], 100
+// CHECK-NEXT:    br i1 [[CMP]], label %[[OMP_META_THEN:.*]], label %[[OMP_META_ELSE:.*]]
+// CHECK:       [[OMP_META_THEN]]:
+// CHECK-NEXT:    store i32 0, ptr [[I]], align 4
+// CHECK-NEXT:    br label %[[FOR_COND:.*]]
+// CHECK:       [[FOR_COND]]:
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[I]], align 4
+// CHECK-NEXT:    [[CMP1:%.*]] = icmp slt i32 [[TMP1]], 10
+// CHECK-NEXT:    br i1 [[CMP1]], label %[[FOR_BODY:.*]], label %[[FOR_END:.*]]
+// CHECK:       [[FOR_BODY]]:
+// CHECK-NEXT:    call void @_Z3barv()
+// CHECK-NEXT:    br label %[[FOR_INC:.*]]
+// CHECK:       [[FOR_INC]]:
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[I]], align 4
+// CHECK-NEXT:    [[INC:%.*]] = add nsw i32 [[TMP2]], 1
+// CHECK-NEXT:    store i32 [[INC]], ptr [[I]], align 4
+// CHECK-NEXT:    br label %[[FOR_COND]], !llvm.loop [[LOOP4:![0-9]+]]
+// CHECK:       [[FOR_END]]:
+// CHECK-NEXT:    br label %[[OMP_META_END:.*]]
+// CHECK:       [[OMP_META_ELSE]]:
+// CHECK-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @.omp_outlined..1)
+// CHECK-NEXT:    br label %[[OMP_META_END]]
+// CHECK:       [[OMP_META_END]]:
+// CHECK-NEXT:    ret void
+//
+void test_runtime_user_condition_integer(int threshold) {
+
+
+
+  #pragma omp metadirective \
+      when(user={condition(threshold > 100)}: parallel for) \
+      otherwise(parallel)
+  {
+    for (int i = 0; i < 10; ++i) {
+      bar();
+    }
+  }
+}
+
+// CHECK-LABEL: define dso_local void @_Z33test_constant_user_condition_truev(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z33test_constant_user_condition_truev.omp_outlined)
+// CHECK-NEXT:    ret void
+//
+void test_constant_user_condition_true() {
+  // For constant conditions, no runtime branch should be generated
+
+  #pragma omp metadirective \
+      when(user={condition(true)}: parallel) \
+      otherwise(single)
+  {
+    bar();
+  }
+}
+
+// CHECK-LABEL: define dso_local void @_Z34test_constant_user_condition_falsev(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
+// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @__kmpc_single(ptr @[[GLOB1]], i32 [[TMP0]])
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0
+// CHECK-NEXT:    br i1 [[TMP2]], label %[[OMP_IF_THEN:.*]], label %[[OMP_IF_END:.*]]
+// CHECK:       [[OMP_IF_THEN]]:
+// CHECK-NEXT:    call void @_Z3barv()
+// CHECK-NEXT:    call void @__kmpc_end_single(ptr @[[GLOB1]], i32 [[TMP0]])
+// CHECK-NEXT:    br label %[[OMP_IF_END]]
+// CHECK:       [[OMP_IF_END]]:
+// CHECK-NEXT:    call void @__kmpc_barrier(ptr @[[GLOB2:[0-9]+]], i32 [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+void test_constant_user_condition_false() {
+  // For constant conditions, no runtime branch should be generated
+
+  #pragma omp metadirective \
+      when(user={condition(false)}: parallel) \
+      otherwise(single)
+  {
+    bar();
+  }
+}
+
+#endif
+//.
+// CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[META5:![0-9]+]]}
+// CHECK: [[META5]] = !{!"llvm.loop.mustprogress"}
+//.

>From ca332f555f9e943854cabc27156d9d5d5610374a Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 16 Apr 2026 06:56:13 -0700
Subject: [PATCH 2/5] Fix format

---
 clang/include/clang/AST/StmtOpenMP.h | 2 +-
 clang/lib/CodeGen/CGStmtOpenMP.cpp   | 6 ++----
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
index 435a060f42281..398c484e26029 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6406,7 +6406,7 @@ class OMPMetaDirective final : public OMPExecutableDirective {
   Stmt *getIfStmt() const { return IfStmt; }
 
   void setNonConstantUserConditions(ArrayRef<Expr *> Conds,
-                                     ArrayRef<OpenMPDirectiveKind> Variants) {
+                                    ArrayRef<OpenMPDirectiveKind> Variants) {
     HasNonConstantConditions = true;
     NumConditions = Conds.size();
     Conditions = const_cast<Expr **>(Conds.data());
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 604a1762d05cb..0363728230218 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -2162,8 +2162,7 @@ void CodeGenFunction::EmitOMPParallelDirective(const OMPParallelDirective &S) {
 /// Used for runtime metadirective selection.
 static void EmitOMPDirectiveWithBody(CodeGenFunction &CGF,
                                      OpenMPDirectiveKind DKind,
-                                     const Stmt *Body,
-                                     SourceLocation Loc) {
+                                     const Stmt *Body, SourceLocation Loc) {
   auto &RT = CGF.CGM.getOpenMPRuntime();
 
   switch (DKind) {
@@ -2205,8 +2204,7 @@ static void EmitOMPDirectiveWithBody(CodeGenFunction &CGF,
     // Emit call to __kmpc_fork_call(loc, 0, outlined_fn).
     llvm::Value *RTLoc = RT.emitUpdateLocation(CGF, Loc);
     llvm::Value *ForkCallArgs[] = {
-        RTLoc,
-        CGF.Builder.getInt32(0),
+        RTLoc, CGF.Builder.getInt32(0),
         CGF.Builder.CreateBitCast(OutlinedFn,
                                   llvm::PointerType::getUnqual(Ctx))};
 

>From 2bc90d0ec24b08d4a8de40f5592727621b02fdaf Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Mon, 27 Apr 2026 07:11:39 -0700
Subject: [PATCH 3/5] Removed codegen code and focused on getting the correct
 AST

---
 clang/include/clang/AST/StmtOpenMP.h          |  64 +++--
 clang/include/clang/Sema/SemaOpenMP.h         |   4 +-
 clang/lib/AST/StmtOpenMP.cpp                  |  31 ++-
 clang/lib/AST/StmtPrinter.cpp                 |  33 ++-
 clang/lib/CodeGen/CGStmtOpenMP.cpp            | 146 ------------
 clang/lib/Parse/ParseOpenMP.cpp               | 220 ++++++++++--------
 clang/lib/Sema/SemaOpenMP.cpp                 |  17 +-
 clang/lib/Serialization/ASTReaderStmt.cpp     |  11 +-
 clang/lib/Serialization/ASTWriterStmt.cpp     |   1 +
 clang/test/OpenMP/metadirective_ast_print.c   |   1 -
 clang/test/OpenMP/metadirective_ast_print.cpp |  20 ++
 .../metadirective_user_condition_codegen.cpp  | 134 -----------
 12 files changed, 247 insertions(+), 435 deletions(-)
 create mode 100644 clang/test/OpenMP/metadirective_ast_print.cpp
 delete mode 100644 clang/test/OpenMP/metadirective_user_condition_codegen.cpp

diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
index 398c484e26029..788a813058703 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6375,50 +6375,48 @@ class OMPMaskedDirective final : public OMPExecutableDirective {
 class OMPMetaDirective final : public OMPExecutableDirective {
   friend class ASTStmtReader;
   friend class OMPExecutableDirective;
-  Stmt *IfStmt;
 
-  // Runtime selection support for non-constant user conditions.
-  // These fields store metadata for generating runtime if-else chains when
-  // user conditions cannot be evaluated at compile time.
-  bool HasNonConstantConditions = false;
-  unsigned NumConditions = 0;
-  Expr **Conditions = nullptr;
-  OpenMPDirectiveKind *DirectiveVariants = nullptr;
+  // Children layout: [IfStmt, Cond0, Dir0, Cond1, Dir1, ...]
+  // IfStmt: compile-time resolved directive (may be null for runtime-only)
+  // CondI: Expr* condition for variant I (null = 'otherwise')
+  // DirI:  OMPExecutableDirective* for variant I
+  unsigned NumVariants = 0;
 
-  OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc)
+  OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc,
+                   unsigned NumVariants)
       : OMPExecutableDirective(OMPMetaDirectiveClass,
-                               llvm::omp::OMPD_metadirective, StartLoc,
-                               EndLoc) {}
-  explicit OMPMetaDirective()
+                               llvm::omp::OMPD_metadirective, StartLoc, EndLoc),
+        NumVariants(NumVariants) {}
+
+  explicit OMPMetaDirective(unsigned NumVariants)
       : OMPExecutableDirective(OMPMetaDirectiveClass,
                                llvm::omp::OMPD_metadirective, SourceLocation(),
-                               SourceLocation()) {}
-
-  void setIfStmt(Stmt *S) { IfStmt = S; }
+                               SourceLocation()),
+        NumVariants(NumVariants) {}
 
 public:
-  static OMPMetaDirective *Create(const ASTContext &C, SourceLocation StartLoc,
-                                  SourceLocation EndLoc,
-                                  ArrayRef<OMPClause *> Clauses,
-                                  Stmt *AssociatedStmt, Stmt *IfStmt);
+  static OMPMetaDirective *
+  Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+         ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Stmt *IfStmt,
+         ArrayRef<Expr *> Conditions,
+         ArrayRef<Stmt *> Directives);
+
   static OMPMetaDirective *CreateEmpty(const ASTContext &C, unsigned NumClauses,
-                                       EmptyShell);
-  Stmt *getIfStmt() const { return IfStmt; }
+                                       unsigned NumVariants, EmptyShell);
 
-  void setNonConstantUserConditions(ArrayRef<Expr *> Conds,
-                                    ArrayRef<OpenMPDirectiveKind> Variants) {
-    HasNonConstantConditions = true;
-    NumConditions = Conds.size();
-    Conditions = const_cast<Expr **>(Conds.data());
-    DirectiveVariants = const_cast<OpenMPDirectiveKind *>(Variants.data());
-  }
+  Stmt *getIfStmt() const { return Data->getChildren()[0]; }
+  void setIfStmt(Stmt *S) { Data->getChildren()[0] = S; }
+
+  unsigned getNumVariants() const { return NumVariants; }
 
-  bool hasNonConstantConditions() const { return HasNonConstantConditions; }
-  ArrayRef<Expr *> getConditions() const {
-    return llvm::ArrayRef(Conditions, NumConditions);
+  Expr *getVariantCondition(unsigned I) const {
+    assert(I < NumVariants);
+    return cast_or_null<Expr>(Data->getChildren()[1 + 2 * I]);
   }
-  ArrayRef<OpenMPDirectiveKind> getDirectiveVariants() const {
-    return llvm::ArrayRef(DirectiveVariants, NumConditions);
+
+  OMPExecutableDirective *getVariantDirective(unsigned I) const {
+    assert(I < NumVariants);
+    return cast_or_null<OMPExecutableDirective>(Data->getChildren()[2 + 2 * I]);
   }
 
   static bool classof(const Stmt *T) {
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index 3621ce96b8724..2394a81c5e66c 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -214,7 +214,9 @@ class SemaOpenMP : public SemaBase {
   /// of the  associated statement.
   StmtResult ActOnOpenMPMetaDirective(ArrayRef<OMPClause *> Clauses,
                                       Stmt *AStmt, SourceLocation StartLoc,
-                                      SourceLocation EndLoc);
+                                      SourceLocation EndLoc,
+                                      ArrayRef<Expr *> Conditions,
+                                      ArrayRef<Stmt *> Directives);
 
   // OpenMP directives and clauses.
   /// Called on correct id-expression from the '#pragma omp
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index 9d6b315effb41..19b02ac9a2304 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -260,23 +260,34 @@ void OMPLoopDirective::setFinalsConditions(ArrayRef<Expr *> A) {
   llvm::copy(A, getFinalsConditions().begin());
 }
 
-OMPMetaDirective *OMPMetaDirective::Create(const ASTContext &C,
-                                           SourceLocation StartLoc,
-                                           SourceLocation EndLoc,
-                                           ArrayRef<OMPClause *> Clauses,
-                                           Stmt *AssociatedStmt, Stmt *IfStmt) {
+OMPMetaDirective *
+OMPMetaDirective::Create(const ASTContext &C, SourceLocation StartLoc,
+                         SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses,
+                         Stmt *AssociatedStmt, Stmt *IfStmt,
+                         ArrayRef<Expr *> Conditions,
+                         ArrayRef<Stmt *> Directives) {
+  assert(Conditions.size() == Directives.size());
+  unsigned NumVariants = Conditions.size();
   auto *Dir = createDirective<OMPMetaDirective>(
-      C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc);
-  Dir->setIfStmt(IfStmt);
+      C, Clauses, AssociatedStmt,
+      /*NumChildren=*/1 + 2 * NumVariants, StartLoc, EndLoc, NumVariants);
+  Dir->Data->getChildren()[0] = IfStmt;
+  for (unsigned I = 0; I < NumVariants; ++I) {
+    Dir->Data->getChildren()[1 + 2 * I] = Conditions[I];
+    Dir->Data->getChildren()[1 + 2 * I + 1] = Directives[I];
+  }
   return Dir;
 }
 
 OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C,
                                                 unsigned NumClauses,
+                                                unsigned NumVariants,
                                                 EmptyShell) {
-  return createEmptyDirective<OMPMetaDirective>(C, NumClauses,
-                                                /*HasAssociatedStmt=*/true,
-                                                /*NumChildren=*/1);
+  return createEmptyDirective<OMPMetaDirective>(
+      C, NumClauses,
+      /*HasAssociatedStmt=*/false,
+      /*NumChildren=*/1 + 2 * NumVariants,
+      NumVariants);
 }
 
 OMPParallelDirective *OMPParallelDirective::Create(
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index e0b930ba0a21a..26f5ad063783a 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -761,8 +761,37 @@ void StmtPrinter::PrintOMPExecutableDirective(OMPExecutableDirective *S,
 }
 
 void StmtPrinter::VisitOMPMetaDirective(OMPMetaDirective *Node) {
-  Indent() << "#pragma omp metadirective";
-  PrintOMPExecutableDirective(Node);
+  if (Node->getNumVariants() > 0) {
+    // Runtime path. Print full metadirective with all variants.
+    Indent() << "#pragma omp metadirective";
+    for (unsigned I = 0; I < Node->getNumVariants(); ++I) {
+      Expr *Cond = Node->getVariantCondition(I);
+      Stmt *Dir = Node->getVariantDirective(I);
+      if (Cond) {
+        OS << " when(";
+        Cond->printPretty(OS, nullptr, Policy);
+        OS << ":";
+      } else {
+        OS << " otherwise(";
+      }
+      if (Dir) {
+        if (auto *OED = dyn_cast<OMPExecutableDirective>(Dir)) {
+          unsigned OpenMPVersion = Context ? Context->getLangOpts().OpenMP
+                                           : llvm::omp::FallbackVersion;
+          OS << " #pragma omp "
+             << getOpenMPDirectiveName(OED->getDirectiveKind(), OpenMPVersion);
+        }
+      }
+      OS << ")";
+    }
+    OS << NL;
+    if (Node->hasAssociatedStmt() && Node->getAssociatedStmt())
+      PrintStmt(Node->getAssociatedStmt());
+  } else {
+    Indent() << "#pragma omp metadirective" << NL;
+    if (Stmt *If = Node->getIfStmt())
+      PrintStmt(If);
+  }
 }
 
 void StmtPrinter::VisitOMPParallelDirective(OMPParallelDirective *Node) {
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 0363728230218..59d0e6825a975 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -2158,153 +2158,7 @@ void CodeGenFunction::EmitOMPParallelDirective(const OMPParallelDirective &S) {
   checkForLastprivateConditionalUpdate(*this, S);
 }
 
-/// Emit an OpenMP directive with the given kind and body statement.
-/// Used for runtime metadirective selection.
-static void EmitOMPDirectiveWithBody(CodeGenFunction &CGF,
-                                     OpenMPDirectiveKind DKind,
-                                     const Stmt *Body, SourceLocation Loc) {
-  auto &RT = CGF.CGM.getOpenMPRuntime();
-
-  switch (DKind) {
-  case OMPD_parallel: {
-    // Outline the body into a separate function and call __kmpc_fork_call.
-    auto &OMPBuilder = RT.getOMPBuilder();
-    llvm::Module &M = CGF.CGM.getModule();
-    llvm::LLVMContext &Ctx = M.getContext();
-    llvm::Type *Int32PtrTy = llvm::PointerType::getUnqual(Ctx);
-    llvm::FunctionType *FnTy = llvm::FunctionType::get(
-        llvm::Type::getVoidTy(Ctx), {Int32PtrTy, Int32PtrTy}, false);
-    llvm::Function *OutlinedFn = llvm::Function::Create(
-        FnTy, llvm::GlobalValue::InternalLinkage, ".omp_outlined.", &M);
-    OutlinedFn->addFnAttr(llvm::Attribute::NoUnwind);
-    QualType Int32PtrQTy =
-        CGF.getContext().getPointerType(CGF.getContext().IntTy);
-    FunctionArgList Args;
-    ImplicitParamDecl GtidArg(CGF.getContext(), Int32PtrQTy,
-                              ImplicitParamKind::Other);
-    ImplicitParamDecl BtidArg(CGF.getContext(), Int32PtrQTy,
-                              ImplicitParamKind::Other);
-    Args.push_back(&GtidArg);
-    Args.push_back(&BtidArg);
-
-    // Create CGFunctionInfo for the outlined function.
-    const CGFunctionInfo &FnInfo =
-        CGF.CGM.getTypes().arrangeBuiltinFunctionDeclaration(
-            CGF.getContext().VoidTy, Args);
-
-    // Generate code in the outlined function.
-    CodeGenFunction OutlinedCGF(CGF.CGM);
-    OutlinedCGF.StartFunction(GlobalDecl(), CGF.getContext().VoidTy, OutlinedFn,
-                              FnInfo, Args, Loc, Loc);
-
-    // Emit the body in the outlined function.
-    OutlinedCGF.EmitStmt(Body);
-    OutlinedCGF.FinishFunction();
-
-    // Emit call to __kmpc_fork_call(loc, 0, outlined_fn).
-    llvm::Value *RTLoc = RT.emitUpdateLocation(CGF, Loc);
-    llvm::Value *ForkCallArgs[] = {
-        RTLoc, CGF.Builder.getInt32(0),
-        CGF.Builder.CreateBitCast(OutlinedFn,
-                                  llvm::PointerType::getUnqual(Ctx))};
-
-    llvm::FunctionCallee ForkCallFn = OMPBuilder.getOrCreateRuntimeFunction(
-        M, llvm::omp::OMPRTL___kmpc_fork_call);
-    CGF.EmitRuntimeCall(ForkCallFn, ForkCallArgs);
-    break;
-  }
-  case OMPD_single: {
-    auto &&CodeGen = [Body](CodeGenFunction &CGF, PrePostActionTy &Action) {
-      Action.Enter(CGF);
-      CGF.EmitStmt(Body);
-    };
-
-    // Use emitSingleRegion which handles __kmpc_single/__kmpc_end_single.
-    RT.emitSingleRegion(CGF, CodeGen, Loc,
-                        /*CopyprivateVars=*/{}, /*DestExprs=*/{},
-                        /*SrcExprs=*/{}, /*AssignmentOps=*/{});
-    RT.emitBarrierCall(CGF, Loc, OMPD_single);
-    break;
-  }
-  default:
-    // For unsupported directives, just emit the body.
-    CGF.EmitStmt(Body);
-    break;
-  }
-}
-
 void CodeGenFunction::EmitOMPMetaDirective(const OMPMetaDirective &S) {
-  if (S.hasNonConstantConditions()) {
-    // Build runtime if-else chain.
-    ArrayRef<Expr *> Conditions = S.getConditions();
-    ArrayRef<OpenMPDirectiveKind> DirectiveVariants = S.getDirectiveVariants();
-
-    assert(Conditions.size() == DirectiveVariants.size() &&
-           "Mismatch between conditions and directive variants");
-
-    // Build if-else chain from the conditions. Process in reverse order: skip
-    // 'otherwise' clauses and emit conditions from last to first.
-    llvm::BasicBlock *ContBB = createBasicBlock("omp.meta.end");
-
-    // Find the index of the 'otherwise' clause (condition == nullptr).
-    int otherwiseIdx = -1;
-    for (unsigned i = 0; i < Conditions.size(); ++i) {
-      if (!Conditions[i]) {
-        otherwiseIdx = i;
-        break;
-      }
-    }
-
-    // Build if-else chain.
-    for (int i = Conditions.size() - 1; i >= 0; --i) {
-      Expr *Cond = Conditions[i];
-      OpenMPDirectiveKind DKind = DirectiveVariants[i];
-
-      if (!Cond) {
-        // This is the 'otherwise' clause; will be emitted in final else.
-        continue;
-      }
-
-      llvm::BasicBlock *ThenBB = createBasicBlock("omp.meta.then");
-      llvm::BasicBlock *ElseBB = createBasicBlock("omp.meta.else");
-      EmitBranchOnBoolExpr(Cond, ThenBB, ElseBB, 0);
-      EmitBlock(ThenBB);
-      {
-        // Save LocalDeclMap to allow reusing the same AST in multiple branches.
-        llvm::DenseMap<const Decl *, Address> SavedLocalDeclMap = LocalDeclMap;
-
-        LexicalScope Scope(*this, S.getSourceRange());
-        // Emit the OpenMP directive with the body.
-        EmitOMPDirectiveWithBody(*this, DKind, S.getAssociatedStmt(),
-                                 S.getBeginLoc());
-
-        // Restore LocalDeclMap for next branch.
-        LocalDeclMap = SavedLocalDeclMap;
-      }
-      EmitBranch(ContBB);
-
-      // Continue with else block.
-      EmitBlock(ElseBB);
-    }
-    // Emit the 'otherwise' clause in the final else block.
-    if (otherwiseIdx >= 0) {
-      // Save LocalDeclMap to allow reusing the same AST.
-      llvm::DenseMap<const Decl *, Address> SavedLocalDeclMap = LocalDeclMap;
-
-      LexicalScope Scope(*this, S.getSourceRange());
-      OpenMPDirectiveKind DKind = DirectiveVariants[otherwiseIdx];
-      // Emit the OpenMP directive with the body.
-      EmitOMPDirectiveWithBody(*this, DKind, S.getAssociatedStmt(),
-                               S.getBeginLoc());
-
-      // Restore LocalDeclMap.
-      LocalDeclMap = SavedLocalDeclMap;
-    }
-    EmitBranch(ContBB);
-    EmitBlock(ContBB);
-    return;
-  }
-  // Compile-time selection.
   EmitStmt(S.getIfStmt());
 }
 
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index f73fe79237d8f..a8f78d89e66ba 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2671,108 +2671,23 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
     TPA.Revert();
     // End of the first iteration. Parser is reset to the start of metadirective
 
-    // Check if we have non-constant user conditions.
-    bool HasNonConstantUserCondition = false;
-    for (unsigned i = 0; i < TraitInfos.size(); ++i) {
-      if (ClauseKinds[i] == OMPC_when) {
-        OMPTraitInfo *TI = TraitInfos[i];
-        TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) {
-          if (!IsScore && E && !E->isIntegerConstantExpr(ASTContext)) {
-            HasNonConstantUserCondition = true;
-            return true;
-          }
-          return false;
-        });
-        if (HasNonConstantUserCondition)
-          break;
-      }
-    }
-    if (HasNonConstantUserCondition) {
-      // Extract directive kinds and conditions.
-      SmallVector<Expr *, 4> Conditions;
-      SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds;
-
-      while (Tok.isNot(tok::annot_pragma_openmp_end)) {
-        OpenMPClauseKind CKind = Tok.isAnnotation()
-                                     ? OMPC_unknown
-                                     : getOpenMPClauseKind(PP.getSpelling(Tok));
-        SourceLocation ClauseLoc = ConsumeToken();
-        T.consumeOpen();
-
-        Expr *Condition = nullptr;
-        if (CKind == OMPC_when) {
-          OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
-          parseOMPContextSelectors(ClauseLoc, TI);
-
-          // Extract the user condition expression.
-          TI.anyScoreOrCondition([&](Expr *&E, bool IsScore) {
-            if (!IsScore && E) {
-              Condition = E;
-              return true;
-            }
-            return false;
-          });
-
-          ConsumeAnyToken(); // consume ':'.
-        }
-
-        OpenMPDirectiveKind DKind = OMPD_unknown;
-        if (!Tok.is(tok::r_paren)) {
-          DKind = parseOpenMPDirectiveKind(*this);
-        }
-
-        Conditions.push_back(Condition);
-        DirectiveKinds.push_back(DKind);
-        // Skip to closing paren.
-        int paren = 0;
-        while (Tok.isNot(tok::r_paren) || paren != 0) {
-          if (Tok.is(tok::l_paren))
-            paren++;
-          if (Tok.is(tok::r_paren))
-            paren--;
-          if (Tok.is(tok::annot_pragma_openmp_end))
-            break;
-          ConsumeAnyToken();
-        }
-        if (Tok.is(tok::r_paren))
-          T.consumeClose();
-      }
-      // Consume end token and parse body once.
-      assert(Tok.is(tok::annot_pragma_openmp_end));
-      SourceLocation EndLoc = ConsumeAnnotationToken();
-
-      StmtResult AssociatedStmt = ParseStatement();
-      if (AssociatedStmt.isInvalid())
-        return StmtError();
-
-      // Allocate arrays in ASTContext.
-      Expr **ConditionsArray = nullptr;
-      OpenMPDirectiveKind *DirectiveKindsArray = nullptr;
-      if (!Conditions.empty()) {
-        ConditionsArray = new (ASTContext) Expr *[Conditions.size()];
-        std::copy(Conditions.begin(), Conditions.end(), ConditionsArray);
-
-        DirectiveKindsArray =
-            new (ASTContext) OpenMPDirectiveKind[DirectiveKinds.size()];
-        std::copy(DirectiveKinds.begin(), DirectiveKinds.end(),
-                  DirectiveKindsArray);
-      }
-      // Create OMPMetaDirective with runtime selection metadata.
-      SmallVector<OMPClause *, 1> Clauses;
-      Directive = Actions.OpenMP().ActOnOpenMPMetaDirective(
-          Clauses, AssociatedStmt.get(), Loc, EndLoc);
-
-      if (Directive.isUsable() && !Conditions.empty()) {
-        if (auto *MD = dyn_cast_or_null<OMPMetaDirective>(Directive.get())) {
-          ArrayRef<Expr *> CondsRef(ConditionsArray, Conditions.size());
-          ArrayRef<OpenMPDirectiveKind> KindsRef(DirectiveKindsArray,
-                                                 DirectiveKinds.size());
-          MD->setNonConstantUserConditions(CondsRef, KindsRef);
+    // Detect non-constant user conditions.
+    bool NeedsRuntimeSelection  = false;
+    for (unsigned I = 0; I < TraitInfos.size(); ++I) {
+      if (ClauseKinds[I] != OMPC_when)
+        continue;
+      TraitInfos[I]->anyScoreOrCondition([&](Expr *&E, bool IsScore) {
+        if (!IsScore && E && !E->isIntegerConstantExpr(ASTContext)) {
+          NeedsRuntimeSelection  = true;
+          return true;
         }
-      }
-
-      return Directive;
+        return false;
+      });
+      if (NeedsRuntimeSelection )
+        break;
     }
+
+    if (!NeedsRuntimeSelection) {
     std::function<void(StringRef)> DiagUnknownTrait =
         [this, Loc](StringRef ISATrait) {
           // TODO Track the selector locations in a way that is accessible here
@@ -2853,6 +2768,111 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       return StmtEmpty();
     }
     break;
+    }
+
+    // Runtime path: NeedsRuntimeSelection == true.
+    // For each clause, do a separate tentative parse to extract its
+    // sub-directive. This is needed because
+    // ParseOpenMPDeclarativeOrExecutableDirective consumes the pragma
+    // end on each call, so we can't parse multiple clauses in one
+    // linear pass.
+    SmallVector<Expr *, 4> Conditions;
+    SmallVector<Stmt *, 4> SubDirectives;
+
+    for (unsigned TargetIdx = 0; TargetIdx < TraitInfos.size(); ++TargetIdx) {
+      // Revert to the start of the clause list for each iteration.
+      TentativeParsingAction TPA2(*this);
+      BalancedDelimiterTracker T2(*this, tok::l_paren,
+                                  tok::annot_pragma_openmp_end);
+      unsigned Idx = 0;
+      Expr *Condition = nullptr;
+      Stmt *SubStmt = nullptr;
+
+      while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+        if (Tok.isAnnotation())
+          break;
+        OpenMPClauseKind CKind = getOpenMPClauseKind(PP.getSpelling(Tok));
+        if (CKind == OMPC_unknown)
+          break;
+
+        SourceLocation ClauseLoc = ConsumeToken();
+        T2.consumeOpen();
+
+        if (Idx != TargetIdx) {
+          int paren = 0;
+          while (Tok.isNot(tok::r_paren) || paren != 0) {
+            if (Tok.is(tok::l_paren))
+              paren++;
+            if (Tok.is(tok::r_paren))
+              paren--;
+            if (Tok.is(tok::annot_pragma_openmp_end))
+              break;
+            ConsumeAnyToken();
+          }
+          if (Tok.is(tok::r_paren))
+            T2.consumeClose();
+          ++Idx;
+          continue;
+        }
+
+        // This is the target clause. Parse it fully.
+        if (CKind == OMPC_when) {
+          OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+          parseOMPContextSelectors(ClauseLoc, TI);
+          TI.anyScoreOrCondition([&](Expr *&E, bool IsScore) {
+            if (!IsScore && E) {
+              Condition = E;
+              return true;
+            }
+            return false;
+          });
+          if (Tok.is(tok::colon))
+            ConsumeAnyToken();
+        }
+        // For 'otherwise'/'default', Condition stays nullptr.
+
+        if (Tok.is(tok::r_paren)) {
+          T2.consumeClose();
+          SubStmt = nullptr;
+        } else {
+          StmtResult SR = ParseOpenMPDeclarativeOrExecutableDirective(
+              StmtCtx, /*ReadDirectiveWithinMetadirective=*/true);
+          if (SR.isInvalid()) {
+            TPA2.Commit();
+            SkipUntil(tok::annot_pragma_openmp_end);
+            return StmtError();
+          }
+          SubStmt = SR.get();
+          if (isa_and_nonnull<NullStmt>(SubStmt))
+            SubStmt = nullptr;
+        }
+        break;
+      }
+
+      // Revert token stream for the next iteration.
+      TPA2.Revert();
+      Conditions.push_back(Condition);
+      SubDirectives.push_back(SubStmt);
+    }
+
+    // After all clauses parsed via tentative passes, consume pragma end once.
+    SkipUntil(tok::annot_pragma_openmp_end);
+    SourceLocation EndLoc;
+    if (Tok.is(tok::annot_pragma_openmp_end))
+      EndLoc = ConsumeAnnotationToken();
+    else
+      EndLoc = Loc;
+
+    StmtResult AssocStmt;
+    if (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof) &&
+        Tok.isNot(tok::annot_pragma_openmp) &&
+        Tok.isNot(tok::annot_attr_openmp)) {
+      AssocStmt = ParseStatement();
+    }
+    Directive = Actions.OpenMP().ActOnOpenMPMetaDirective(
+        /*Clauses=*/{}, /*AStmt=*/AssocStmt.get(), Loc, EndLoc, Conditions,
+        SubDirectives);
+    return Directive;
   }
   case OMPD_threadprivate: {
     // FIXME: Should this be permitted in C++?
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 6b890f3e76f2a..4655685fcbaf4 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -7956,12 +7956,21 @@ setBranchProtectedScope(Sema &SemaRef, OpenMPDirectiveKind DKind, Stmt *AStmt) {
 StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(ArrayRef<OMPClause *> Clauses,
                                                 Stmt *AStmt,
                                                 SourceLocation StartLoc,
-                                                SourceLocation EndLoc) {
-  if (!AStmt)
-    return StmtError();
+                                                SourceLocation EndLoc,
+                                                ArrayRef<Expr *> Conditions,
+                                                ArrayRef<Stmt *> Directives) {
+  assert(Conditions.size() == Directives.size() &&
+         "Conditions and Directives must have the same size");
+
+  // Determine if any condition is a non-constant runtime expression.
+  // If all conditions are constant (or this is a compile-time-only
+  // metadirective), IfStmt will already have been resolved by the caller
+  // and Conditions/Directives may be empty.
+  Stmt *IfStmt = Conditions.empty() ? AStmt : nullptr;
+  Stmt *AssocStmt = Conditions.empty() ? nullptr : AStmt;
 
   return OMPMetaDirective::Create(getASTContext(), StartLoc, EndLoc, Clauses,
-                                  AStmt, /*IfStmt=*/nullptr);
+                                  AssocStmt, IfStmt, Conditions, Directives);
 }
 
 StmtResult
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index fb81e4fefdebb..06c3157259df6 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2482,7 +2482,7 @@ void ASTStmtReader::VisitOMPLoopDirective(OMPLoopDirective *D) {
 void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) {
   VisitStmt(D);
   // The NumClauses field was read in ReadStmtFromStream.
-  Record.skipInts(1);
+  Record.skipInts(2);
   VisitOMPExecutableDirective(D);
 }
 
@@ -3642,10 +3642,13 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = OMPCanonicalLoop::createEmpty(Context);
       break;
 
-    case STMT_OMP_META_DIRECTIVE:
-      S = OMPMetaDirective::CreateEmpty(
-          Context, Record[ASTStmtReader::NumStmtFields], Empty);
+    case STMT_OMP_META_DIRECTIVE: {
+      unsigned NumClauses = Record[ASTStmtReader::NumStmtFields];
+      unsigned NumVariants = Record[ASTStmtReader::NumStmtFields + 1];
+      S = OMPMetaDirective::CreateEmpty(Context, NumClauses, NumVariants,
+                                        Empty);
       break;
+    }
 
     case STMT_OMP_PARALLEL_DIRECTIVE:
       S =
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 4612cd2a7944d..699555a53a463 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2499,6 +2499,7 @@ void ASTStmtWriter::VisitOMPLoopDirective(OMPLoopDirective *D) {
 void ASTStmtWriter::VisitOMPMetaDirective(OMPMetaDirective *D) {
   VisitStmt(D);
   Record.push_back(D->getNumClauses());
+  Record.push_back(D->getNumVariants());
   VisitOMPExecutableDirective(D);
   Code = serialization::STMT_OMP_META_DIRECTIVE;
 }
diff --git a/clang/test/OpenMP/metadirective_ast_print.c b/clang/test/OpenMP/metadirective_ast_print.c
index 75ef5fa26827c..42ce2bc4db764 100644
--- a/clang/test/OpenMP/metadirective_ast_print.c
+++ b/clang/test/OpenMP/metadirective_ast_print.c
@@ -294,4 +294,3 @@ void foo2(void) {
 
 #endif
 #endif
-
diff --git a/clang/test/OpenMP/metadirective_ast_print.cpp b/clang/test/OpenMP/metadirective_ast_print.cpp
new file mode 100644
index 0000000000000..3c1432838e056
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_ast_print.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 \
+// RUN: -triple x86_64-unknown-linux-gnu -ast-print %s -o - | FileCheck %s 
+
+// expected-no-diagnostics
+
+void bar();
+void test_nonconstant_condition(bool use_gpu) {
+  #pragma omp metadirective \
+      when(user={condition(use_gpu)}: parallel) \
+      otherwise(single)
+  {
+       bar();
+  }
+}
+
+// LABEL: void test_nonconstant_condition(bool use_gpu)
+// CHECK: #pragma omp metadirective when(use_gpu: #pragma omp parallel) otherwise( #pragma omp single)
+// CHECK: bar();
+
+
diff --git a/clang/test/OpenMP/metadirective_user_condition_codegen.cpp b/clang/test/OpenMP/metadirective_user_condition_codegen.cpp
deleted file mode 100644
index f2a087f28d1f3..0000000000000
--- a/clang/test/OpenMP/metadirective_user_condition_codegen.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
-// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 \
-// RUN: -triple x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s
-
-// expected-no-diagnostics
-
-#ifndef HEADER
-#define HEADER
-
-void bar();
-
-
-// CHECK-LABEL: define dso_local void @_Z27test_runtime_user_conditionb(
-// CHECK-SAME: i1 noundef zeroext [[USE_GPU:%.*]]) #[[ATTR0:[0-9]+]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[USE_GPU_ADDR:%.*]] = alloca i8, align 1
-// CHECK-NEXT:    [[STOREDV:%.*]] = zext i1 [[USE_GPU]] to i8
-// CHECK-NEXT:    store i8 [[STOREDV]], ptr [[USE_GPU_ADDR]], align 1
-// CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[USE_GPU_ADDR]], align 1
-// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i8 [[TMP0]] to i1
-// CHECK-NEXT:    br i1 [[LOADEDV]], label %[[OMP_META_THEN:.*]], label %[[OMP_META_ELSE:.*]]
-// CHECK:       [[OMP_META_THEN]]:
-// CHECK-NEXT:    call void @_Z3barv()
-// CHECK-NEXT:    br label %[[OMP_META_END:.*]]
-// CHECK:       [[OMP_META_ELSE]]:
-// CHECK-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1:[0-9]+]], i32 0, ptr @.omp_outlined.)
-// CHECK-NEXT:    br label %[[OMP_META_END]]
-// CHECK:       [[OMP_META_END]]:
-// CHECK-NEXT:    ret void
-//
-void test_runtime_user_condition(bool use_gpu) {
-  #pragma omp metadirective \
-      when(user={condition(use_gpu)}: target) \
-      otherwise(parallel)
-  {
-    bar();
-  }
-}
-
-
-// CHECK-LABEL: define dso_local void @_Z35test_runtime_user_condition_integeri(
-// CHECK-SAME: i32 noundef [[THRESHOLD:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[THRESHOLD_ADDR:%.*]] = alloca i32, align 4
-// CHECK-NEXT:    [[I:%.*]] = alloca i32, align 4
-// CHECK-NEXT:    store i32 [[THRESHOLD]], ptr [[THRESHOLD_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[THRESHOLD_ADDR]], align 4
-// CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[TMP0]], 100
-// CHECK-NEXT:    br i1 [[CMP]], label %[[OMP_META_THEN:.*]], label %[[OMP_META_ELSE:.*]]
-// CHECK:       [[OMP_META_THEN]]:
-// CHECK-NEXT:    store i32 0, ptr [[I]], align 4
-// CHECK-NEXT:    br label %[[FOR_COND:.*]]
-// CHECK:       [[FOR_COND]]:
-// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[I]], align 4
-// CHECK-NEXT:    [[CMP1:%.*]] = icmp slt i32 [[TMP1]], 10
-// CHECK-NEXT:    br i1 [[CMP1]], label %[[FOR_BODY:.*]], label %[[FOR_END:.*]]
-// CHECK:       [[FOR_BODY]]:
-// CHECK-NEXT:    call void @_Z3barv()
-// CHECK-NEXT:    br label %[[FOR_INC:.*]]
-// CHECK:       [[FOR_INC]]:
-// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[I]], align 4
-// CHECK-NEXT:    [[INC:%.*]] = add nsw i32 [[TMP2]], 1
-// CHECK-NEXT:    store i32 [[INC]], ptr [[I]], align 4
-// CHECK-NEXT:    br label %[[FOR_COND]], !llvm.loop [[LOOP4:![0-9]+]]
-// CHECK:       [[FOR_END]]:
-// CHECK-NEXT:    br label %[[OMP_META_END:.*]]
-// CHECK:       [[OMP_META_ELSE]]:
-// CHECK-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @.omp_outlined..1)
-// CHECK-NEXT:    br label %[[OMP_META_END]]
-// CHECK:       [[OMP_META_END]]:
-// CHECK-NEXT:    ret void
-//
-void test_runtime_user_condition_integer(int threshold) {
-
-
-
-  #pragma omp metadirective \
-      when(user={condition(threshold > 100)}: parallel for) \
-      otherwise(parallel)
-  {
-    for (int i = 0; i < 10; ++i) {
-      bar();
-    }
-  }
-}
-
-// CHECK-LABEL: define dso_local void @_Z33test_constant_user_condition_truev(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @[[GLOB1]], i32 0, ptr @_Z33test_constant_user_condition_truev.omp_outlined)
-// CHECK-NEXT:    ret void
-//
-void test_constant_user_condition_true() {
-  // For constant conditions, no runtime branch should be generated
-
-  #pragma omp metadirective \
-      when(user={condition(true)}: parallel) \
-      otherwise(single)
-  {
-    bar();
-  }
-}
-
-// CHECK-LABEL: define dso_local void @_Z34test_constant_user_condition_falsev(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]])
-// CHECK-NEXT:    [[TMP1:%.*]] = call i32 @__kmpc_single(ptr @[[GLOB1]], i32 [[TMP0]])
-// CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0
-// CHECK-NEXT:    br i1 [[TMP2]], label %[[OMP_IF_THEN:.*]], label %[[OMP_IF_END:.*]]
-// CHECK:       [[OMP_IF_THEN]]:
-// CHECK-NEXT:    call void @_Z3barv()
-// CHECK-NEXT:    call void @__kmpc_end_single(ptr @[[GLOB1]], i32 [[TMP0]])
-// CHECK-NEXT:    br label %[[OMP_IF_END]]
-// CHECK:       [[OMP_IF_END]]:
-// CHECK-NEXT:    call void @__kmpc_barrier(ptr @[[GLOB2:[0-9]+]], i32 [[TMP0]])
-// CHECK-NEXT:    ret void
-//
-void test_constant_user_condition_false() {
-  // For constant conditions, no runtime branch should be generated
-
-  #pragma omp metadirective \
-      when(user={condition(false)}: parallel) \
-      otherwise(single)
-  {
-    bar();
-  }
-}
-
-#endif
-//.
-// CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[META5:![0-9]+]]}
-// CHECK: [[META5]] = !{!"llvm.loop.mustprogress"}
-//.

>From c70a376da0fc86a625967f39170da79dff23f56d Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Mon, 27 Apr 2026 09:57:31 -0700
Subject: [PATCH 4/5] Fix format

---
 clang/include/clang/AST/StmtOpenMP.h |   3 +-
 clang/lib/AST/StmtOpenMP.cpp         |  13 +--
 clang/lib/Parse/ParseOpenMP.cpp      | 148 +++++++++++++--------------
 3 files changed, 80 insertions(+), 84 deletions(-)

diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
index 788a813058703..7205c10ccea88 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6398,8 +6398,7 @@ class OMPMetaDirective final : public OMPExecutableDirective {
   static OMPMetaDirective *
   Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
          ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Stmt *IfStmt,
-         ArrayRef<Expr *> Conditions,
-         ArrayRef<Stmt *> Directives);
+         ArrayRef<Expr *> Conditions, ArrayRef<Stmt *> Directives);
 
   static OMPMetaDirective *CreateEmpty(const ASTContext &C, unsigned NumClauses,
                                        unsigned NumVariants, EmptyShell);
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index 19b02ac9a2304..10dcdb7f5e1ea 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -260,12 +260,10 @@ void OMPLoopDirective::setFinalsConditions(ArrayRef<Expr *> A) {
   llvm::copy(A, getFinalsConditions().begin());
 }
 
-OMPMetaDirective *
-OMPMetaDirective::Create(const ASTContext &C, SourceLocation StartLoc,
-                         SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses,
-                         Stmt *AssociatedStmt, Stmt *IfStmt,
-                         ArrayRef<Expr *> Conditions,
-                         ArrayRef<Stmt *> Directives) {
+OMPMetaDirective *OMPMetaDirective::Create(
+    const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+    ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Stmt *IfStmt,
+    ArrayRef<Expr *> Conditions, ArrayRef<Stmt *> Directives) {
   assert(Conditions.size() == Directives.size());
   unsigned NumVariants = Conditions.size();
   auto *Dir = createDirective<OMPMetaDirective>(
@@ -286,8 +284,7 @@ OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C,
   return createEmptyDirective<OMPMetaDirective>(
       C, NumClauses,
       /*HasAssociatedStmt=*/false,
-      /*NumChildren=*/1 + 2 * NumVariants,
-      NumVariants);
+      /*NumChildren=*/1 + 2 * NumVariants, NumVariants);
 }
 
 OMPParallelDirective *OMPParallelDirective::Create(
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index a8f78d89e66ba..f01614792c9e9 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2672,103 +2672,103 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
     // End of the first iteration. Parser is reset to the start of metadirective
 
     // Detect non-constant user conditions.
-    bool NeedsRuntimeSelection  = false;
+    bool NeedsRuntimeSelection = false;
     for (unsigned I = 0; I < TraitInfos.size(); ++I) {
       if (ClauseKinds[I] != OMPC_when)
         continue;
       TraitInfos[I]->anyScoreOrCondition([&](Expr *&E, bool IsScore) {
         if (!IsScore && E && !E->isIntegerConstantExpr(ASTContext)) {
-          NeedsRuntimeSelection  = true;
+          NeedsRuntimeSelection = true;
           return true;
         }
         return false;
       });
-      if (NeedsRuntimeSelection )
+      if (NeedsRuntimeSelection)
         break;
     }
 
     if (!NeedsRuntimeSelection) {
-    std::function<void(StringRef)> DiagUnknownTrait =
-        [this, Loc](StringRef ISATrait) {
-          // TODO Track the selector locations in a way that is accessible here
-          // to improve the diagnostic location.
-          Diag(Loc, diag::warn_unknown_declare_variant_isa_trait) << ISATrait;
-        };
-    TargetOMPContext OMPCtx(ASTContext, std::move(DiagUnknownTrait),
-                            /* CurrentFunctionDecl */ nullptr,
-                            ArrayRef<llvm::omp::TraitProperty>(),
-                            Actions.OpenMP().getOpenMPDeviceNum());
-
-    // A single match is returned for OpenMP 5.0
-    int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx);
-
-    int Idx = 0;
-    // In OpenMP 5.0 metadirective is either replaced by another directive or
-    // ignored.
-    // TODO: In OpenMP 5.1 generate multiple directives based upon the matches
-    // found by getBestWhenMatchForContext.
-    while (Tok.isNot(tok::annot_pragma_openmp_end)) {
-      // OpenMP 5.0 implementation - Skip to the best index found.
-      if (Idx++ != BestIdx) {
-        ConsumeToken();  // Consume clause name
-        T.consumeOpen(); // Consume '('
-        int paren = 0;
-        // Skip everything inside the clause
-        while (Tok.isNot(tok::r_paren) || paren != 0) {
-          if (Tok.is(tok::l_paren))
-            paren++;
+      std::function<void(StringRef)> DiagUnknownTrait =
+          [this, Loc](StringRef ISATrait) {
+            // TODO Track the selector locations in a way that is accessible
+            // here to improve the diagnostic location.
+            Diag(Loc, diag::warn_unknown_declare_variant_isa_trait) << ISATrait;
+          };
+      TargetOMPContext OMPCtx(ASTContext, std::move(DiagUnknownTrait),
+                              /* CurrentFunctionDecl */ nullptr,
+                              ArrayRef<llvm::omp::TraitProperty>(),
+                              Actions.OpenMP().getOpenMPDeviceNum());
+
+      // A single match is returned for OpenMP 5.0
+      int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx);
+
+      int Idx = 0;
+      // In OpenMP 5.0 metadirective is either replaced by another directive or
+      // ignored.
+      // TODO: In OpenMP 5.1 generate multiple directives based upon the matches
+      // found by getBestWhenMatchForContext.
+      while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+        // OpenMP 5.0 implementation - Skip to the best index found.
+        if (Idx++ != BestIdx) {
+          ConsumeToken();  // Consume clause name
+          T.consumeOpen(); // Consume '('
+          int paren = 0;
+          // Skip everything inside the clause
+          while (Tok.isNot(tok::r_paren) || paren != 0) {
+            if (Tok.is(tok::l_paren))
+              paren++;
+            if (Tok.is(tok::r_paren))
+             paren--;
+            ConsumeAnyToken();
+          }
+          // Parse ')'
           if (Tok.is(tok::r_paren))
-            paren--;
-          ConsumeAnyToken();
+            T.consumeClose();
+          continue;
         }
-        // Parse ')'
-        if (Tok.is(tok::r_paren))
-          T.consumeClose();
-        continue;
-      }
 
-      OpenMPClauseKind CKind = Tok.isAnnotation()
-                                   ? OMPC_unknown
-                                   : getOpenMPClauseKind(PP.getSpelling(Tok));
-      SourceLocation Loc = ConsumeToken();
+        OpenMPClauseKind CKind = Tok.isAnnotation()
+                                     ? OMPC_unknown
+                                     : getOpenMPClauseKind(PP.getSpelling(Tok));
+        SourceLocation Loc = ConsumeToken();
 
-      // Parse '('.
-      T.consumeOpen();
+        // Parse '('.
+        T.consumeOpen();
 
-      // Skip ContextSelectors for when clause
-      if (CKind == OMPC_when) {
-        OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
-        // parse and skip the ContextSelectors
-        parseOMPContextSelectors(Loc, TI);
+        // Skip ContextSelectors for when clause
+        if (CKind == OMPC_when) {
+          OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+          // parse and skip the ContextSelectors
+          parseOMPContextSelectors(Loc, TI);
 
-        // Parse ':'
-        ConsumeAnyToken();
-      }
+          // Parse ':'
+          ConsumeAnyToken();
+        }
 
-      // If no directive is passed, skip in OpenMP 5.0.
-      // TODO: Generate nothing directive from OpenMP 5.1.
-      if (Tok.is(tok::r_paren)) {
-        SkipUntil(tok::annot_pragma_openmp_end);
-        break;
-      }
+        // If no directive is passed, skip in OpenMP 5.0.
+        // TODO: Generate nothing directive from OpenMP 5.1.
+        if (Tok.is(tok::r_paren)) {
+          SkipUntil(tok::annot_pragma_openmp_end);
+              break;
+          }
 
-      // Parse Directive
-      Directive = ParseOpenMPDeclarativeOrExecutableDirective(
-          StmtCtx,
-          /*ReadDirectiveWithinMetadirective=*/true);
+         // Parse Directive
+          Directive = ParseOpenMPDeclarativeOrExecutableDirective(
+              StmtCtx,
+              /*ReadDirectiveWithinMetadirective=*/true);
+          break;
+      }
+      // If no match is found and no otherwise clause is present, skip
+      // OMP5.2 Chapter 7.4: If no otherwise clause is specified the effect is as
+      // if one was specified without an associated directive variant.
+      if (BestIdx == -1 && Idx > 0) {
+        assert(Tok.is(tok::annot_pragma_openmp_end) &&
+               "Expecting the end of the pragma here");
+        ConsumeAnnotationToken();
+        return StmtEmpty();
+      }
       break;
     }
-    // If no match is found and no otherwise clause is present, skip
-    // OMP5.2 Chapter 7.4: If no otherwise clause is specified the effect is as
-    // if one was specified without an associated directive variant.
-    if (BestIdx == -1 && Idx > 0) {
-      assert(Tok.is(tok::annot_pragma_openmp_end) &&
-             "Expecting the end of the pragma here");
-      ConsumeAnnotationToken();
-      return StmtEmpty();
-    }
-    break;
-    }
 
     // Runtime path: NeedsRuntimeSelection == true.
     // For each clause, do a separate tentative parse to extract its

>From dead773f31079db82f7ef0bddc6a7947fc71c29b Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Wed, 29 Apr 2026 07:56:12 -0700
Subject: [PATCH 5/5] Fix format

---
 clang/lib/Parse/ParseOpenMP.cpp | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index f01614792c9e9..ab893f7a86bff 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2718,7 +2718,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
             if (Tok.is(tok::l_paren))
               paren++;
             if (Tok.is(tok::r_paren))
-             paren--;
+              paren--;
             ConsumeAnyToken();
           }
           // Parse ')'
@@ -2749,18 +2749,18 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
         // TODO: Generate nothing directive from OpenMP 5.1.
         if (Tok.is(tok::r_paren)) {
           SkipUntil(tok::annot_pragma_openmp_end);
-              break;
-          }
-
-         // Parse Directive
-          Directive = ParseOpenMPDeclarativeOrExecutableDirective(
-              StmtCtx,
-              /*ReadDirectiveWithinMetadirective=*/true);
           break;
+        }
+
+        // Parse Directive
+        Directive = ParseOpenMPDeclarativeOrExecutableDirective(
+            StmtCtx,
+            /*ReadDirectiveWithinMetadirective=*/true);
+        break;
       }
       // If no match is found and no otherwise clause is present, skip
-      // OMP5.2 Chapter 7.4: If no otherwise clause is specified the effect is as
-      // if one was specified without an associated directive variant.
+      // OMP5.2 Chapter 7.4: If no otherwise clause is specified the effect is
+      // as if one was specified without an associated directive variant.
       if (BestIdx == -1 && Idx > 0) {
         assert(Tok.is(tok::annot_pragma_openmp_end) &&
                "Expecting the end of the pragma here");



More information about the cfe-commits mailing list