[llvm-branch-commits] [flang] [flang][OpenMP] Support lowering of metadirective (part 2) (PR #194424)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Apr 27 10:53:54 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Chi-Chun, Chen (chichunchen)
<details>
<summary>Changes</summary>
Lower non-constant user={condition(expr)} selectors in metadirectives to a fir.if/else chain.
Only statically applicable when-clauses participate in dynamic selection. Dynamic conditions are evaluated at runtime in declaration order, with the best static match, an explicit otherwise/default clause, or implicit nothing as the final fallback.
This patch is part of the feature work for #<!-- -->188820 and stacked on top of https://github.com/llvm/llvm-project/pull/193664.
Assisted with copilot and GPT-5.4
---
Full diff: https://github.com/llvm/llvm-project/pull/194424.diff
3 Files Affected:
- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+60-13)
- (removed) flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90 (-10)
- (modified) flang/test/Lower/OpenMP/metadirective-user.f90 (+202-1)
``````````diff
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 0ac0f0071ec3b..4c2ffb723534c 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4446,6 +4446,11 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
llvm::SmallVector<const parser::OmpDirectiveSpecification *> candidates;
llvm::SmallVector<llvm::omp::VariantMatchInfo, 4> vmis;
+ struct DynamicCandidate {
+ const parser::OmpDirectiveSpecification *dirSpec = nullptr;
+ const parser::ScalarExpr *condExpr = nullptr;
+ };
+ llvm::SmallVector<DynamicCandidate> dynamicCandidates;
// A null directive specification represents either the implicit `nothing`
// variant or the absence of an explicit otherwise/default clause.
const parser::OmpDirectiveSpecification *fallback = nullptr;
@@ -4497,9 +4502,16 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
makeVariantMatchInfo(vmi, *ctxSel, semaCtx,
converter.genLocation(clause.source), dynCondExpr);
- if (dynCondExpr)
- TODO(converter.genLocation(clause.source),
- "dynamic user condition in METADIRECTIVE");
+ // Check if this variant has a dynamic user condition.
+ if (dynCondExpr) {
+ // For dynamic candidates, verify that the non-user parts (device,
+ // implementation) still match statically before keeping the candidate.
+ if (!llvm::omp::isVariantApplicableInContext(
+ vmi, ompCtx, /*DeviceOrImplementationSetOnly=*/true))
+ continue;
+ dynamicCandidates.push_back({variant, dynCondExpr});
+ continue;
+ }
if (!llvm::omp::isVariantApplicableInContext(vmi, ompCtx))
continue;
@@ -4542,18 +4554,53 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
queue.begin());
};
- const parser::OmpDirectiveSpecification *selected = fallback;
- if (candidates.size() == 1) {
- selected = candidates.front();
- } else if (!candidates.empty()) {
- int bestIdx = llvm::omp::getBestVariantMatchForContext(vmis, ompCtx);
- if (bestIdx >= 0) {
- assert(static_cast<size_t>(bestIdx) < candidates.size() &&
- "best variant index out of range");
- selected = candidates[bestIdx];
+ auto selectStaticBest = [&]() -> const parser::OmpDirectiveSpecification * {
+ if (candidates.size() == 1)
+ return candidates.front();
+ if (!candidates.empty()) {
+ int bestIdx = llvm::omp::getBestVariantMatchForContext(vmis, ompCtx);
+ if (bestIdx >= 0) {
+ assert(static_cast<size_t>(bestIdx) < candidates.size() &&
+ "best variant index out of range");
+ return candidates[bestIdx];
+ }
}
+ return fallback;
+ };
+
+ // If no dynamic candidates, do pure static resolution.
+ if (dynamicCandidates.empty()) {
+ genVariant(selectStaticBest());
+ return;
+ }
+
+ // Dynamic resolution: build a fir.if chain for user conditions.
+ // Candidates are tested in declaration order. The final else branch
+ // falls back to the best static match, an explicit otherwise/default
+ // clause, or an implicit no-op.
+ mlir::Location loc = converter.genLocation(meta.v.source);
+ lower::StatementContext stmtCtx;
+ const parser::OmpDirectiveSpecification *staticFallback = selectStaticBest();
+
+ for (auto [i, cand] : llvm::enumerate(dynamicCandidates)) {
+ assert(cand.condExpr && "dynamic candidate must have condition expr");
+ const auto *typedExpr = semantics::GetExpr(semaCtx, *cand.condExpr);
+ assert(typedExpr && "missing typed expression for user condition");
+ mlir::Value condVal =
+ fir::getBase(converter.genExprValue(*typedExpr, stmtCtx, &loc));
+
+ if (condVal.getType() != builder.getI1Type())
+ condVal = builder.createConvert(loc, builder.getI1Type(), condVal);
+
+ auto ifOp =
+ fir::IfOp::create(builder, loc, condVal, /*withElseRegion=*/true);
+ builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
+ genVariant(cand.dirSpec);
+
+ builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
+ if (i == dynamicCandidates.size() - 1)
+ genVariant(staticFallback);
}
- genVariant(selected);
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
diff --git a/flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90 b/flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90
deleted file mode 100644
index a9a02ece1db4b..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90
+++ /dev/null
@@ -1,10 +0,0 @@
-! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
-
-! CHECK: not yet implemented: dynamic user condition in METADIRECTIVE
-
-subroutine test_dynamic_user_condition(flag)
- logical, intent(in) :: flag
- !$omp metadirective &
- !$omp & when(user={condition(flag)}: taskyield) &
- !$omp & default(nothing)
-end subroutine
diff --git a/flang/test/Lower/OpenMP/metadirective-user.f90 b/flang/test/Lower/OpenMP/metadirective-user.f90
index 5165268cf7cc2..cc20da2d2b5d4 100644
--- a/flang/test/Lower/OpenMP/metadirective-user.f90
+++ b/flang/test/Lower/OpenMP/metadirective-user.f90
@@ -1,9 +1,13 @@
-! Test lowering of OpenMP metadirective with constant-folded user selectors.
+! Test lowering of OpenMP metadirective with user={condition()} selectors.
! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 %s -o - | FileCheck %s
! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 %s -o - | FileCheck %s
! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 %s -o - | FileCheck %s
+!===----------------------------------------------------------------------===!
+! Static (constant-folded) user conditions
+!===----------------------------------------------------------------------===!
+
! CHECK-LABEL: func.func @_QPtest_condition_true()
! CHECK: omp.taskyield
! CHECK-NOT: fir.if
@@ -31,3 +35,200 @@ subroutine test_condition_false()
!$omp & default(nothing)
#endif
end subroutine
+
+!===----------------------------------------------------------------------===!
+! Dynamic (runtime) user conditions
+!===----------------------------------------------------------------------===!
+
+! CHECK-LABEL: func.func @_QPtest_dynamic_condition(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.logical<4>>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARG0]]
+! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0
+! CHECK: %[[COND:.*]] = fir.convert %[[LOAD]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[COND]] {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK: }
+! CHECK: return
+subroutine test_dynamic_condition(flag)
+ logical, intent(in) :: flag
+ !$omp metadirective &
+ !$omp & when(user={condition(flag)}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_dynamic_condition_expr(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARG0]]
+! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0
+! CHECK: %[[C1000:.*]] = arith.constant 1000 : i32
+! CHECK: %[[CMP:.*]] = arith.cmpi sgt, %[[LOAD]], %[[C1000]] : i32
+! CHECK: fir.if %[[CMP]] {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK: }
+! CHECK: return
+subroutine test_dynamic_condition_expr(n)
+ integer, intent(in) :: n
+ !$omp metadirective &
+ !$omp & when(user={condition(n > 1000)}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! Both when clauses pass vendor(llvm) statically. The first has a dynamic
+! condition so becomes a runtime branch; the second is fully static and
+! becomes the fallback.
+! CHECK-LABEL: func.func @_QPtest_mixed_static_dynamic(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32>
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARG0]]
+! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0
+! CHECK: %[[C100:.*]] = arith.constant 100 : i32
+! CHECK: %[[CMP:.*]] = arith.cmpi sgt, %[[LOAD]], %[[C100]] : i32
+! CHECK: fir.if %[[CMP]] {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK: omp.taskwait
+! CHECK: }
+! CHECK: return
+subroutine test_mixed_static_dynamic(n)
+ integer, intent(in) :: n
+ !$omp metadirective &
+ !$omp & when(implementation={vendor(llvm)}, user={condition(n > 100)}: barrier) &
+ !$omp & when(implementation={vendor(llvm)}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! Dynamic candidate whose static traits don't match is skipped entirely.
+! CHECK-LABEL: func.func @_QPtest_dynamic_static_mismatch(
+! CHECK-NOT: fir.if
+! CHECK: omp.taskyield
+! CHECK: return
+subroutine test_dynamic_static_mismatch(flag)
+ logical, intent(in) :: flag
+ !$omp metadirective &
+ !$omp & when(implementation={vendor("unknown")}, user={condition(flag)}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(taskyield)
+#else
+ !$omp & default(taskyield)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_two_dynamic(
+! CHECK-SAME: %[[ARG0:[^,]*]]: !fir.ref<!fir.logical<4>>
+! CHECK-SAME: %[[ARG1:.*]]: !fir.ref<!fir.logical<4>>
+! CHECK: %[[DECLA:.*]]:2 = hlfir.declare %[[ARG0]]
+! CHECK: %[[DECLB:.*]]:2 = hlfir.declare %[[ARG1]]
+! CHECK: %[[LOADA:.*]] = fir.load %[[DECLA]]#0
+! CHECK: %[[CONDA:.*]] = fir.convert %[[LOADA]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[CONDA]] {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK: %[[LOADB:.*]] = fir.load %[[DECLB]]#0
+! CHECK: %[[CONDB:.*]] = fir.convert %[[LOADB]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[CONDB]] {
+! CHECK: omp.taskwait
+! CHECK: } else {
+! CHECK: omp.taskyield
+! CHECK: }
+! CHECK: }
+! CHECK: return
+subroutine test_two_dynamic(a, b)
+ logical, intent(in) :: a, b
+ !$omp metadirective &
+ !$omp & when(user={condition(a)}: barrier) &
+ !$omp & when(user={condition(b)}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(taskyield)
+#else
+ !$omp & default(taskyield)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_three_dynamic(
+! CHECK-SAME: %[[A:[^,]*]]: !fir.ref<!fir.logical<4>>
+! CHECK-SAME: %[[B:[^,]*]]: !fir.ref<!fir.logical<4>>
+! CHECK-SAME: %[[C:.*]]: !fir.ref<!fir.logical<4>>
+! CHECK: %[[DA:.*]]:2 = hlfir.declare %[[A]]
+! CHECK: %[[DB:.*]]:2 = hlfir.declare %[[B]]
+! CHECK: %[[DC:.*]]:2 = hlfir.declare %[[C]]
+! CHECK: %[[LA:.*]] = fir.load %[[DA]]#0
+! CHECK: %[[CA:.*]] = fir.convert %[[LA]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[CA]] {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK: %[[LB:.*]] = fir.load %[[DB]]#0
+! CHECK: %[[CB:.*]] = fir.convert %[[LB]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[CB]] {
+! CHECK: omp.taskwait
+! CHECK: } else {
+! CHECK: %[[LC:.*]] = fir.load %[[DC]]#0
+! CHECK: %[[CC:.*]] = fir.convert %[[LC]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[CC]] {
+! CHECK: omp.taskyield
+! CHECK: } else {
+! CHECK: }
+! CHECK: }
+! CHECK: }
+! CHECK: return
+subroutine test_three_dynamic(a, b, c)
+ logical, intent(in) :: a, b, c
+ !$omp metadirective &
+ !$omp & when(user={condition(a)}: barrier) &
+ !$omp & when(user={condition(b)}: taskwait) &
+ !$omp & when(user={condition(c)}: taskyield) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_multi_dynamic_multi_static(
+! CHECK-SAME: %[[A:[^,]*]]: !fir.ref<!fir.logical<4>>
+! CHECK-SAME: %[[B:.*]]: !fir.ref<!fir.logical<4>>
+! CHECK: %[[DA:.*]]:2 = hlfir.declare %[[A]]
+! CHECK: %[[DB:.*]]:2 = hlfir.declare %[[B]]
+! CHECK: %[[LA:.*]] = fir.load %[[DA]]#0
+! CHECK: %[[CA:.*]] = fir.convert %[[LA]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[CA]] {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK: %[[LB:.*]] = fir.load %[[DB]]#0
+! CHECK: %[[CB:.*]] = fir.convert %[[LB]] : (!fir.logical<4>) -> i1
+! CHECK: fir.if %[[CB]] {
+! CHECK: omp.taskyield
+! CHECK: } else {
+! CHECK: omp.taskwait
+! CHECK: }
+! CHECK: }
+! CHECK: return
+subroutine test_multi_dynamic_multi_static(a, b)
+ logical, intent(in) :: a, b
+ ! dynamic + vendor(llvm) -> kept
+ !$omp metadirective &
+ !$omp & when(implementation={vendor(llvm)}, user={condition(a)}: barrier) &
+ ! dynamic + vendor("unknown") -> skipped (static mismatch)
+ !$omp & when(implementation={vendor("unknown")}, user={condition(.true.)}: taskwait) &
+ ! dynamic + vendor(llvm) -> kept
+ !$omp & when(implementation={vendor(llvm)}, user={condition(b)}: taskyield) &
+ ! static + vendor(llvm) -> best static fallback
+ !$omp & when(implementation={vendor(llvm)}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
``````````
</details>
https://github.com/llvm/llvm-project/pull/194424
More information about the llvm-branch-commits
mailing list