[flang-commits] [flang] 419a152 - [MLIR][OpenMP] Support for dispatch construct with clauses nocontext & novariants (#215877)
via flang-commits
flang-commits at lists.llvm.org
Mon Sep 21 03:36:34 PDT 2026
Author: SunilKuravinakop
Date: 2026-09-21T16:06:25+05:30
New Revision: 419a152df732336ffac80a676a81b3cd0560936b
URL: https://github.com/llvm/llvm-project/commit/419a152df732336ffac80a676a81b3cd0560936b
DIFF: https://github.com/llvm/llvm-project/commit/419a152df732336ffac80a676a81b3cd0560936b.diff
LOG: [MLIR][OpenMP] Support for dispatch construct with clauses nocontext & novariants (#215877)
Support for dispatch construct with clauses nocontext & novariants clauses in MLIR
This patch adds the omp.dispatch operation to the OpenMP dialect, along
with the nocontext and novariants clauses, the verifier, and LLVM IR
translation. It is limited to the MLIR layer. Flang lowering follows in
later patches.
Part 1 of 3 of the !$omp dispatch support.
This is derived from
[203320](https://github.com/llvm/llvm-project/pull/203320) (split into 3
parts).
This also Fixes[
#203317](https://github.com/llvm/llvm-project/issues/203317)
---------
Co-authored-by: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Added:
mlir/test/Target/LLVMIR/openmp-dispatch.mlir
Modified:
flang/docs/OpenMPSupport.md
mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
mlir/test/Dialect/OpenMP/ops.mlir
mlir/test/Target/LLVMIR/openmp-todo.mlir
Removed:
################################################################################
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 01de25f824969..a63ae9f5a2716 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -184,7 +184,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
| compare clause on atomic construct | <span class="part">partial</span> | SunilKuravinakop | Semantics and lowering coverage exist (`flang/test/Semantics/OpenMP/atomic-compare.f90`, `flang/test/Lower/OpenMP/atomic-compare.f90`); remaining gaps are primarily fail/capture combinations and broader type coverage. | [llvm/llvm-project#184761](https://github.com/llvm/llvm-project/pull/184761) |
| fail clause on atomic construct | <span class="part">partial</span> | SunilKuravinakop | Semantics coverage exists (`flang/test/Semantics/OpenMP/atomic-compare.f90`), but lowering for `fail(...)` paths is still TODO (`flang/test/Lower/OpenMP/Todo/atomic-compare-fail.f90`). Complete lowering for compare+fail(+capture), then add non-TODO lowering tests. | [llvm/llvm-project#184761](https://github.com/llvm/llvm-project/pull/184761) |
| interop construct | <span class="part">partial</span> | sshrestha-aa | Semantics coverage exists (`flang/test/Semantics/OpenMP/interop-construct.f90`), but lowering remains TODO-tracked (`flang/lib/Lower/OpenMP/OpenMP.cpp` TODO: `OpenMPInteropConstruct`; `flang/test/Lower/OpenMP/Todo/interop-construct.f90`). | [llvm/llvm-project#132343](https://github.com/llvm/llvm-project/pull/132343) |
-| dispatch construct | <span class="part">partial</span> | SunilKuravinakop | Semantics coverage exists (`flang/test/Semantics/OpenMP/dispatch.f90`), but lowering remains TODO-tracked (`flang/lib/Lower/OpenMP/OpenMP.cpp` TODO: `OpenMPDispatchConstruct`; `flang/test/Lower/OpenMP/Todo/dispatch.f90`). | [llvm/llvm-project#121982](https://github.com/llvm/llvm-project/pull/121982) |
+| dispatch construct | <span class="part">partial</span> | SunilKuravinakop | Semantics coverage exists (`flang/test/Semantics/OpenMP/dispatch.f90`), follow-on completeness work still in progress: Being implemented in 3 parts, MLIR support under review | [llvm/llvm-project#121982](https://github.com/llvm/llvm-project/pull/121982), [llvm/llvm-project#215877](https://github.com/llvm/llvm-project/pull/215877) |
| masked construct | <span class="part">partial</span> | | Covered in semantics/lowering (`flang/test/Semantics/OpenMP/masked.f90`, `flang/test/Lower/OpenMP/masked.f90`). | [llvm/llvm-project#91432](https://github.com/llvm/llvm-project/pull/91432) |
| masked combined constructs | <span class="part">partial</span> | | Covered in lowering tests (`flang/test/Lower/OpenMP/masked_taskloop.f90`, `flang/test/Lower/OpenMP/parallel-masked-taskloop.f90`) with ongoing breadth expansion. | [llvm/llvm-project#188071](https://github.com/llvm/llvm-project/pull/188071), [llvm/llvm-project#188070](https://github.com/llvm/llvm-project/pull/188070) |
| present map type modifier | <span class="part">partial</span> | | Semantics coverage exists (`flang/test/Semantics/OpenMP/present.f90`) and map lowering exists, with ongoing completeness checks. | [llvm/llvm-project#129586](https://github.com/llvm/llvm-project/pull/129586) |
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
index 55a8bba40404b..bd8bc6ed12770 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
@@ -1005,6 +1005,36 @@ class OpenMP_MergeableClauseSkip<
def OpenMP_MergeableClause : OpenMP_MergeableClauseSkip<>;
+//===----------------------------------------------------------------------===//
+// V5.2: [7.6.2] `nocontext` clause
+//===----------------------------------------------------------------------===//
+
+class OpenMP_NocontextClauseSkip<
+ bit traits = false, bit arguments = false, bit assemblyFormat = false,
+ bit description = false, bit extraClassDeclaration = false
+ > : OpenMP_Clause<traits, arguments, assemblyFormat, description,
+ extraClassDeclaration> {
+ let arguments = (ins
+ Optional<I1>:$nocontext
+ );
+
+ let optAssemblyFormat = [{
+ `nocontext` `(` $nocontext `)`
+ }];
+
+ let description = [{
+ When parameter to `nocontext` evaluates to `true`, the dispatch construct is
+ not added to the OpenMP context, so a function variant that would be selected
+ only because of the enclosing dispatch construct is not selected.
+
+ At the dialect level this operand carries no semantics of its own. The
+ producer of the region is responsible for materializing the base/variant
+ selection, and the LLVM IR translation deliberately ignores this operand.
+ }];
+}
+
+def OpenMP_NocontextClause : OpenMP_NocontextClauseSkip<>;
+
//===----------------------------------------------------------------------===//
// V5.2: [15.7] `nogroup` clause
//===----------------------------------------------------------------------===//
@@ -1080,6 +1110,38 @@ class OpenMP_NotinbranchClauseSkip<
}
def OpenMP_NotinbranchClause : OpenMP_NotinbranchClauseSkip<>;
+//===----------------------------------------------------------------------===//
+// V5.2: [7.6.1] `novariants` clause
+//===----------------------------------------------------------------------===//
+
+class OpenMP_NovariantsClauseSkip<
+ bit traits = false, bit arguments = false, bit assemblyFormat = false,
+ bit description = false, bit extraClassDeclaration = false
+ > : OpenMP_Clause<traits, arguments, assemblyFormat, description,
+ extraClassDeclaration> {
+ let arguments = (ins
+ Optional<I1>:$novariants
+ );
+
+ let optAssemblyFormat = [{
+ `novariants` `(` $novariants `)`
+ }];
+
+ let description = [{
+ When a `novariants` clause is present and its expression evaluates to
+ `true`, no function variant is selected for the target call of the dispatch
+ region, even if one would be selected normally. The use of a variable in a
+ `novariants` clause expression causes an implicit reference to the variable
+ in all enclosing constructs.
+
+ At the dialect level this operand carries no semantics of its own. The
+ producer of the region is responsible for materializing the base/variant
+ selection, and the LLVM IR translation deliberately ignores this operand.
+ }];
+}
+
+def OpenMP_NovariantsClause : OpenMP_NovariantsClauseSkip<>;
+
//===----------------------------------------------------------------------===//
// V5.2: [15.6] `nowait` clause
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index b2ad6abd40ed3..6bc99cf765699 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2425,6 +2425,37 @@ def MaskedOp : OpenMP_Op<"masked", traits = [
];
}
+//===----------------------------------------------------------------------===//
+// [Spec 5.2] 7.6 dispatch Construct
+//===----------------------------------------------------------------------===//
+def DispatchOp : OpenMP_Op<"dispatch", traits = [
+ AttrSizedOperandSegments, NoRegionArguments
+ ], clauses = [
+ // TODO: Complete clause list (device, depend, is_device_ptr).
+ OpenMP_NocontextClause, OpenMP_NovariantsClause, OpenMP_NowaitClause
+ ], singleRegion = true> {
+ let summary = "dispatch construct";
+ let description = [{
+ The dispatch construct enables the invocation of a variant of a
+ base procedure. It was introduced in OpenMP 5.1; the section numbers cited
+ here follow the OpenMP 5.2 spec (7.6). At the source level (C/C++/Fortran)
+ the structured block of a dispatch construct is a single expression
+ statement containing a function or subroutine call. That is a restriction on
+ the source form, not an invariant of this op: the region may contain
+ multiple blocks and control flow (for example, a runtime base/variant
+ branch).
+
+ The producer of the region is responsible for materializing the base/variant
+ selection inside the region. The `nocontext` and `novariants` operands are
+ retained on the op only so the producer can read them back during call
+ lowering; the LLVM IR translation deliberately ignores these operands.
+ }] # clausesDescription;
+
+ let builders = [
+ OpBuilder<(ins CArg<"const DispatchOperands &">:$clauses)>
+ ];
+}
+
//===----------------------------------------------------------------------===//
// [Spec 5.2] 6.5 allocate Directive
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 0098f1dabd84b..ed144f7b28983 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -5428,6 +5428,16 @@ void MaskedOp::build(OpBuilder &builder, OperationState &state,
MaskedOp::build(builder, state, clauses.filteredThreadId);
}
+//===----------------------------------------------------------------------===//
+// Spec 5.2: Dispatch construct (7.6)
+//===----------------------------------------------------------------------===//
+
+void DispatchOp::build(OpBuilder &builder, OperationState &state,
+ const DispatchOperands &clauses) {
+ DispatchOp::build(builder, state, clauses.nocontext, clauses.novariants,
+ clauses.nowait);
+}
+
//===----------------------------------------------------------------------===//
// Spec 5.2: Scan construct (5.6)
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index e5e3e0cc6d944..d8b596ad9c83e 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -572,6 +572,15 @@ static LogicalResult checkImplementationStatus(Operation &op) {
checkTaskReductionByref(op, result);
})
.Case([&](omp::TaskwaitOp op) { checkNowait(op, result); })
+ .Case([&](omp::DispatchOp op) {
+ // OpenMP 5.1 dispatch creates an explicit task; nowait controls whether
+ // it is included. Diagnose unsupported asynchronous tasking before 5.2,
+ // where the nowait property has no effect on dispatch.
+ int64_t version = omp::getOpenMPVersionAttribute(
+ op->getParentOfType<ModuleOp>(), /*fallback=*/51);
+ if (version < 52)
+ checkNowait(op, result);
+ })
.Case([&](omp::TaskloopContextOp op) {
checkAllocate(op, result);
checkInReduction(op, result);
@@ -872,6 +881,24 @@ static llvm::omp::ProcBindKind getProcBindKind(omp::ClauseProcBindKind kind) {
llvm_unreachable("Unknown ClauseProcBindKind kind");
}
+/// Convert 'dispatch' operation into LLVM IR.
+static LogicalResult
+convertOmpDispatch(Operation &opInst, llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ auto dispatchOp = cast<omp::DispatchOp>(opInst);
+
+ if (failed(checkImplementationStatus(opInst)))
+ return failure();
+
+ auto ®ion = dispatchOp.getRegion();
+ auto result = convertOmpOpRegions(region, "omp.dispatch.region", builder,
+ moduleTranslation);
+ if (!result)
+ return handleError(result.takeError(), opInst);
+ builder.SetInsertPoint(*result);
+ return success();
+}
+
/// Converts an OpenMP 'masked' operation into LLVM IR using OpenMPIRBuilder.
static LogicalResult
convertOmpMasked(Operation &opInst, llvm::IRBuilderBase &builder,
@@ -10614,6 +10641,9 @@ LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(
.Case([&](omp::ParallelOp op) {
return convertOmpParallel(op, builder, moduleTranslation);
})
+ .Case([&](omp::DispatchOp) {
+ return convertOmpDispatch(*op, builder, moduleTranslation);
+ })
.Case([&](omp::MaskedOp) {
return convertOmpMasked(*op, builder, moduleTranslation);
})
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index d20ed5871c8d1..49646e3c64ddf 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -4515,3 +4515,121 @@ func.func @omp_interop_depend(%obj : !llvm.ptr, %dep : !llvm.ptr) -> () {
omp.interop.destroy %obj : !llvm.ptr depend(taskdependout -> %dep : !llvm.ptr)
return
}
+
+// -----
+
+// Variant selection (e.g. from Fortran `declare variant`) is resolved by the
+// producer of the region, so at the MLIR level the dispatch region simply wraps
+// a call to the selected variant procedure.
+
+// CHECK-LABEL: func.func @omp_dispatch
+// CHECK-SAME: (%[[X:.*]]: memref<i32>)
+func.func @omp_dispatch(%x : memref<i32>) -> () {
+ // CHECK: omp.dispatch {
+ // CHECK-NEXT: func.call @variant(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ omp.dispatch {
+ func.call @variant(%x) : (memref<i32>) -> ()
+ omp.terminator
+ }
+ return
+}
+
+// Test that the generic form of omp.dispatch roundtrips to pretty-printed form.
+// CHECK-LABEL: func.func @omp_dispatch_generic_to_pretty
+// CHECK-SAME: (%[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_generic_to_pretty(%x : memref<i32>) -> () {
+ // A plain call (outside any dispatch region) is left untouched.
+ // CHECK: call @omp_dispatch(%[[X]]) : (memref<i32>) -> ()
+ func.call @omp_dispatch(%x) : (memref<i32>) -> ()
+ // CHECK: omp.dispatch {
+ // CHECK-NEXT: func.call @variant(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ "omp.dispatch" () ({
+ func.call @variant(%x) : (memref<i32>) -> ()
+ "omp.terminator" () : () -> ()
+ }) : () -> ()
+ return
+}
+
+// Test the nowait clause on omp.dispatch.
+// CHECK-LABEL: func.func @omp_dispatch_nowait
+// CHECK-SAME: (%[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_nowait(%x : memref<i32>) -> () {
+ // CHECK: omp.dispatch nowait {
+ // CHECK-NEXT: func.call @variant(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ omp.dispatch nowait {
+ func.call @variant(%x) : (memref<i32>) -> ()
+ omp.terminator
+ }
+ return
+}
+
+// novariants clause round-trip; the producer of the region materializes the
+// base/variant selection inside the region.
+// CHECK-LABEL: func.func @omp_dispatch_novariants
+// CHECK-SAME: (%[[COND:.*]]: i1, %[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_novariants(%cond : i1, %x : memref<i32>) -> () {
+ // CHECK: omp.dispatch novariants(%[[COND]]) {
+ // CHECK-NEXT: func.call @variant(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ omp.dispatch novariants(%cond) {
+ func.call @variant(%x) : (memref<i32>) -> ()
+ omp.terminator
+ }
+ return
+}
+
+// novariants and nowait together.
+// CHECK-LABEL: func.func @omp_dispatch_novariants_nowait
+// CHECK-SAME: (%[[COND:.*]]: i1, %[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_novariants_nowait(%cond : i1, %x : memref<i32>) -> () {
+ // CHECK: omp.dispatch novariants(%[[COND]]) nowait {
+ // CHECK-NEXT: func.call @variant(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ omp.dispatch novariants(%cond) nowait {
+ func.call @variant(%x) : (memref<i32>) -> ()
+ omp.terminator
+ }
+ return
+}
+
+// nocontext clause round-trip; the producer of the region materializes the
+// base/variant selection inside the region.
+// CHECK-LABEL: func.func @omp_dispatch_nocontext
+// CHECK-SAME: (%[[COND:.*]]: i1, %[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_nocontext(%cond : i1, %x : memref<i32>) -> () {
+ // CHECK: omp.dispatch nocontext(%[[COND]]) {
+ // CHECK-NEXT: func.call @variant(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ omp.dispatch nocontext(%cond) {
+ func.call @variant(%x) : (memref<i32>) -> ()
+ omp.terminator
+ }
+ return
+}
+
+// nocontext and novariants together.
+// CHECK-LABEL: func.func @omp_dispatch_nocontext_novariants
+// CHECK-SAME: (%[[COND:.*]]: i1, %[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_nocontext_novariants(%cond : i1, %x : memref<i32>) -> () {
+ // CHECK: omp.dispatch nocontext(%[[COND]]) novariants(%[[COND]]) {
+ // CHECK-NEXT: func.call @variant(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ omp.dispatch nocontext(%cond) novariants(%cond) {
+ func.call @variant(%x) : (memref<i32>) -> ()
+ omp.terminator
+ }
+ return
+}
+
+// CHECK-LABEL: func.func private @variant(memref<i32>)
+func.func private @variant(memref<i32>) -> ()
diff --git a/mlir/test/Target/LLVMIR/openmp-dispatch.mlir b/mlir/test/Target/LLVMIR/openmp-dispatch.mlir
new file mode 100644
index 0000000000000..15eb085c73029
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-dispatch.mlir
@@ -0,0 +1,111 @@
+// RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s
+
+module attributes {omp.is_target_device = false, omp.is_gpu = false, omp.version = #omp.version<version = 51>} {
+ // CHECK-LABEL: define void @_QMfuncsPfoo_variant()
+ llvm.func @_QMfuncsPfoo_variant() {
+ llvm.return
+ }
+ // CHECK-LABEL: define void @_QMfuncsPfoo_dispatch()
+ llvm.func @_QMfuncsPfoo_dispatch() {
+ llvm.return
+ }
+ // CHECK-LABEL: define void @_QQmain()
+ llvm.func @_QQmain() {
+ // CHECK: call void @_QMfuncsPfoo_dispatch()
+ llvm.call @_QMfuncsPfoo_dispatch() : () -> ()
+ // CHECK: br label %omp.dispatch.region
+ // CHECK: omp.dispatch.region:
+ // The producer of the region has already materialized the base/variant
+ // selection; MLIR only translates the call inside the region.
+ omp.dispatch {
+ // CHECK: call void @_QMfuncsPfoo_variant()
+ llvm.call @_QMfuncsPfoo_variant() : () -> ()
+ // CHECK: br label %omp.region.cont
+ omp.terminator
+ }
+ // CHECK: omp.region.cont:
+ llvm.return
+ }
+ // The producer of the region materializes the base/variant selection; the
+ // LLVM IR translation deliberately ignores the novariants operand.
+ // CHECK-LABEL: define void @test_dispatch_novariants(i1
+ llvm.func @test_dispatch_novariants(%cond : i1) {
+ // CHECK: br label %omp.dispatch.region
+ // CHECK: omp.dispatch.region:
+ omp.dispatch novariants(%cond) {
+ // CHECK: call void @_QMfuncsPfoo_variant()
+ llvm.call @_QMfuncsPfoo_variant() : () -> ()
+ // CHECK: br label %omp.region.cont
+ omp.terminator
+ }
+ // CHECK: omp.region.cont:
+ llvm.return
+ }
+ // Preserve the producer's runtime selection across a multi-block region.
+ // CHECK-LABEL: define void @test_dispatch_runtime_novariants(
+ // CHECK-SAME: i1 %[[COND:[^ )]+]])
+ // CHECK: br label %[[ENTRY:[a-zA-Z0-9._]+]]
+ // CHECK: [[MERGE:[a-zA-Z0-9._]+]]:
+ // CHECK-NEXT: br label %[[CONT:[a-zA-Z0-9._]+]]
+ // CHECK: [[VARIANT:[a-zA-Z0-9._]+]]:
+ // CHECK-NEXT: call void @_QMfuncsPfoo_variant()
+ // CHECK-NEXT: br label %[[MERGE]]
+ // CHECK: [[BASE:[a-zA-Z0-9._]+]]:
+ // CHECK-NEXT: call void @_QMfuncsPfoo_dispatch()
+ // CHECK-NEXT: br label %[[MERGE]]
+ // CHECK: [[ENTRY]]:
+ // CHECK-NEXT: br i1 %[[COND]], label %[[BASE]], label %[[VARIANT]]
+ // CHECK: [[CONT]]:
+ // CHECK-NEXT: ret void
+ llvm.func @test_dispatch_runtime_novariants(%cond : i1) {
+ omp.dispatch novariants(%cond) {
+ llvm.cond_br %cond, ^base, ^variant
+ ^base:
+ llvm.call @_QMfuncsPfoo_dispatch() : () -> ()
+ llvm.br ^merge
+ ^variant:
+ llvm.call @_QMfuncsPfoo_variant() : () -> ()
+ llvm.br ^merge
+ ^merge:
+ omp.terminator
+ }
+ llvm.return
+ }
+ // The producer of the region materializes the base/variant selection; the
+ // LLVM IR translation deliberately ignores the nocontext operand.
+ // CHECK-LABEL: define void @test_dispatch_nocontext(i1
+ llvm.func @test_dispatch_nocontext(%cond : i1) {
+ // CHECK: br label %omp.dispatch.region
+ // CHECK: omp.dispatch.region:
+ omp.dispatch nocontext(%cond) {
+ // CHECK: call void @_QMfuncsPfoo_variant()
+ llvm.call @_QMfuncsPfoo_variant() : () -> ()
+ // CHECK: br label %omp.region.cont
+ omp.terminator
+ }
+ // CHECK: omp.region.cont:
+ llvm.return
+ }
+}
+
+// -----
+
+// In OpenMP 5.2 and later `nowait` has no effect on `dispatch`; it is accepted
+// and translated as a no-op (contrast openmp-todo.mlir, which diagnoses it for
+// OpenMP < 5.2). Version 52 is used here as a representative of all >= 5.2.
+module attributes {omp.is_target_device = false, omp.is_gpu = false, omp.version = #omp.version<version = 52>} {
+ llvm.func @variant()
+ // CHECK-LABEL: define void @dispatch_nowait_52()
+ llvm.func @dispatch_nowait_52() {
+ // CHECK: br label %omp.dispatch.region
+ // CHECK: omp.dispatch.region:
+ omp.dispatch nowait {
+ // CHECK: call void @variant()
+ llvm.call @variant() : () -> ()
+ // CHECK: br label %omp.region.cont
+ omp.terminator
+ }
+ // CHECK: omp.region.cont:
+ llvm.return
+ }
+}
diff --git a/mlir/test/Target/LLVMIR/openmp-todo.mlir b/mlir/test/Target/LLVMIR/openmp-todo.mlir
index 635665c653f8d..43063cdf1c82d 100644
--- a/mlir/test/Target/LLVMIR/openmp-todo.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-todo.mlir
@@ -638,6 +638,21 @@ llvm.func @taskwait_nowait() {
// -----
+// `nowait` on dispatch is unimplemented for OpenMP <= 5.1; from 5.2 it has no
+// effect and is accepted (see openmp-dispatch.mlir).
+module attributes {omp.version = #omp.version<version = 51>} {
+ llvm.func @dispatch_nowait() {
+ // expected-error at below {{not yet implemented: Unhandled clause nowait in omp.dispatch operation}}
+ // expected-error at below {{LLVM Translation failed for operation: omp.dispatch}}
+ omp.dispatch nowait {
+ omp.terminator
+ }
+ llvm.return
+ }
+}
+
+// -----
+
llvm.func @teams_allocate(%x : !llvm.ptr) {
// expected-error at below {{not yet implemented: Unhandled clause allocate in omp.teams operation}}
// expected-error at below {{LLVM Translation failed for operation: omp.teams}}
More information about the flang-commits
mailing list