[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 &region = 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 &region = 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