[flang-commits] [flang] [mlir] [flang][OpenMP] Support for "!$omp dispatch". (PR #203320)
via flang-commits
flang-commits at lists.llvm.org
Thu Jun 18 09:54:14 PDT 2026
https://github.com/SunilKuravinakop updated https://github.com/llvm/llvm-project/pull/203320
>From d007fc287982ed638ef3900307ca700f0d529f87 Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Thu, 11 Jun 2026 10:56:08 -0500
Subject: [PATCH 1/3] Support for "!$omp dispatch".
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 33 ++++++-
flang/test/Integration/OpenMP/dispatch.f90 | 76 ++++++++++++++++
flang/test/Lower/OpenMP/Todo/dispatch.f90 | 12 ---
flang/test/Lower/OpenMP/dispatch.f90 | 89 +++++++++++++++++++
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 15 ++++
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 17 ++++
mlir/test/Dialect/OpenMP/dispatch.mlir | 77 ++++++++++++++++
mlir/test/Target/LLVMIR/openmp-dispatch.mlir | 81 +++++++++++++++++
8 files changed, 385 insertions(+), 15 deletions(-)
create mode 100644 flang/test/Integration/OpenMP/dispatch.f90
delete mode 100644 flang/test/Lower/OpenMP/Todo/dispatch.f90
create mode 100644 flang/test/Lower/OpenMP/dispatch.f90
create mode 100644 mlir/test/Dialect/OpenMP/dispatch.mlir
create mode 100644 mlir/test/Target/LLVMIR/openmp-dispatch.mlir
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index be876c563433a..758d8eb8148e5 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2592,6 +2592,18 @@ genMasterOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
queue, item);
}
+static mlir::omp::DispatchOp
+genDispatchOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
+ semantics::SemanticsContext &semaCtx,
+ lower::pft::Evaluation &eval, mlir::Location loc,
+ const ConstructQueue &queue,
+ ConstructQueue::const_iterator item) {
+ return genOpWithBody<mlir::omp::DispatchOp>(
+ OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,
+ llvm::omp::Directive::OMPD_dispatch),
+ queue, item);
+}
+
static mlir::omp::OrderedOp
genOrderedOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
@@ -3940,6 +3952,9 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
case llvm::omp::Directive::OMPD_barrier:
newOp = genBarrierOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
+ case llvm::omp::Directive::OMPD_dispatch:
+ newOp = genDispatchOp(converter, symTable, semaCtx, eval, loc, queue, item);
+ break;
case llvm::omp::Directive::OMPD_distribute:
newOp = genStandaloneDistribute(converter, symTable, stmtCtx, semaCtx, eval,
loc, queue, item);
@@ -5312,9 +5327,21 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
- const parser::OpenMPDispatchConstruct &) {
- if (!semaCtx.langOptions().OpenMPSimd)
- TODO(converter.getCurrentLocation(), "OpenMPDispatchConstruct");
+ const parser::OpenMPDispatchConstruct &dispatchConstruct) {
+ const parser::OmpDirectiveSpecification &beginSpec =
+ dispatchConstruct.BeginDir();
+ List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx);
+ if (auto &endSpec = dispatchConstruct.EndDir())
+ clauses.append(makeClauses(endSpec->Clauses(), semaCtx));
+
+ llvm::omp::Directive directive = beginSpec.DirId();
+ mlir::Location currentLocation = converter.genLocation(beginSpec.source);
+
+ ConstructQueue queue{
+ buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,
+ eval, beginSpec.source, directive, clauses)};
+ genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,
+ queue.begin());
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
diff --git a/flang/test/Integration/OpenMP/dispatch.f90 b/flang/test/Integration/OpenMP/dispatch.f90
new file mode 100644
index 0000000000000..c4d16690c8e29
--- /dev/null
+++ b/flang/test/Integration/OpenMP/dispatch.f90
@@ -0,0 +1,76 @@
+!===----------------------------------------------------------------------===!
+! This directory can be used to add Integration tests involving multiple
+! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
+! contain executable tests. We should only add tests here sparingly and only
+! if there is no other way to test. Repeat this message in each test that is
+! added to this directory and sub-directories.
+!===----------------------------------------------------------------------===!
+
+!RUN: %flang_fc1 -emit-llvm -fopenmp %s -o - | FileCheck %s
+
+!CHECK-LABEL: define void @_QMfuncsPfoo_variant1()
+!CHECK: call ptr @_FortranAioBeginExternalListOutput
+
+!CHECK-LABEL: define void @_QMfuncsPfoo_variant2()
+!CHECK: call ptr @_FortranAioBeginExternalListOutput
+
+!CHECK-LABEL: define void @_QMfuncsPfoo_dispatch()
+!CHECK: %[[COND:.*]] = load i32, ptr @_QMfuncsEfoo_sub
+!CHECK: %[[CMP:.*]] = icmp ne i32 %[[COND]], 0
+!CHECK: br i1 %[[CMP]], label %[[IF_TRUE:.*]], label %[[IF_FALSE:.*]]
+!CHECK: [[IF_TRUE]]:
+!CHECK: call void @_QMfuncsPfoo_variant2()
+!CHECK: [[IF_FALSE]]:
+!CHECK: call void @_QMfuncsPfoo_variant1()
+
+!CHECK-LABEL: define void @_QQmain()
+!CHECK: store i32 0, ptr @_QMfuncsEfoo_sub
+!CHECK: br label %omp.dispatch.region
+!CHECK: omp.dispatch.region:
+!CHECK: call void @_QMfuncsPfoo_dispatch()
+!CHECK: br label %omp.region.cont
+!CHECK: omp.region.cont:
+!CHECK: store i32 1, ptr @_QMfuncsEfoo_sub
+!CHECK: br label %omp.dispatch.region2
+!CHECK: omp.dispatch.region2:
+!CHECK: call void @_QMfuncsPfoo_dispatch()
+!CHECK: br label %omp.region.cont1
+!CHECK: omp.region.cont1:
+
+module funcs
+ implicit none
+ logical :: foo_sub
+
+contains
+
+ subroutine foo_variant1()
+ print *, "in foo_variant1"
+ end subroutine
+
+ subroutine foo_variant2()
+ print *, "in foo_variant2"
+ end subroutine
+
+ subroutine foo_dispatch()
+ if (foo_sub) then
+ call foo_variant2()
+ else
+ call foo_variant1()
+ end if
+ end subroutine
+
+end module funcs
+
+program dispatch_test
+ use funcs
+ implicit none
+
+ foo_sub = .false.
+ !$omp dispatch
+ call foo_dispatch()
+
+ foo_sub = .true.
+ !$omp dispatch
+ call foo_dispatch()
+
+end program
diff --git a/flang/test/Lower/OpenMP/Todo/dispatch.f90 b/flang/test/Lower/OpenMP/Todo/dispatch.f90
deleted file mode 100644
index 380dfa14eaae1..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/dispatch.f90
+++ /dev/null
@@ -1,12 +0,0 @@
-! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=51 -o - %s 2>&1 | FileCheck %s
-
-! CHECK: not yet implemented: OpenMPDispatchConstruct
-program p
- integer r
- r = 1
-!$omp dispatch nowait
- call foo()
-contains
- subroutine foo
- end subroutine
-end program p
diff --git a/flang/test/Lower/OpenMP/dispatch.f90 b/flang/test/Lower/OpenMP/dispatch.f90
new file mode 100644
index 0000000000000..df86503817163
--- /dev/null
+++ b/flang/test/Lower/OpenMP/dispatch.f90
@@ -0,0 +1,89 @@
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s --check-prefix=HLFIR
+!RUN: %flang_fc1 -emit-mlir -fopenmp %s -o - | FileCheck %s --check-prefix=FIR
+
+!HLFIR-LABEL: func @_QMfuncsPfoo_dispatch
+!HLFIR: %[[XD_H:.*]]:2 = hlfir.declare %{{.*}} {{{.*}}uniq_name = {{.*}}foo_dispatch{{.*}}x{{.*}}
+!HLFIR: %[[LOAD_H:.*]] = fir.load %[[XD_H]]#0 : !fir.ref<i32>
+!HLFIR: %[[C1_H:.*]] = arith.constant 1 : i32
+!HLFIR: %[[CMP_H:.*]] = arith.cmpi eq, %[[LOAD_H]], %[[C1_H]] : i32
+!HLFIR: fir.if %[[CMP_H]] {
+!HLFIR: fir.call @_QMfuncsPvariant1() {{.*}}: () -> ()
+!HLFIR: } else {
+!HLFIR: fir.call @_QMfuncsPvariant2() {{.*}}: () -> ()
+!HLFIR: }
+
+!FIR-LABEL: func @_QMfuncsPfoo_dispatch
+!FIR: %[[XD_F:.*]] = fir.declare %{{.*}} {{{.*}}uniq_name = {{.*}}foo_dispatch{{.*}}x{{.*}}
+!FIR: %[[LOAD_F:.*]] = fir.load %[[XD_F]] : !fir.ref<i32>
+!FIR: %[[CMP_F:.*]] = arith.cmpi eq, %[[LOAD_F]], %{{.*}} : i32
+!FIR: fir.if %[[CMP_F]] {
+!FIR: fir.call @_QMfuncsPvariant1() {{.*}}: () -> ()
+!FIR: } else {
+!FIR: fir.call @_QMfuncsPvariant2() {{.*}}: () -> ()
+!FIR: }
+
+module funcs
+ implicit none
+
+contains
+
+ subroutine variant1()
+ print *, "in variant1"
+ end subroutine
+
+ subroutine variant2()
+ print *, "in variant2"
+ end subroutine
+
+ subroutine foo_dispatch(x)
+ integer, intent(in) :: x
+ if (x == 1) then
+ call variant1()
+ else
+ call variant2()
+ end if
+ end subroutine
+
+end module funcs
+
+!HLFIR-LABEL: func @_QQmain
+!FIR-LABEL: func @_QQmain
+!FIR-DAG: %[[C1:.*]] = arith.constant 1 : i32
+!FIR-DAG: %[[C2:.*]] = arith.constant 2 : i32
+!FIR: %[[X:.*]] = fir.declare %{{.*}} {uniq_name = {{.*}}x{{.*}}
+program dispatch_test
+ use funcs
+ implicit none
+ integer :: x
+
+ !HLFIR: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = {{.*}}x{{.*}}
+ !HLFIR: %[[C1:.*]] = arith.constant 1 : i32
+ !HLFIR: hlfir.assign %[[C1]] to %[[X]]#0 : i32, !fir.ref<i32>
+ !FIR: fir.store %[[C1]] to %[[X]] : !fir.ref<i32>
+ x = 1
+ !HLFIR: omp.dispatch {
+ !FIR: omp.dispatch {
+ !$omp dispatch
+ !HLFIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]#0) {{.*}}: (!fir.ref<i32>) -> ()
+ !FIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]) {{.*}}: (!fir.ref<i32>) -> ()
+ call foo_dispatch(x)
+ !HLFIR: omp.terminator
+ !FIR: omp.terminator
+ !HLFIR: }
+ !FIR: }
+
+ !HLFIR: %[[C2:.*]] = arith.constant 2 : i32
+ !HLFIR: hlfir.assign %[[C2]] to %[[X]]#0 : i32, !fir.ref<i32>
+ !FIR: fir.store %[[C2]] to %[[X]] : !fir.ref<i32>
+ x = 2
+ !HLFIR: omp.dispatch {
+ !FIR: omp.dispatch {
+ !$omp dispatch
+ !HLFIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]#0) {{.*}}: (!fir.ref<i32>) -> ()
+ !FIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]) {{.*}}: (!fir.ref<i32>) -> ()
+ call foo_dispatch(x)
+ !HLFIR: omp.terminator
+ !FIR: omp.terminator
+ !HLFIR: }
+ !FIR: }
+end program
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 51e7080db5b29..6170e40a835c6 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2286,6 +2286,21 @@ def MaskedOp : OpenMP_Op<"masked", clauses = [
];
}
+//===----------------------------------------------------------------------===//
+// [Spec 5.1] 12.3 dispatch Construct
+//===----------------------------------------------------------------------===//
+def DispatchOp : OpenMP_Op<"dispatch", singleRegion = true> {
+ let summary = "dispatch construct";
+ let description = [{
+ The dispatch construct enables the invocation of a variant of a
+ base procedure. The structured block of a dispatch construct is a
+ single expression statement that contains a function call or a
+ subroutine call.
+ }];
+
+ let assemblyFormat = "$region attr-dict";
+}
+
//===----------------------------------------------------------------------===//
// [Spec 5.2] 6.5 allocate Directive
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index d35e8612e158b..12818a5e56ac6 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -761,6 +761,20 @@ 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);
+ 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,
@@ -8815,6 +8829,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/dispatch.mlir b/mlir/test/Dialect/OpenMP/dispatch.mlir
new file mode 100644
index 0000000000000..c935f56836185
--- /dev/null
+++ b/mlir/test/Dialect/OpenMP/dispatch.mlir
@@ -0,0 +1,77 @@
+// RUN: mlir-opt %s | mlir-opt | FileCheck %s
+
+// CHECK-LABEL: func @foo_dispatch
+// CHECK-SAME: (%[[X:.*]]: memref<i32>)
+func.func @foo_dispatch(%x : memref<i32>) -> () {
+ // CHECK: %[[V:.*]] = memref.load %[[X]][] : memref<i32>
+ // CHECK: %[[C1:.*]] = arith.constant 1 : i32
+ // CHECK: %[[CMP:.*]] = arith.cmpi eq, %[[V]], %[[C1]] : i32
+ // CHECK: cf.cond_br %[[CMP]], ^[[BB1:.*]], ^[[BB2:.*]]
+ %v = memref.load %x[] : memref<i32>
+ %c1 = arith.constant 1 : i32
+ %cmp = arith.cmpi eq, %v, %c1 : i32
+ cf.cond_br %cmp, ^bb1, ^bb2
+// CHECK: ^[[BB1]]:
+// CHECK: call @variant1() : () -> ()
+^bb1:
+ func.call @variant1() : () -> ()
+ cf.br ^bb3
+// CHECK: ^[[BB2]]:
+// CHECK: call @variant2() : () -> ()
+^bb2:
+ func.call @variant2() : () -> ()
+ cf.br ^bb3
+^bb3:
+ return
+}
+
+// Test that the generic form of omp.dispatch roundtrips to pretty-printed form.
+// CHECK-LABEL: func @omp_dispatch_generic_to_pretty
+// CHECK-SAME: (%[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_generic_to_pretty(%x : memref<i32>) -> () {
+ // CHECK: omp.dispatch {
+ // CHECK-NEXT: func.call @foo_dispatch(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ "omp.dispatch" () ({
+ func.call @foo_dispatch(%x) : (memref<i32>) -> ()
+ "omp.terminator" () : () -> ()
+ }) : () -> ()
+ return
+}
+
+// Test multiple dispatch regions with stores selecting different variants.
+// CHECK-LABEL: func @omp_dispatch_multiple
+// CHECK-SAME: (%[[X:.*]]: memref<i32>)
+func.func @omp_dispatch_multiple(%x : memref<i32>) -> () {
+ // CHECK: %[[C1:.*]] = arith.constant 1 : i32
+ // CHECK: memref.store %[[C1]], %[[X]][] : memref<i32>
+ %c1 = arith.constant 1 : i32
+ memref.store %c1, %x[] : memref<i32>
+ // CHECK: omp.dispatch {
+ // CHECK-NEXT: func.call @foo_dispatch(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ "omp.dispatch" () ({
+ "func.call" (%x) {callee = @foo_dispatch} : (memref<i32>) -> ()
+ "omp.terminator" () : () -> ()
+ }) : () -> ()
+ // CHECK: %[[C2:.*]] = arith.constant 2 : i32
+ // CHECK: memref.store %[[C2]], %[[X]][] : memref<i32>
+ %c2 = arith.constant 2 : i32
+ memref.store %c2, %x[] : memref<i32>
+ // CHECK: omp.dispatch {
+ // CHECK-NEXT: func.call @foo_dispatch(%[[X]]) : (memref<i32>) -> ()
+ // CHECK-NEXT: omp.terminator
+ // CHECK-NEXT: }
+ "omp.dispatch" () ({
+ "func.call" (%x) {callee = @foo_dispatch} : (memref<i32>) -> ()
+ "omp.terminator" () : () -> ()
+ }) : () -> ()
+ return
+}
+
+// CHECK-LABEL: func private @variant1()
+// CHECK-LABEL: func private @variant2()
+func.func private @variant1() -> ()
+func.func private @variant2() -> ()
diff --git a/mlir/test/Target/LLVMIR/openmp-dispatch.mlir b/mlir/test/Target/LLVMIR/openmp-dispatch.mlir
new file mode 100644
index 0000000000000..8a3a7a6b5d8e6
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-dispatch.mlir
@@ -0,0 +1,81 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+llvm.mlir.global external @x() {addr_space = 0 : i32} : i32 {
+ %0 = llvm.mlir.constant(0 : i32) : i32
+ llvm.return %0 : i32
+}
+
+llvm.func @variant1() -> ()
+llvm.func @variant2() -> ()
+
+// CHECK-LABEL: define void @foo_dispatch()
+llvm.func @foo_dispatch() {
+ // CHECK: %[[ADDR:.*]] = load i32, ptr @x
+ // CHECK: %[[CMP:.*]] = icmp eq i32 %[[ADDR]], 1
+ // CHECK: br i1 %[[CMP]], label %[[BB1:.*]], label %[[BB2:.*]]
+ %0 = llvm.mlir.addressof @x : !llvm.ptr
+ %1 = llvm.load %0 : !llvm.ptr -> i32
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ %cmp = llvm.icmp "eq" %1, %c1 : i32
+ llvm.cond_br %cmp, ^bb1, ^bb2
+// CHECK: [[BB1]]:
+// CHECK: call void @variant1()
+^bb1:
+ llvm.call @variant1() : () -> ()
+ llvm.br ^bb3
+// CHECK: [[BB2]]:
+// CHECK: call void @variant2()
+^bb2:
+ llvm.call @variant2() : () -> ()
+ llvm.br ^bb3
+^bb3:
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_omp_dispatch()
+llvm.func @test_omp_dispatch() {
+ // CHECK: store i32 1, ptr @x
+ %0 = llvm.mlir.addressof @x : !llvm.ptr
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ llvm.store %c1, %0 : i32, !llvm.ptr
+ // CHECK: br label %omp.dispatch.region
+ omp.dispatch {
+ // CHECK: omp.dispatch.region:
+ // CHECK-NEXT: call void @foo_dispatch()
+ llvm.call @foo_dispatch() : () -> ()
+ // CHECK-NEXT: br label %omp.region.cont
+ omp.terminator
+ }
+ // CHECK: omp.region.cont:
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_omp_dispatch_multiple()
+llvm.func @test_omp_dispatch_multiple() {
+ // CHECK: store i32 1, ptr @x
+ %0 = llvm.mlir.addressof @x : !llvm.ptr
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ llvm.store %c1, %0 : i32, !llvm.ptr
+ // CHECK: br label %omp.dispatch.region
+ omp.dispatch {
+ // CHECK: omp.dispatch.region:
+ // CHECK-NEXT: call void @foo_dispatch()
+ llvm.call @foo_dispatch() : () -> ()
+ // CHECK-NEXT: br label %omp.region.cont
+ omp.terminator
+ }
+ // CHECK: omp.region.cont:
+ // CHECK: store i32 2, ptr @x
+ %c2 = llvm.mlir.constant(2 : i32) : i32
+ llvm.store %c2, %0 : i32, !llvm.ptr
+ // CHECK: br label %omp.dispatch.region2
+ omp.dispatch {
+ // CHECK: omp.dispatch.region2:
+ // CHECK-NEXT: call void @foo_dispatch()
+ llvm.call @foo_dispatch() : () -> ()
+ // CHECK-NEXT: br label %omp.region.cont1
+ omp.terminator
+ }
+ // CHECK: omp.region.cont1:
+ llvm.return
+}
>From b50c11a322615d4e189303f9b4b73e1c6246c7ae Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Thu, 11 Jun 2026 12:19:36 -0500
Subject: [PATCH 2/3] Checking for no caluses support with "omp dispatch".
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 3 +++
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 7 ++++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 758d8eb8148e5..71bc392d0e8d9 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -5337,6 +5337,9 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
llvm::omp::Directive directive = beginSpec.DirId();
mlir::Location currentLocation = converter.genLocation(beginSpec.source);
+ if (!clauses.empty())
+ TODO(currentLocation, "OpenMP Dispatch clauses");
+
ConstructQueue queue{
buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,
eval, beginSpec.source, directive, clauses)};
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 6170e40a835c6..47c7587c5d08b 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2293,9 +2293,10 @@ def DispatchOp : OpenMP_Op<"dispatch", singleRegion = true> {
let summary = "dispatch construct";
let description = [{
The dispatch construct enables the invocation of a variant of a
- base procedure. The structured block of a dispatch construct is a
- single expression statement that contains a function call or a
- subroutine call.
+ base procedure (without any construct's clauses: device, is_device_ptr,
+ nowait, nocontext, novariants). The structured block of a dispatch
+ construct is a single expression statement that contains a function
+ call or a subroutine call.
}];
let assemblyFormat = "$region attr-dict";
>From 3ca936ebb5aff08e318fcd4abede5951e77fcb07 Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Thu, 18 Jun 2026 11:50:04 -0500
Subject: [PATCH 3/3] Handling feedbacks suggested by Sergio Afonso.
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 37 ++++++++++++-------
flang/test/Lower/OpenMP/dispatch.f90 | 26 +------------
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 11 +++---
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 4 ++
mlir/test/Dialect/OpenMP/dispatch.mlir | 1 +
mlir/test/Target/LLVMIR/openmp-dispatch.mlir | 1 +
6 files changed, 36 insertions(+), 44 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 33e67ed2c3ab7..fe7d17807727a 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1661,6 +1661,16 @@ static void genDistributeClauses(lower::AbstractConverter &converter,
cp.processOrder(clauseOps);
}
+static void genDispatchClauses(lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx,
+ const List<Clause> &clauses,
+ mlir::Location loc) {
+ ClauseProcessor cp(converter, semaCtx, clauses);
+ cp.processTODO<clause::Depend, clause::Device, clause::IsDevicePtr,
+ clause::Nocontext, clause::Novariants, clause::Nowait>(
+ loc, llvm::omp::Directive::OMPD_dispatch);
+}
+
static void genFlushClauses(lower::AbstractConverter &converter,
semantics::SemanticsContext &semaCtx,
const ObjectList &objects,
@@ -2154,6 +2164,18 @@ genCriticalOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
queue, item, nameAttr);
}
+static mlir::omp::DispatchOp
+genDispatchOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
+ semantics::SemanticsContext &semaCtx,
+ lower::pft::Evaluation &eval, mlir::Location loc,
+ const ConstructQueue &queue,
+ ConstructQueue::const_iterator item) {
+ return genOpWithBody<mlir::omp::DispatchOp>(
+ OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,
+ llvm::omp::Directive::OMPD_dispatch),
+ queue, item);
+}
+
static mlir::omp::FlushOp
genFlushOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
@@ -2592,18 +2614,6 @@ genMasterOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
queue, item);
}
-static mlir::omp::DispatchOp
-genDispatchOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
- semantics::SemanticsContext &semaCtx,
- lower::pft::Evaluation &eval, mlir::Location loc,
- const ConstructQueue &queue,
- ConstructQueue::const_iterator item) {
- return genOpWithBody<mlir::omp::DispatchOp>(
- OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,
- llvm::omp::Directive::OMPD_dispatch),
- queue, item);
-}
-
static mlir::omp::OrderedOp
genOrderedOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
@@ -5294,8 +5304,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
llvm::omp::Directive directive = beginSpec.DirId();
mlir::Location currentLocation = converter.genLocation(beginSpec.source);
- if (!clauses.empty())
- TODO(currentLocation, "OpenMP Dispatch clauses");
+ genDispatchClauses(converter, semaCtx, clauses, currentLocation);
ConstructQueue queue{
buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,
diff --git a/flang/test/Lower/OpenMP/dispatch.f90 b/flang/test/Lower/OpenMP/dispatch.f90
index df86503817163..ecc7b1b31454f 100644
--- a/flang/test/Lower/OpenMP/dispatch.f90
+++ b/flang/test/Lower/OpenMP/dispatch.f90
@@ -1,5 +1,4 @@
!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s --check-prefix=HLFIR
-!RUN: %flang_fc1 -emit-mlir -fopenmp %s -o - | FileCheck %s --check-prefix=FIR
!HLFIR-LABEL: func @_QMfuncsPfoo_dispatch
!HLFIR: %[[XD_H:.*]]:2 = hlfir.declare %{{.*}} {{{.*}}uniq_name = {{.*}}foo_dispatch{{.*}}x{{.*}}
@@ -12,16 +11,6 @@
!HLFIR: fir.call @_QMfuncsPvariant2() {{.*}}: () -> ()
!HLFIR: }
-!FIR-LABEL: func @_QMfuncsPfoo_dispatch
-!FIR: %[[XD_F:.*]] = fir.declare %{{.*}} {{{.*}}uniq_name = {{.*}}foo_dispatch{{.*}}x{{.*}}
-!FIR: %[[LOAD_F:.*]] = fir.load %[[XD_F]] : !fir.ref<i32>
-!FIR: %[[CMP_F:.*]] = arith.cmpi eq, %[[LOAD_F]], %{{.*}} : i32
-!FIR: fir.if %[[CMP_F]] {
-!FIR: fir.call @_QMfuncsPvariant1() {{.*}}: () -> ()
-!FIR: } else {
-!FIR: fir.call @_QMfuncsPvariant2() {{.*}}: () -> ()
-!FIR: }
-
module funcs
implicit none
@@ -35,6 +24,7 @@ subroutine variant2()
print *, "in variant2"
end subroutine
+ !TODO: replace with declare_variant when the support is merged.
subroutine foo_dispatch(x)
integer, intent(in) :: x
if (x == 1) then
@@ -47,10 +37,6 @@ subroutine foo_dispatch(x)
end module funcs
!HLFIR-LABEL: func @_QQmain
-!FIR-LABEL: func @_QQmain
-!FIR-DAG: %[[C1:.*]] = arith.constant 1 : i32
-!FIR-DAG: %[[C2:.*]] = arith.constant 2 : i32
-!FIR: %[[X:.*]] = fir.declare %{{.*}} {uniq_name = {{.*}}x{{.*}}
program dispatch_test
use funcs
implicit none
@@ -59,31 +45,21 @@ program dispatch_test
!HLFIR: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = {{.*}}x{{.*}}
!HLFIR: %[[C1:.*]] = arith.constant 1 : i32
!HLFIR: hlfir.assign %[[C1]] to %[[X]]#0 : i32, !fir.ref<i32>
- !FIR: fir.store %[[C1]] to %[[X]] : !fir.ref<i32>
x = 1
!HLFIR: omp.dispatch {
- !FIR: omp.dispatch {
!$omp dispatch
!HLFIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]#0) {{.*}}: (!fir.ref<i32>) -> ()
- !FIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]) {{.*}}: (!fir.ref<i32>) -> ()
call foo_dispatch(x)
!HLFIR: omp.terminator
- !FIR: omp.terminator
!HLFIR: }
- !FIR: }
!HLFIR: %[[C2:.*]] = arith.constant 2 : i32
!HLFIR: hlfir.assign %[[C2]] to %[[X]]#0 : i32, !fir.ref<i32>
- !FIR: fir.store %[[C2]] to %[[X]] : !fir.ref<i32>
x = 2
!HLFIR: omp.dispatch {
- !FIR: omp.dispatch {
!$omp dispatch
!HLFIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]#0) {{.*}}: (!fir.ref<i32>) -> ()
- !FIR: fir.call @_QMfuncsPfoo_dispatch(%[[X]]) {{.*}}: (!fir.ref<i32>) -> ()
call foo_dispatch(x)
!HLFIR: omp.terminator
- !FIR: omp.terminator
!HLFIR: }
- !FIR: }
end program
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 47c7587c5d08b..490bee24799c2 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2289,14 +2289,15 @@ def MaskedOp : OpenMP_Op<"masked", clauses = [
//===----------------------------------------------------------------------===//
// [Spec 5.1] 12.3 dispatch Construct
//===----------------------------------------------------------------------===//
-def DispatchOp : OpenMP_Op<"dispatch", singleRegion = true> {
+def DispatchOp : OpenMP_Op<"dispatch", singleRegion = true, clauses = [
+ // TODO: Complete clause lists (device, is_device_ptr,nowait, nocontext, novariants).
+ ]> {
let summary = "dispatch construct";
let description = [{
The dispatch construct enables the invocation of a variant of a
- base procedure (without any construct's clauses: device, is_device_ptr,
- nowait, nocontext, novariants). The structured block of a dispatch
- construct is a single expression statement that contains a function
- call or a subroutine call.
+ base procedure. The structured block of a dispatch construct is
+ a single expression statement that contains a function call or
+ a subroutine call.
}];
let assemblyFormat = "$region attr-dict";
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 12818a5e56ac6..04c36b87d73ad 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -766,6 +766,10 @@ 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);
diff --git a/mlir/test/Dialect/OpenMP/dispatch.mlir b/mlir/test/Dialect/OpenMP/dispatch.mlir
index c935f56836185..b8d4b93a6c8e3 100644
--- a/mlir/test/Dialect/OpenMP/dispatch.mlir
+++ b/mlir/test/Dialect/OpenMP/dispatch.mlir
@@ -41,6 +41,7 @@ func.func @omp_dispatch_generic_to_pretty(%x : memref<i32>) -> () {
}
// Test multiple dispatch regions with stores selecting different variants.
+// TODO: Use declare_variant when the support is merged.
// CHECK-LABEL: func @omp_dispatch_multiple
// CHECK-SAME: (%[[X:.*]]: memref<i32>)
func.func @omp_dispatch_multiple(%x : memref<i32>) -> () {
diff --git a/mlir/test/Target/LLVMIR/openmp-dispatch.mlir b/mlir/test/Target/LLVMIR/openmp-dispatch.mlir
index 8a3a7a6b5d8e6..d947a871ca30c 100644
--- a/mlir/test/Target/LLVMIR/openmp-dispatch.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-dispatch.mlir
@@ -8,6 +8,7 @@ llvm.mlir.global external @x() {addr_space = 0 : i32} : i32 {
llvm.func @variant1() -> ()
llvm.func @variant2() -> ()
+// TODO: Add support for declare_variant, when support to it is merged.
// CHECK-LABEL: define void @foo_dispatch()
llvm.func @foo_dispatch() {
// CHECK: %[[ADDR:.*]] = load i32, ptr @x
More information about the flang-commits
mailing list