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

Zahira Ammarguellat via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 16 06: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/2] [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/2] 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))};
 



More information about the cfe-commits mailing list