[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