[flang-commits] [flang] [mlir] [Flang][MLIR][OpenMP] Move function filtering to the omp dialect (PR #214182)

Sergio Afonso via flang-commits flang-commits at lists.llvm.org
Mon Aug 10 06:34:07 PDT 2026


https://github.com/skatrak updated https://github.com/llvm/llvm-project/pull/214182

>From 0fd7db2e0014504c97e517190e929df638d2036a Mon Sep 17 00:00:00 2001
From: Sergio Afonso <Sergio.AfonsoFumero at amd.com>
Date: Thu, 23 Jul 2026 15:43:17 +0100
Subject: [PATCH 1/2] [Flang][MLIR][OpenMP] Move function filtering to the omp
 dialect

The `FunctionFilteringPass`, which removes host-only functions when
compiling for an OpenMP target device, is currently defined only for
Flang. However, it implements logic that would generally be useful for
other frontends that can generate OpenMP offloading code. This patch
makes that transition by implementing the following changes:
- It rewrites the `FunctionFilteringPass` to work on lower level non-FIR
  MLIR modules.
- It splits off preexisting logic to check for not-yet-implemented target
  device features during function filtering to its own pass and it improves
  context detection to properly diagnostic only device code.
- It delays function filtering to run at the end of the pipeline, as well
  as the `MarkDeclareTargetPass`. This will enable the latter to run
  only once in the pipeline after the `UnimplementedDeviceCheckPass`
  becomes no longer necessary.

One side effect of these changes is that delaying the filtering will
cause all following passes to process host functions that are eventually
deleted. On the other hand, it minimizes divergence between host and
device MLIR modules making passes such as the `DeleteUnreachableTargets`
no longer necessary, as well as allowing passes to introduce new host
functions without causing problems on the device.
---
 .../include/flang/Optimizer/OpenMP/Passes.td  |  26 +--
 flang/lib/Optimizer/OpenMP/CMakeLists.txt     |   2 +-
 .../Optimizer/OpenMP/FunctionFiltering.cpp    | 155 ------------------
 .../OpenMP/UnimplementedDeviceCheck.cpp       |  98 +++++++++++
 flang/lib/Optimizer/Passes/Pipelines.cpp      |  27 ++-
 flang/test/Fir/basic-program.fir              |   5 +-
 .../OpenMP/function-filtering-2.f90           |  61 +++++++
 .../OpenMP/function-filtering-3.f90           |  51 ++++++
 .../OpenMP/function-filtering-4.f90           |  33 ++--
 .../OpenMP/function-filtering.f90             |  38 +++--
 ...are-target-deferred-marking-reductions.f90 |   9 +-
 .../declare-target-deferred-marking.f90       |  12 +-
 ...arget-implicit-func-and-subr-cap-enter.f90 |  48 +++---
 ...lare-target-implicit-func-and-subr-cap.f90 |  52 +++---
 .../declare-target-implicit-tarop-cap.f90     |  22 +--
 .../declare-target-named-main-interface.f90   |  17 +-
 .../Lower/OpenMP/function-filtering-2.f90     |  49 ------
 .../Lower/OpenMP/function-filtering-3.f90     |  34 ----
 .../Transforms/OpenMP/function-filtering.mlir | 137 ----------------
 .../omp-function-filtering-todo.mlir          |  72 +++++++-
 .../mlir/Dialect/OpenMP/Transforms/Passes.td  |   6 +
 .../Dialect/OpenMP/Transforms/CMakeLists.txt  |   1 +
 .../OpenMP/Transforms/FunctionFiltering.cpp   | 109 ++++++++++++
 mlir/test/Dialect/OpenMP/function-filter.mlir | 152 +++++++++++++++++
 24 files changed, 703 insertions(+), 513 deletions(-)
 delete mode 100644 flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
 create mode 100644 flang/lib/Optimizer/OpenMP/UnimplementedDeviceCheck.cpp
 create mode 100644 flang/test/Integration/OpenMP/function-filtering-2.f90
 create mode 100644 flang/test/Integration/OpenMP/function-filtering-3.f90
 rename flang/test/{Lower => Integration}/OpenMP/function-filtering-4.f90 (55%)
 rename flang/test/{Lower => Integration}/OpenMP/function-filtering.f90 (58%)
 delete mode 100644 flang/test/Lower/OpenMP/function-filtering-2.f90
 delete mode 100644 flang/test/Lower/OpenMP/function-filtering-3.f90
 delete mode 100644 flang/test/Transforms/OpenMP/function-filtering.mlir
 create mode 100644 mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp
 create mode 100644 mlir/test/Dialect/OpenMP/function-filter.mlir

diff --git a/flang/include/flang/Optimizer/OpenMP/Passes.td b/flang/include/flang/Optimizer/OpenMP/Passes.td
index 9ec159e1ba1e0..b81ff842948c3 100644
--- a/flang/include/flang/Optimizer/OpenMP/Passes.td
+++ b/flang/include/flang/Optimizer/OpenMP/Passes.td
@@ -47,16 +47,6 @@ def DeleteUnreachableTargetsPass
   let dependentDialects = ["mlir::omp::OpenMPDialect"];
 }
 
-def FunctionFilteringPass : Pass<"omp-function-filtering"> {
-  let summary = "Filters out functions intended for the host when compiling "
-                "for the target device.";
-  let dependentDialects = [
-    "mlir::func::FuncDialect",
-    "fir::FIROpsDialect",
-    "mlir::omp::OpenMPDialect"
-  ];
-}
-
 def DoConcurrentConversionPass : Pass<"omp-do-concurrent-conversion", "mlir::ModuleOp"> {
   let summary = "Map `DO CONCURRENT` loops to OpenMP worksharing loops.";
 
@@ -139,4 +129,20 @@ def AutomapToTargetDataPass
   let dependentDialects = ["mlir::omp::OpenMPDialect"];
 }
 
+def UnimplementedDeviceCheckPass
+    : Pass<"omp-unimplemented-device-check", "::mlir::ModuleOp"> {
+  let summary = "Report not-yet-implemented situations on target device code";
+  let description = [{
+    This pass is for not-yet-implemented cases that neither Flang lowering nor
+    MLIR to LLVM IR translation can properly identify. In particular, it should
+    be used to detect situations that meet all the following:
+      1. They are supported on the host but not on the device. Then, it cannot
+      be checked during Flang lowering because implicit `declare_target`
+      information hasn't been propagated yet.
+      2. They need to use FIR operations or types to be reliably detected.
+      Otherwise, `checkImplementationStatus()` in OpenMP MLIR to LLVM IR
+      translation is the preferred location of the check.
+  }];
+}
+
 #endif //FORTRAN_OPTIMIZER_OPENMP_PASSES
diff --git a/flang/lib/Optimizer/OpenMP/CMakeLists.txt b/flang/lib/Optimizer/OpenMP/CMakeLists.txt
index db29e93b71dad..9c14716f4f06a 100644
--- a/flang/lib/Optimizer/OpenMP/CMakeLists.txt
+++ b/flang/lib/Optimizer/OpenMP/CMakeLists.txt
@@ -3,7 +3,6 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
 add_flang_library(FlangOpenMPTransforms
   AutomapToTargetData.cpp
   DoConcurrentConversion.cpp
-  FunctionFiltering.cpp
   GenericLoopConversion.cpp
   MapsForPrivatizedSymbols.cpp
   MapInfoFinalization.cpp
@@ -12,6 +11,7 @@ add_flang_library(FlangOpenMPTransforms
   LowerWorkshare.cpp
   LowerNontemporal.cpp
   SimdOnly.cpp
+  UnimplementedDeviceCheck.cpp
 
   DEPENDS
   FIRDialect
diff --git a/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp b/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
deleted file mode 100644
index ee61dd8881fd0..0000000000000
--- a/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-//===- FunctionFiltering.cpp -------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements transforms to filter out functions intended for the host
-// when compiling for the device and vice versa.
-//
-//===----------------------------------------------------------------------===//
-
-#include "flang/Optimizer/Builder/Todo.h"
-#include "flang/Optimizer/Dialect/FIRDialect.h"
-#include "flang/Optimizer/Dialect/FIROpsSupport.h"
-#include "flang/Optimizer/OpenMP/Passes.h"
-
-#include "mlir/Dialect/Func/IR/FuncOps.h"
-#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
-#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace flangomp {
-#define GEN_PASS_DEF_FUNCTIONFILTERINGPASS
-#include "flang/Optimizer/OpenMP/Passes.h.inc"
-} // namespace flangomp
-
-using namespace mlir;
-
-/// This function triggers TODO errors and halts compilation if it detects
-/// patterns representing unimplemented features.
-///
-/// It exclusively checks situations that cannot be detected after all of the
-/// MLIR pipeline has ran (i.e. at the MLIR to LLVM IR translation stage, where
-/// the preferred location for these types of checks is), and it only checks for
-/// features that have not been implemented for target offload, but are
-/// supported on host execution.
-static void
-checkDeviceImplementationStatus(omp::OffloadModuleInterface offloadModule) {
-  if (!offloadModule.getIsGPU())
-    return;
-
-  offloadModule->walk<WalkOrder::PreOrder>([&](omp::DeclareReductionOp redOp) {
-    if (redOp.symbolKnownUseEmpty(offloadModule))
-      return WalkResult::advance();
-
-    if (!redOp.getByrefElementType())
-      return WalkResult::advance();
-
-    auto seqTy = dyn_cast<fir::SequenceType>(*redOp.getByrefElementType());
-
-    bool isByRefReductionSupported =
-        !seqTy || !fir::sequenceWithNonConstantShape(seqTy);
-
-    if (!isByRefReductionSupported) {
-      TODO(redOp.getLoc(),
-           "Reduction of dynamically-shaped arrays are not supported yet "
-           "on the GPU.");
-    }
-
-    return WalkResult::advance();
-  });
-}
-
-namespace {
-class FunctionFilteringPass
-    : public flangomp::impl::FunctionFilteringPassBase<FunctionFilteringPass> {
-public:
-  FunctionFilteringPass() = default;
-
-  void runOnOperation() override {
-    MLIRContext *context = &getContext();
-    OpBuilder opBuilder(context);
-    auto op = dyn_cast<omp::OffloadModuleInterface>(getOperation());
-    if (!op || !op.getIsTargetDevice())
-      return;
-
-    op->walk<WalkOrder::PreOrder>([&](func::FuncOp funcOp) {
-      // Do not filter functions with target regions inside, because they have
-      // to be available for both host and device so that regular and reverse
-      // offloading can be supported.
-      bool hasTargetRegion =
-          funcOp
-              ->walk<WalkOrder::PreOrder>([&](omp::TargetOp targetOp) {
-                return WalkResult::interrupt();
-              })
-              .wasInterrupted();
-
-      omp::DeclareTargetDeviceType declareType =
-          omp::DeclareTargetDeviceType::host;
-      auto declareTargetOp =
-          dyn_cast<omp::DeclareTargetInterface>(funcOp.getOperation());
-      if (declareTargetOp && declareTargetOp.isDeclareTarget())
-        declareType = declareTargetOp.getDeclareTargetDeviceType();
-
-      // Filtering a function here means deleting it if it doesn't contain a
-      // target region. Else we explicitly set the omp.declare_target
-      // attribute. The second stage of function filtering at the MLIR to LLVM
-      // IR translation level will remove functions that contain the target
-      // region from the generated llvm IR.
-      if (declareType == omp::DeclareTargetDeviceType::host) {
-        SymbolTable::UseRange funcUses = *funcOp.getSymbolUses(op);
-        for (SymbolTable::SymbolUse use : funcUses) {
-          Operation *callOp = use.getUser();
-          if (auto internalFunc = dyn_cast<func::FuncOp>(callOp)) {
-            // Do not delete internal procedures holding the symbol of their
-            // Fortran host procedure as attribute.
-            internalFunc->removeAttr(fir::getHostSymbolAttrName());
-            // Set public visibility so that the function is not deleted by MLIR
-            // because unused. Changing it is OK here because the function will
-            // be deleted anyway in the second filtering phase.
-            internalFunc.setVisibility(mlir::SymbolTable::Visibility::Public);
-            continue;
-          }
-          // Prevent dispatch table entries pointing to deleted functions
-          // from being removed. This prevents the lowering of any
-          // corresponding fir.dispatch ops from triggering errors. These
-          // fir.dt_entry ops will point to an undefined symbol as a result,
-          // which currently doesn't cause an issue, as fir.dispatch-related ops
-          // are later removed by the host op filtering pass.
-          if (isa<fir::DTEntryOp>(callOp))
-            continue;
-
-          // If the callOp has users then replace them with Undef values.
-          if (!callOp->use_empty()) {
-            SmallVector<Value> undefResults;
-            for (Value res : callOp->getResults()) {
-              opBuilder.setInsertionPoint(callOp);
-              undefResults.emplace_back(
-                  fir::UndefOp::create(opBuilder, res.getLoc(), res.getType()));
-            }
-            callOp->replaceAllUsesWith(undefResults);
-          }
-          // Remove the callOp
-          callOp->erase();
-        }
-
-        if (!hasTargetRegion) {
-          funcOp.erase();
-          return WalkResult::skip();
-        }
-
-        if (declareTargetOp)
-          declareTargetOp.setDeclareTarget(
-              declareType, omp::DeclareTargetCaptureClause::to,
-              declareTargetOp.getDeclareTargetAutomap());
-      }
-      return WalkResult::advance();
-    });
-
-    checkDeviceImplementationStatus(op);
-  }
-};
-} // namespace
diff --git a/flang/lib/Optimizer/OpenMP/UnimplementedDeviceCheck.cpp b/flang/lib/Optimizer/OpenMP/UnimplementedDeviceCheck.cpp
new file mode 100644
index 0000000000000..c393e2f90f88f
--- /dev/null
+++ b/flang/lib/Optimizer/OpenMP/UnimplementedDeviceCheck.cpp
@@ -0,0 +1,98 @@
+//===- UnimplementedDeviceCheck.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Diagnose not-yet-implemented target device cases.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/OpenMP/Passes.h"
+
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
+#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
+#include "mlir/IR/BuiltinOps.h"
+
+using namespace mlir;
+
+namespace flangomp {
+#define GEN_PASS_DEF_UNIMPLEMENTEDDEVICECHECKPASS
+#include "flang/Optimizer/OpenMP/Passes.h.inc"
+} // namespace flangomp
+
+/// Check whether all uses of the given symbol inside of the module are only
+/// intended for the host.
+static bool allUsesInHostCode(Operation *moduleOp, SymbolOpInterface symOp) {
+  if (auto symUses = symOp.getSymbolUses(moduleOp)) {
+    for (auto symUse : symUses.value()) {
+      Operation *symUser = symUse.getUser();
+      if (!symUser)
+        continue;
+
+      if (symUser->getParentOfType<omp::TargetOp>())
+        return false;
+
+      if (auto declareTargetOp =
+              symUser->getParentOfType<omp::DeclareTargetInterface>()) {
+        if (declareTargetOp.isDeclareTarget() &&
+            declareTargetOp.getDeclareTargetDeviceType() !=
+                omp::DeclareTargetDeviceType::host)
+          return false;
+      }
+    }
+  }
+  return true;
+}
+
+/// Emit not-yet-implemented errors for reductions over dynamically-shaped
+/// arrays.
+static LogicalResult checkReduction(omp::DeclareReductionOp reductionOp) {
+  if (!reductionOp.getByrefElementType())
+    return success();
+
+  auto seqTy = dyn_cast<fir::SequenceType>(*reductionOp.getByrefElementType());
+
+  bool isByRefReductionSupported =
+      !seqTy || !fir::sequenceWithNonConstantShape(seqTy);
+
+  if (!isByRefReductionSupported)
+    return reductionOp.emitError()
+           << "not yet implemented: Reduction of dynamically-shaped arrays on "
+              "the GPU.";
+
+  return success();
+}
+
+namespace {
+class UnimplementedDeviceCheckPass
+    : public flangomp::impl::UnimplementedDeviceCheckPassBase<
+          UnimplementedDeviceCheckPass> {
+
+  void runOnOperation() override {
+    // Only run checks when compiling for a target device.
+    auto op = dyn_cast<omp::OffloadModuleInterface>(*getOperation());
+    if (!op || !op.getIsTargetDevice())
+      return;
+
+    bool errorsEmitted = false;
+
+    if (op.getIsGPU()) {
+      op->walk([&](omp::DeclareReductionOp reductionOp) {
+        // Only check reductions that aren't exclusively used on the host.
+        if (op.getIsGPU() && !allUsesInHostCode(op, reductionOp)) {
+          if (failed(checkReduction(reductionOp)))
+            errorsEmitted = true;
+        }
+      });
+    }
+
+    if (errorsEmitted)
+      signalPassFailure();
+
+    markAllAnalysesPreserved();
+  }
+};
+} // namespace
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 15a342e10fc7f..2812d23bbec73 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -371,14 +371,11 @@ void createOpenMPFIRPassPipeline(mlir::PassManager &pm,
   pm.addPass(flangomp::createMapsForPrivatizedSymbolsPass());
   pm.addPass(flangomp::createAutomapToTargetDataPass());
   pm.addPass(flangomp::createMapInfoFinalizationPass());
-  pm.addPass(mlir::omp::createMarkDeclareTargetPass());
 
   // Delete unreachable target operations before FunctionFilteringPass
   // extracts them.
   pm.addPass(flangomp::createDeleteUnreachableTargetsPass());
   pm.addPass(flangomp::createGenericLoopConversionPass());
-  if (opts.isTargetDevice)
-    pm.addPass(flangomp::createFunctionFilteringPass());
 }
 
 void createDebugPasses(mlir::PassManager &pm,
@@ -448,18 +445,30 @@ void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm,
         flangomp::createLowerNontemporalPass());
   }
 
+  bool runOMPNonSimdPasses = config.EnableOpenMP && !config.EnableOpenMPSimd;
+  if (runOMPNonSimdPasses) {
+    // Propagate implicit declare target information early in order to diagnose
+    // target device not-yet-implemented cases based on FIR.
+    pm.addPass(mlir::omp::createMarkDeclareTargetPass());
+    pm.addPass(flangomp::createUnimplementedDeviceCheckPass());
+  }
+
   fir::addFIRToLLVMPass(pm, config);
   pm.addPass(fir::createEmitMIFGlobalCtors());
 
-  if (config.EnableOpenMP && !config.EnableOpenMPSimd) {
+  if (runOMPNonSimdPasses) {
     // Since some math operations may be converted to function calls by the
-    // ConvertMathToFuncs pass, we need to mark them with the omp.declare_target
-    // attribute if they're called from a target region.
+    // ConvertMathToFuncs pass, we need to run the implicit declare_target
+    // propagation and dependent passes late in the pipeline.
     pm.addPass(mlir::omp::createMarkDeclareTargetPass());
 
-    // Remove all non target-related operations from host functions still
-    // remaining at this point, if compiling for an OpenMP target device. This
-    // is required before translating 'omp' dialect operations to LLVM IR.
+    // First remove host-only functions from target device modules, and then
+    // clean up any remaining host functions holding target regions to only
+    // contain the bare minimum host operations needed for target device
+    // compilation. These passes must always run back to back to ensure no
+    // temporary poison values, introduced by the first pass, cause other passes
+    // to encounter UB before the second pass removes them.
+    pm.addPass(mlir::omp::createFunctionFilteringPass());
     pm.addPass(mlir::omp::createHostOpFilteringPass());
 
     // Convert applicable OpenMP stack allocations to shared memory allocations
diff --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir
index 4a483851e7ba6..536963920bdb7 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -183,11 +183,14 @@ func.func @_QQmain() {
 // PASSES-NEXT: 'func.func' Pipeline
 // PASSES-NEXT:  FunctionAttr
 // PASSES-NEXT:  LowerNontemporalPass
+// PASSES-NEXT: MarkDeclareTargetPass
+// PASSES-NEXT: UnimplementedDeviceCheckPass
 // PASSES-NEXT: FIRToLLVMLowering
 // PASSES-NEXT: ReconcileUnrealizedCasts
 // PASSES-NEXT: EmitMIFGlobalCtors
 // PASSES-NEXT: MarkDeclareTargetPass
-// PASSES-NEXT:  HostOpFilteringPass
+// PASSES-NEXT: FunctionFilteringPass
+// PASSES-NEXT: HostOpFilteringPass
 // PASSES-NEXT: 'llvm.func' Pipeline
 // PASSES-NEXT:  StackToSharedPass
 // PASSES-NEXT: PrepareForOMPOffloadPrivatizationPass
diff --git a/flang/test/Integration/OpenMP/function-filtering-2.f90 b/flang/test/Integration/OpenMP/function-filtering-2.f90
new file mode 100644
index 0000000000000..eda3015811e73
--- /dev/null
+++ b/flang/test/Integration/OpenMP/function-filtering-2.f90
@@ -0,0 +1,61 @@
+!===----------------------------------------------------------------------===!
+! 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 -fopenmp -fopenmp-version=52 -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-HOST %s
+! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-DEVICE %s %}
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
+! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
+
+! MLIR-ALL: llvm.func @{{.*}}implicit_invocation() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
+! MLIR-ALL: llvm.return
+! LLVM-ALL: define {{.*}} @{{.*}}implicit_invocation{{.*}}(
+subroutine implicit_invocation()
+end subroutine implicit_invocation
+
+! MLIR-ALL: llvm.func @{{.*}}declaretarget() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
+! MLIR-ALL: llvm.return
+! LLVM-ALL: define {{.*}} @{{.*}}declaretarget{{.*}}(
+subroutine declaretarget()
+!$omp declare target to(declaretarget) device_type(nohost)
+    call implicit_invocation()
+end subroutine declaretarget
+
+! MLIR-ALL: llvm.func @{{.*}}declaretarget_enter() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
+! MLIR-ALL: llvm.return
+! LLVM-ALL: define {{.*}} @{{.*}}declaretarget_enter{{.*}}(
+subroutine declaretarget_enter()
+!$omp declare target enter(declaretarget_enter) device_type(nohost)
+    call implicit_invocation()
+end subroutine declaretarget_enter
+
+! MLIR-ALL: llvm.func @{{.*}}no_declaretarget() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
+! MLIR-ALL: llvm.return
+! LLVM-ALL: define {{.*}} @{{.*}}no_declaretarget{{.*}}(
+subroutine no_declaretarget()
+end subroutine no_declaretarget
+
+! MLIR-ALL: llvm.func @{{.+}}main(
+! MLIR-ALL: omp.target
+! MLIR-ALL: llvm.return
+
+! MLIR-HOST: llvm.func @main(
+! MLIR-HOST: llvm.return
+! MLIR-DEVICE-NOT: llvm.func @main(
+
+! LLVM-HOST: define {{.*}} @{{.*}}main{{.*}}(
+! LLVM-HOST: {{.*}} @{{.*}}__omp_offloading{{.*}}main_{{.*}}(
+! LLVM-DEVICE-NOT: {{.*}} @{{.*}}main{{.*}}(
+! LLVM-DEVICE: define {{.*}} @{{.*}}__omp_offloading{{.*}}main_{{.*}}(
+program main
+!$omp target
+    call declaretarget()
+    call no_declaretarget()
+!$omp end target
+end program main
diff --git a/flang/test/Integration/OpenMP/function-filtering-3.f90 b/flang/test/Integration/OpenMP/function-filtering-3.f90
new file mode 100644
index 0000000000000..dcc35b1181461
--- /dev/null
+++ b/flang/test/Integration/OpenMP/function-filtering-3.f90
@@ -0,0 +1,51 @@
+!===----------------------------------------------------------------------===!
+! 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 -fopenmp -fopenmp-version=52 -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-HOST %s
+! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-DEVICE %s %}
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
+! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
+
+! Check that the correct LLVM IR functions are kept for the host and device
+! after running the whole set of translation and transformation passes from
+! Fortran.
+
+! MLIR-HOST: llvm.func @{{.*}}host_parent_procedure(
+! MLIR-HOST: llvm.return
+! MLIR-DEVICE-NOT: llvm.func {{.*}}host_parent_procedure(
+
+! LLVM-HOST: define {{.*}} @host_parent_procedure{{.*}}(
+! LLVM-DEVICE-NOT: {{.*}} @{{.*}}_host_parent_procedure{{.*}}(
+subroutine host_parent_procedure(x)
+  integer, intent(out) :: x
+  call target_internal_proc(x)
+contains
+
+! MLIR-ALL: llvm.func {{.*}}@_QFhost_parent_procedurePtarget_internal_proc(
+! MLIR-ALL: llvm.func {{.*}}@_QFhost_parent_procedurePdeclare_target_internal_proc
+
+! LLVM-HOST: define {{.*}} @_QFhost_parent_procedurePtarget_internal_proc(
+! LLVM-DEVICE-NOT: define {{.*}} @_QFhost_parent_procedurePtarget_internal_proc(
+! LLVM-ALL: define {{.*}} void @_QFhost_parent_procedurePdeclare_target_internal_proc
+
+! LLVM-ALL: define {{.*}} @__omp_offloading_{{.*}}QFhost_parent_procedurePtarget_internal_proc{{.*}}(
+
+subroutine target_internal_proc(x)
+  integer, intent(out) :: x
+  !$omp target map(from:x)
+    x = 10
+    call declare_target_internal_proc()
+  !$omp end target
+end subroutine
+
+subroutine declare_target_internal_proc()
+  !$omp declare target enter(declare_target_internal_proc) device_type(nohost)
+end subroutine
+end subroutine
diff --git a/flang/test/Lower/OpenMP/function-filtering-4.f90 b/flang/test/Integration/OpenMP/function-filtering-4.f90
similarity index 55%
rename from flang/test/Lower/OpenMP/function-filtering-4.f90
rename to flang/test/Integration/OpenMP/function-filtering-4.f90
index de8a26c789dc5..18a762b9acf3d 100644
--- a/flang/test/Lower/OpenMP/function-filtering-4.f90
+++ b/flang/test/Integration/OpenMP/function-filtering-4.f90
@@ -1,9 +1,17 @@
-! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-HOST,LLVM-ALL %s
-! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-HOST,MLIR-ALL %s
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-DEVICE,LLVM-ALL %s %}
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-DEVICE,MLIR-ALL %s %}
-! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-HOST,MLIR-ALL %s
-! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-DEVICE,MLIR-ALL %s %}
+!===----------------------------------------------------------------------===!
+! 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 -fopenmp -fopenmp-version=52 -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-HOST %s
+! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-DEVICE %s %}
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
+! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
 
 ! Check that the correct LLVM IR operations are kept for the host and device
 ! after running the whole set of translation and transformation passes from
@@ -18,8 +26,8 @@ module mymodule
   end type myclass
 
 contains
-  ! MLIR-HOST: func.func @_QMmymodulePmyfunc
-  ! MLIR-DEVICE-NOT: func.func @_QMmymodulePmyfunc
+  ! MLIR-HOST: llvm.func @_QMmymodulePmyfunc
+  ! MLIR-DEVICE-NOT: llvm.func @_QMmymodulePmyfunc
 
   ! LLVM-HOST: define void @_QMmymodulePmyfunc
   ! LLVM-DEVICE-NOT: define void @_QMmymodulePmyfunc
@@ -29,7 +37,7 @@ subroutine myfunc(self)
   end subroutine
 end module
 
-! MLIR-ALL: func.func @_QPmain
+! MLIR-ALL: llvm.func @_QPmain
 
 ! LLVM-HOST: define void @main_
 ! LLVM-DEVICE-NOT: define void @main_
@@ -41,7 +49,9 @@ subroutine main(x)
   class(myclass), allocatable :: myobj
   allocate(myobj)
 
-  ! MLIR-ALL: fir.dispatch "myfunc"
+  ! Indirect function call only present on the host.
+  ! MLIR-HOST: llvm.call %{{.*}}(%{{.*}})
+  ! MLIR-DEVICE-NOT: llvm.call %{{.*}}(%{{.*}})
 
   ! LLVM-HOST: %[[MYFUNC_PTR:.*]] = inttoptr i64 %{{.*}} to ptr
   ! LLVM-HOST: call void %[[MYFUNC_PTR]](ptr %{{.*}})
@@ -58,6 +68,3 @@ subroutine main(x)
 end subroutine main
 
 ! LLVM-ALL: define {{.*}}void @__omp_offloading{{.*}}main_{{.*}}
-
-! MLIR-ALL: fir.type_info @_QMmymoduleTmyclass {{.*}} dispatch_table
-! MLIR-ALL-NEXT: fir.dt_entry "myfunc", @_QMmymodulePmyfunc
diff --git a/flang/test/Lower/OpenMP/function-filtering.f90 b/flang/test/Integration/OpenMP/function-filtering.f90
similarity index 58%
rename from flang/test/Lower/OpenMP/function-filtering.f90
rename to flang/test/Integration/OpenMP/function-filtering.f90
index 65dee4eb62332..4c4ca3792503a 100644
--- a/flang/test/Lower/OpenMP/function-filtering.f90
+++ b/flang/test/Integration/OpenMP/function-filtering.f90
@@ -1,16 +1,24 @@
-! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-HOST,LLVM-ALL %s
-! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-HOST,MLIR-ALL %s
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-DEVICE,LLVM-ALL %s %}
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-DEVICE,MLIR-ALL %s %}
-! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-HOST,MLIR-ALL %s
-! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-DEVICE,MLIR-ALL %s %}
+!===----------------------------------------------------------------------===!
+! 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 -fopenmp -fopenmp-version=52 -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-HOST %s
+! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-ALL,LLVM-DEVICE %s %}
+! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
+! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-HOST %s
+! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | tco -test-gen | FileCheck --check-prefixes=MLIR-ALL,MLIR-DEVICE %s %}
 
 ! Check that the correct LLVM IR functions are kept for the host and device
 ! after running the whole set of translation and transformation passes from
 ! Fortran.
 
-! MLIR-ALL: func.func @{{.*}}device_fn(
-! MLIR-ALL: return
+! MLIR-ALL: llvm.func @{{.*}}device_fn(
+! MLIR-ALL: llvm.return
 
 ! LLVM-ALL: define {{.*}} @{{.*}}device_fn{{.*}}(
 function device_fn() result(x)
@@ -19,8 +27,8 @@ function device_fn() result(x)
   x = 10
 end function device_fn
 
-! MLIR-ALL: func.func @{{.*}}device_fn_enter(
-! MLIR-ALL: return
+! MLIR-ALL: llvm.func @{{.*}}device_fn_enter(
+! MLIR-ALL: llvm.return
 
 ! LLVM-ALL: define {{.*}} @{{.*}}device_fn_enter{{.*}}(
 function device_fn_enter() result(x)
@@ -29,9 +37,9 @@ function device_fn_enter() result(x)
   x = 10
 end function device_fn_enter
 
-! MLIR-HOST: func.func @{{.*}}host_fn(
-! MLIR-HOST: return
-! MLIR-DEVICE-NOT: func.func {{.*}}host_fn(
+! MLIR-HOST: llvm.func @{{.*}}host_fn(
+! MLIR-HOST: llvm.return
+! MLIR-DEVICE-NOT: llvm.func {{.*}}host_fn(
 
 ! LLVM-HOST: define {{.*}} @{{.*}}host_fn{{.*}}(
 ! LLVM-DEVICE-NOT: {{.*}} @{{.*}}host_fn{{.*}}(
@@ -49,8 +57,8 @@ function host_fn_enter() result(x)
   x = 10
 end function host_fn_enter
 
-! MLIR-ALL: func.func @{{.*}}target_subr(
-! MLIR-ALL: return
+! MLIR-ALL: llvm.func @{{.*}}target_subr(
+! MLIR-ALL: llvm.return
 
 ! LLVM-HOST: define {{.*}} @{{.*}}target_subr{{.*}}(
 ! LLVM-ALL: define {{.*}} @__omp_offloading_{{.*}}_{{.*}}_target_subr__{{.*}}(
diff --git a/flang/test/Lower/OpenMP/declare-target-deferred-marking-reductions.f90 b/flang/test/Lower/OpenMP/declare-target-deferred-marking-reductions.f90
index 66697ef6bbe70..b245f5fdc560b 100644
--- a/flang/test/Lower/OpenMP/declare-target-deferred-marking-reductions.f90
+++ b/flang/test/Lower/OpenMP/declare-target-deferred-marking-reductions.f90
@@ -1,5 +1,5 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-device %s -o - | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | tco -test-gen | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-device %s -o - | tco -test-gen | FileCheck %s
 
 program main
     use, intrinsic ::  iso_c_binding
@@ -30,8 +30,7 @@ end function mycombine
     !$omp end target
  end program main
 
-!CHECK: func.func {{.*}} @myinit(!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: llvm.func @myinit(!llvm.ptr, !llvm.ptr)
 !CHECK-SAME: {{.*}}, omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}
-!CHECK-LABEL: func.func {{.*}} @mycombine(!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: llvm.func @mycombine(!llvm.ptr, !llvm.ptr)
 !CHECK-SAME: {{.*}}, omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}
-
diff --git a/flang/test/Lower/OpenMP/declare-target-deferred-marking.f90 b/flang/test/Lower/OpenMP/declare-target-deferred-marking.f90
index 528563aba6962..ee2a6c70c0057 100644
--- a/flang/test/Lower/OpenMP/declare-target-deferred-marking.f90
+++ b/flang/test/Lower/OpenMP/declare-target-deferred-marking.f90
@@ -1,5 +1,5 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s --check-prefixes ALL,HOST
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-device %s -o - | FileCheck %s --check-prefixes ALL
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | tco -test-gen | FileCheck %s --check-prefixes ALL,HOST
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-device %s -o - | tco -test-gen | FileCheck %s --check-prefixes ALL
 
 program main
     use, intrinsic ::  iso_c_binding
@@ -50,11 +50,11 @@ end subroutine unused_unemitted_interface
 !$omp end target
  end program main
 
-!HOST-LABEL: func.func {{.*}} @host_interface()
+!HOST-LABEL: llvm.func @host_interface()
 !HOST-SAME: {{.*}}, omp.declare_target = #omp.declaretarget<device_type = (host), capture_clause = (enter), automap = false>{{.*}}
-!ALL-LABEL: func.func {{.*}} @called_from_target_interface(!fir.ref<i64>, !fir.ref<i64>)
+!ALL-LABEL: llvm.func @called_from_target_interface(!llvm.ptr, !llvm.ptr)
 !ALL-SAME: {{.*}}, omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}
-!ALL-LABEL: func.func {{.*}} @any_interface()
+!ALL-LABEL: llvm.func @any_interface()
 !ALL-SAME: {{.*}}, omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}
-!ALL-LABEL: func.func {{.*}} @device_interface()
+!ALL-LABEL: llvm.func @device_interface()
 !ALL-SAME: {{.*}}, omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}
diff --git a/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap-enter.f90 b/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap-enter.f90
index 2caa6ba0b5b1e..3e20e7a8b0170 100644
--- a/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap-enter.f90
+++ b/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap-enter.f90
@@ -1,9 +1,9 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | FileCheck %s  --check-prefix=DEVICE
-!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
-!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | FileCheck %s --check-prefix=DEVICE
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | tco -test-gen | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s  --check-prefix=DEVICE
+!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | tco -test-gen | FileCheck %s
+!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s --check-prefix=DEVICE
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_twice() result(k)
    integer :: i
@@ -11,7 +11,7 @@ function implicitly_captured_twice() result(k)
    k = i
 end function implicitly_captured_twice
 
-! CHECK-LABEL: func.func @_QPtarget_function_twice_host
+! CHECK-LABEL: llvm.func @_QPtarget_function_twice_host
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (host), capture_clause = (enter), automap = false>{{.*}}}
 function target_function_twice_host() result(i)
 !$omp declare target enter(target_function_twice_host) device_type(host)
@@ -19,7 +19,7 @@ function target_function_twice_host() result(i)
    i = implicitly_captured_twice()
 end function target_function_twice_host
 
-! DEVICE-LABEL: func.func @_QPtarget_function_twice_device
+! DEVICE-LABEL: llvm.func @_QPtarget_function_twice_device
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 function target_function_twice_device() result(i)
 !$omp declare target enter(target_function_twice_device) device_type(nohost)
@@ -29,7 +29,7 @@ end function target_function_twice_device
 
 !! -----
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_nest
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_nest
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_nest() result(k)
    integer :: i
@@ -37,13 +37,13 @@ function implicitly_captured_nest() result(k)
    k = i
 end function implicitly_captured_nest
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_one
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_one
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter){{.*}}}
 function implicitly_captured_one() result(k)
    k = implicitly_captured_nest()
 end function implicitly_captured_one
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_two
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_two
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_two() result(k)
    integer :: i
@@ -51,7 +51,7 @@ function implicitly_captured_two() result(k)
    k = i
 end function implicitly_captured_two
 
-! DEVICE-LABEL: func.func @_QPtarget_function_test
+! DEVICE-LABEL: llvm.func @_QPtarget_function_test
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 function target_function_test() result(j)
 !$omp declare target enter(target_function_test) device_type(nohost)
@@ -62,7 +62,7 @@ end function target_function_test
 
 !! -----
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_nest_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_nest_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_nest_twice() result(k)
    integer :: i
@@ -70,13 +70,13 @@ function implicitly_captured_nest_twice() result(k)
    k = i
 end function implicitly_captured_nest_twice
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_one_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_one_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_one_twice() result(k)
    k = implicitly_captured_nest_twice()
 end function implicitly_captured_one_twice
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_two_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_two_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_two_twice() result(k)
    integer :: i
@@ -84,7 +84,7 @@ function implicitly_captured_two_twice() result(k)
    k = i
 end function implicitly_captured_two_twice
 
-! DEVICE-LABEL: func.func @_QPtarget_function_test_device
+! DEVICE-LABEL: llvm.func @_QPtarget_function_test_device
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 function target_function_test_device() result(j)
    !$omp declare target enter(target_function_test_device) device_type(nohost)
@@ -93,7 +93,7 @@ function target_function_test_device() result(j)
    j = implicitly_captured_two_twice() + i
 end function target_function_test_device
 
-! CHECK-LABEL: func.func @_QPtarget_function_test_host
+! CHECK-LABEL: llvm.func @_QPtarget_function_test_host
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (host), capture_clause = (enter), automap = false>{{.*}}}
 function target_function_test_host() result(j)
    !$omp declare target enter(target_function_test_host) device_type(host)
@@ -104,7 +104,7 @@ end function target_function_test_host
 
 !! -----
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_with_dev_type_recursive
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_with_dev_type_recursive
 ! DEVICE-SAME: {{.*}}attributes {{.*}}omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}}
 recursive function implicitly_captured_with_dev_type_recursive(increment) result(k)
 !$omp declare target enter(implicitly_captured_with_dev_type_recursive) device_type(host)
@@ -116,7 +116,7 @@ recursive function implicitly_captured_with_dev_type_recursive(increment) result
    end if
 end function implicitly_captured_with_dev_type_recursive
 
-! DEVICE-LABEL: func.func @_QPtarget_function_with_dev_type_recurse
+! DEVICE-LABEL: llvm.func @_QPtarget_function_with_dev_type_recurse
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 function target_function_with_dev_type_recurse() result(i)
 !$omp declare target enter(target_function_with_dev_type_recurse) device_type(nohost)
@@ -128,28 +128,28 @@ end function target_function_with_dev_type_recurse
 
 module test_module
 contains
-! CHECK-LABEL: func.func @_QMtest_modulePimplicitly_captured_nest_twice
+! CHECK-LABEL: llvm.func @_QMtest_modulePimplicitly_captured_nest_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}}
    function implicitly_captured_nest_twice() result(i)
       integer :: i
       i = 10
    end function implicitly_captured_nest_twice
 
-! CHECK-LABEL: func.func @_QMtest_modulePimplicitly_captured_one_twice
+! CHECK-LABEL: llvm.func @_QMtest_modulePimplicitly_captured_one_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>{{.*}}}
    function implicitly_captured_one_twice() result(k)
       !$omp declare target enter(implicitly_captured_one_twice) device_type(host)
       k = implicitly_captured_nest_twice()
    end function implicitly_captured_one_twice
 
-! DEVICE-LABEL: func.func @_QMtest_modulePimplicitly_captured_two_twice
+! DEVICE-LABEL: llvm.func @_QMtest_modulePimplicitly_captured_two_twice
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
    function implicitly_captured_two_twice() result(y)
       integer :: y
       y = 5
    end function implicitly_captured_two_twice
 
-! DEVICE-LABEL: func.func @_QMtest_modulePtarget_function_test_device
+! DEVICE-LABEL: llvm.func @_QMtest_modulePtarget_function_test_device
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
    function target_function_test_device() result(j)
       !$omp declare target enter(target_function_test_device) device_type(nohost)
@@ -173,7 +173,7 @@ recursive subroutine implicitly_captured_recursive(increment)
    end interface
 end program
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_recursive
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_recursive
 ! DEVICE-SAME: {{.*}}attributes {{.*}}omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 recursive subroutine implicitly_captured_recursive(increment)
    integer :: increment
@@ -184,7 +184,7 @@ recursive subroutine implicitly_captured_recursive(increment)
    end if
 end subroutine
 
-! DEVICE-LABEL: func.func @_QPcaller_recursive
+! DEVICE-LABEL: llvm.func @_QPcaller_recursive
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>{{.*}}}
 subroutine caller_recursive
 !$omp declare target enter(caller_recursive) device_type(nohost)
diff --git a/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap.f90 b/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap.f90
index 32a5058029174..01599a045dc6b 100644
--- a/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap.f90
+++ b/flang/test/Lower/OpenMP/declare-target-implicit-func-and-subr-cap.f90
@@ -1,9 +1,9 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %s -o - | FileCheck %s
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -fopenmp-is-target-device %s -o - | FileCheck %s  --check-prefix=DEVICE
-!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 %s -o - | FileCheck %s
-!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 -fopenmp-is-target-device %s -o - | FileCheck %s --check-prefix=DEVICE
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %s -o - | tco -test-gen | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s  --check-prefix=DEVICE
+!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 %s -o - | tco -test-gen | FileCheck %s
+!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s --check-prefix=DEVICE
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured(toggle) result(k)
    integer :: i, j, k
@@ -18,7 +18,7 @@ function implicitly_captured(toggle) result(k)
 end function implicitly_captured
 
 
-! CHECK-LABEL: func.func @_QPtarget_function
+! CHECK-LABEL: llvm.func @_QPtarget_function
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function target_function(toggle) result(i)
 !$omp declare target
@@ -29,7 +29,7 @@ end function target_function
 
 !! -----
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_twice() result(k)
    integer :: i
@@ -37,7 +37,7 @@ function implicitly_captured_twice() result(k)
    k = i
 end function implicitly_captured_twice
 
-! CHECK-LABEL: func.func @_QPtarget_function_twice_host
+! CHECK-LABEL: llvm.func @_QPtarget_function_twice_host
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (host), capture_clause = (to), automap = false>{{.*}}}
 function target_function_twice_host() result(i)
 !$omp declare target to(target_function_twice_host) device_type(host)
@@ -45,7 +45,7 @@ function target_function_twice_host() result(i)
    i = implicitly_captured_twice()
 end function target_function_twice_host
 
-! DEVICE-LABEL: func.func @_QPtarget_function_twice_device
+! DEVICE-LABEL: llvm.func @_QPtarget_function_twice_device
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function target_function_twice_device() result(i)
 !$omp declare target to(target_function_twice_device) device_type(nohost)
@@ -55,7 +55,7 @@ end function target_function_twice_device
 
 !! -----
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_nest
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_nest
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_nest() result(k)
    integer :: i
@@ -63,13 +63,13 @@ function implicitly_captured_nest() result(k)
    k = i
 end function implicitly_captured_nest
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_one
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_one
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to){{.*}}}
 function implicitly_captured_one() result(k)
    k = implicitly_captured_nest()
 end function implicitly_captured_one
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_two
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_two
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_two() result(k)
    integer :: i
@@ -77,7 +77,7 @@ function implicitly_captured_two() result(k)
    k = i
 end function implicitly_captured_two
 
-! DEVICE-LABEL: func.func @_QPtarget_function_test
+! DEVICE-LABEL: llvm.func @_QPtarget_function_test
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function target_function_test() result(j)
 !$omp declare target to(target_function_test) device_type(nohost)
@@ -88,7 +88,7 @@ end function target_function_test
 
 !! -----
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_nest_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_nest_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_nest_twice() result(k)
    integer :: i
@@ -96,13 +96,13 @@ function implicitly_captured_nest_twice() result(k)
    k = i
 end function implicitly_captured_nest_twice
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_one_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_one_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_one_twice() result(k)
    k = implicitly_captured_nest_twice()
 end function implicitly_captured_one_twice
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_two_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_two_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_two_twice() result(k)
    integer :: i
@@ -110,7 +110,7 @@ function implicitly_captured_two_twice() result(k)
    k = i
 end function implicitly_captured_two_twice
 
-! DEVICE-LABEL: func.func @_QPtarget_function_test_device
+! DEVICE-LABEL: llvm.func @_QPtarget_function_test_device
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function target_function_test_device() result(j)
    !$omp declare target to(target_function_test_device) device_type(nohost)
@@ -119,7 +119,7 @@ function target_function_test_device() result(j)
    j = implicitly_captured_two_twice() + i
 end function target_function_test_device
 
-! CHECK-LABEL: func.func @_QPtarget_function_test_host
+! CHECK-LABEL: llvm.func @_QPtarget_function_test_host
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (host), capture_clause = (to), automap = false>{{.*}}}
 function target_function_test_host() result(j)
    !$omp declare target to(target_function_test_host) device_type(host)
@@ -130,7 +130,7 @@ end function target_function_test_host
 
 !! -----
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_with_dev_type_recursive
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_with_dev_type_recursive
 ! DEVICE-SAME: {{.*}}attributes {{.*}}omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 recursive function implicitly_captured_with_dev_type_recursive(increment) result(k)
 !$omp declare target to(implicitly_captured_with_dev_type_recursive) device_type(host)
@@ -142,7 +142,7 @@ recursive function implicitly_captured_with_dev_type_recursive(increment) result
    end if
 end function implicitly_captured_with_dev_type_recursive
 
-! DEVICE-LABEL: func.func @_QPtarget_function_with_dev_type_recurse
+! DEVICE-LABEL: llvm.func @_QPtarget_function_with_dev_type_recurse
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function target_function_with_dev_type_recurse() result(i)
 !$omp declare target to(target_function_with_dev_type_recurse) device_type(nohost)
@@ -154,28 +154,28 @@ end function target_function_with_dev_type_recurse
 
 module test_module
 contains
-! CHECK-LABEL: func.func @_QMtest_modulePimplicitly_captured_nest_twice
+! CHECK-LABEL: llvm.func @_QMtest_modulePimplicitly_captured_nest_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
    function implicitly_captured_nest_twice() result(i)
       integer :: i
       i = 10
    end function implicitly_captured_nest_twice
 
-! CHECK-LABEL: func.func @_QMtest_modulePimplicitly_captured_one_twice
+! CHECK-LABEL: llvm.func @_QMtest_modulePimplicitly_captured_one_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
    function implicitly_captured_one_twice() result(k)
       !$omp declare target to(implicitly_captured_one_twice) device_type(host)
       k = implicitly_captured_nest_twice()
    end function implicitly_captured_one_twice
 
-! DEVICE-LABEL: func.func @_QMtest_modulePimplicitly_captured_two_twice
+! DEVICE-LABEL: llvm.func @_QMtest_modulePimplicitly_captured_two_twice
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
    function implicitly_captured_two_twice() result(y)
       integer :: y
       y = 5
    end function implicitly_captured_two_twice
 
-! DEVICE-LABEL: func.func @_QMtest_modulePtarget_function_test_device
+! DEVICE-LABEL: llvm.func @_QMtest_modulePtarget_function_test_device
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
    function target_function_test_device() result(j)
       !$omp declare target to(target_function_test_device) device_type(nohost)
@@ -199,7 +199,7 @@ recursive subroutine implicitly_captured_recursive(increment)
    end interface
 end program
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_recursive
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_recursive
 ! DEVICE-SAME: {{.*}}attributes {{.*}}omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 recursive subroutine implicitly_captured_recursive(increment)
    integer :: increment
@@ -210,7 +210,7 @@ recursive subroutine implicitly_captured_recursive(increment)
    end if
 end subroutine
 
-! DEVICE-LABEL: func.func @_QPcaller_recursive
+! DEVICE-LABEL: llvm.func @_QPcaller_recursive
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 subroutine caller_recursive
 !$omp declare target to(caller_recursive) device_type(nohost)
diff --git a/flang/test/Lower/OpenMP/declare-target-implicit-tarop-cap.f90 b/flang/test/Lower/OpenMP/declare-target-implicit-tarop-cap.f90
index c31bfd1e61e8e..7a04cedf22474 100644
--- a/flang/test/Lower/OpenMP/declare-target-implicit-tarop-cap.f90
+++ b/flang/test/Lower/OpenMP/declare-target-implicit-tarop-cap.f90
@@ -1,9 +1,9 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-device %s -o - | FileCheck %s  --check-prefix=DEVICE
-!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
-!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | FileCheck %s --check-prefix=DEVICE
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | tco -test-gen | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-device %s -o - | tco -test-gen | FileCheck %s  --check-prefix=DEVICE
+!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | tco -test-gen | FileCheck %s
+!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s --check-prefix=DEVICE
 
-! DEVICE-LABEL: func.func @_QPimplicit_capture
+! DEVICE-LABEL: llvm.func @_QPimplicit_capture
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function implicit_capture() result(i)
    implicit none
@@ -20,35 +20,35 @@ subroutine subr_target()
 
 !! -----
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_nest_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_nest_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_nest_twice() result(i)
    integer :: i
    i = 10
 end function implicitly_captured_nest_twice
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_one_twice
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_one_twice
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_one_twice() result(k)
 !$omp declare target to(implicitly_captured_one_twice) device_type(host)
    k = implicitly_captured_nest_twice()
 end function implicitly_captured_one_twice
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_nest_twice_enter
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_nest_twice_enter
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (host), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_nest_twice_enter() result(i)
    integer :: i
    i = 10
 end function implicitly_captured_nest_twice_enter
 
-! CHECK-LABEL: func.func @_QPimplicitly_captured_one_twice_enter
+! CHECK-LABEL: llvm.func @_QPimplicitly_captured_one_twice_enter
 ! CHECK-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (host), capture_clause = (enter), automap = false>{{.*}}}
 function implicitly_captured_one_twice_enter() result(k)
 !$omp declare target enter(implicitly_captured_one_twice_enter) device_type(host)
    k = implicitly_captured_nest_twice_enter()
 end function implicitly_captured_one_twice_enter
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_two_twice
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_two_twice
 ! DEVICE-SAME: {{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 function implicitly_captured_two_twice() result(y)
    integer :: y
@@ -66,7 +66,7 @@ end function target_function_test_device
 
 !! -----
 
-! DEVICE-LABEL: func.func @_QPimplicitly_captured_recursive
+! DEVICE-LABEL: llvm.func @_QPimplicitly_captured_recursive
 ! DEVICE-SAME: {{.*}}attributes {{.*}}omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>{{.*}}}
 recursive function implicitly_captured_recursive(increment) result(k)
    integer :: increment, k
diff --git a/flang/test/Lower/OpenMP/declare-target-named-main-interface.f90 b/flang/test/Lower/OpenMP/declare-target-named-main-interface.f90
index b283f98f5407e..f002de46d721b 100644
--- a/flang/test/Lower/OpenMP/declare-target-named-main-interface.f90
+++ b/flang/test/Lower/OpenMP/declare-target-named-main-interface.f90
@@ -1,21 +1,14 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s --check-prefix=HOST
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-is-target-device %s -o - | FileCheck %s --check-prefix=DEVICE
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | tco -test-gen | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s
 
 ! Test that a bare '!$omp declare target' inside an interface body that
 ! appears in a *named* main program does not incorrectly mark the main
 ! program (_QQmain) as a declare-target function while still correctly
 ! marking the declared subroutine (sub_a) as device_type(nohost).
-!
-! In host compilation _QQmain must not be tagged with declare_target at all.
-! In device compilation the MarkDeclareTargetPass may annotate _QQmain with
-! device_type(host) (harmless); the bug was device_type(any) which caused
-! _QQmain to be emitted into the device image.
 
-! HOST-NOT: func.func @_QQmain{{.*}}omp.declare_target
-! DEVICE-NOT: func.func @_QQmain{{.*}}device_type = (any)
-! DEVICE-NOT: func.func @_QQmain{{.*}}device_type = (nohost)
-! HOST: func.func private @_QPsub_a{{.*}}device_type = (nohost)
-! DEVICE: func.func private @_QPsub_a{{.*}}device_type = (nohost)
+! CHECK-NOT: llvm.func @_QQmain{{.*}}device_type = (any)
+! CHECK-NOT: llvm.func @_QQmain{{.*}}device_type = (nohost)
+! CHECK: llvm.func @_QPsub_a{{.*}}device_type = (nohost), {{.*}}sym_visibility = "private"
 
 program named_main
   interface
diff --git a/flang/test/Lower/OpenMP/function-filtering-2.f90 b/flang/test/Lower/OpenMP/function-filtering-2.f90
deleted file mode 100644
index 6ced7851dd4ea..0000000000000
--- a/flang/test/Lower/OpenMP/function-filtering-2.f90
+++ /dev/null
@@ -1,49 +0,0 @@
-! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM,LLVM-HOST %s
-! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck --check-prefix=MLIR %s
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM,LLVM-DEVICE %s %}
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefix=MLIR %s %}
-! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-HOST,MLIR-ALL %s
-! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-version=52 -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-DEVICE,MLIR-ALL %s %}
-
-! MLIR: func.func @{{.*}}implicit_invocation() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>}
-! MLIR: return
-! LLVM: define {{.*}} @{{.*}}implicit_invocation{{.*}}(
-subroutine implicit_invocation()
-end subroutine implicit_invocation
-
-! MLIR: func.func @{{.*}}declaretarget() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>}
-! MLIR: return
-! LLVM: define {{.*}} @{{.*}}declaretarget{{.*}}(
-subroutine declaretarget()
-!$omp declare target to(declaretarget) device_type(nohost)
-    call implicit_invocation()
-end subroutine declaretarget
-
-! MLIR: func.func @{{.*}}declaretarget_enter() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false>}
-! MLIR: return
-! LLVM: define {{.*}} @{{.*}}declaretarget_enter{{.*}}(
-subroutine declaretarget_enter()
-!$omp declare target enter(declaretarget_enter) device_type(nohost)
-    call implicit_invocation()
-end subroutine declaretarget_enter
-
-! MLIR: func.func @{{.*}}no_declaretarget() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (to), automap = false>}
-! MLIR: return
-! LLVM: define {{.*}} @{{.*}}no_declaretarget{{.*}}(
-subroutine no_declaretarget()
-end subroutine no_declaretarget
-
-! MLIR-HOST: func.func @{{.*}}main(
-! MLIR-DEVICE-NOT: func.func @{{.*}}main(
-! MLIR-ALL: return
-
-! LLVM-HOST: define {{.*}} @{{.*}}main{{.*}}(
-! LLVM-HOST: {{.*}} @{{.*}}__omp_offloading{{.*}}main_{{.*}}(
-! LLVM-DEVICE-NOT: {{.*}} @{{.*}}main{{.*}}(
-! LLVM-DEVICE: define {{.*}} @{{.*}}__omp_offloading{{.*}}main_{{.*}}(
-program main
-!$omp target
-    call declaretarget()
-    call no_declaretarget()
-!$omp end target
-end program main
diff --git a/flang/test/Lower/OpenMP/function-filtering-3.f90 b/flang/test/Lower/OpenMP/function-filtering-3.f90
deleted file mode 100644
index 0257994b78201..0000000000000
--- a/flang/test/Lower/OpenMP/function-filtering-3.f90
+++ /dev/null
@@ -1,34 +0,0 @@
-! RUN: %flang_fc1 -fopenmp -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-HOST,LLVM-ALL %s
-! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-HOST,MLIR-ALL %s
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-is-target-device -emit-llvm %s -o - | FileCheck --check-prefixes=LLVM-DEVICE,LLVM-ALL %s %}
-! RUN: %if amdgpu-registered-target %{ %flang_fc1 -triple amdgcn-amd-amdhsa -fopenmp -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-DEVICE,MLIR-ALL %s %}
-! RUN: bbc -fopenmp -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-HOST,MLIR-ALL %s
-! RUN: %if amdgpu-registered-target %{ bbc -target amdgcn-amd-amdhsa -fopenmp -fopenmp-is-target-device -emit-hlfir %s -o - | FileCheck --check-prefixes=MLIR-DEVICE,MLIR-ALL %s %}
-
-! Check that the correct LLVM IR functions are kept for the host and device
-! after running the whole set of translation and transformation passes from
-! Fortran.
-
-! MLIR-HOST: func.func @{{.*}}host_parent_procedure(
-! MLIR-HOST: return
-! MLIR-DEVICE-NOT: func.func {{.*}}host_parent_procedure(
-
-! LLVM-HOST: define {{.*}} @host_parent_procedure{{.*}}(
-! LLVM-DEVICE-NOT: {{.*}} @{{.*}}_host_parent_procedure{{.*}}(
-subroutine host_parent_procedure(x)
-  integer, intent(out) :: x
-  call target_internal_proc(x)
-contains
-! MLIR-ALL: func.func {{.*}}@_QFhost_parent_procedurePtarget_internal_proc(
-
-! LLVM-HOST: define {{.*}} @_QFhost_parent_procedurePtarget_internal_proc(
-! LLVM-DEVICE-NOT: define {{.*}} @_QFhost_parent_procedurePtarget_internal_proc(
-! LLVM-ALL: define {{.*}} @__omp_offloading_{{.*}}QFhost_parent_procedurePtarget_internal_proc{{.*}}(
-
-subroutine target_internal_proc(x)
-  integer, intent(out) :: x
-  !$omp target map(from:x)
-    x = 10
-  !$omp end target
-end subroutine
-end subroutine
diff --git a/flang/test/Transforms/OpenMP/function-filtering.mlir b/flang/test/Transforms/OpenMP/function-filtering.mlir
deleted file mode 100644
index 343769b805494..0000000000000
--- a/flang/test/Transforms/OpenMP/function-filtering.mlir
+++ /dev/null
@@ -1,137 +0,0 @@
-// RUN: fir-opt -split-input-file --omp-function-filtering %s | FileCheck %s
-
-// CHECK: func.func @any
-// CHECK: return
-// CHECK: func.func @nohost
-// CHECK: return
-// CHECK-NOT: func.func {{.*}}}} @host
-// CHECK-NOT: func.func {{.*}}}} @none
-// CHECK: func.func @nohost_target
-// CHECK: return
-// CHECK: func.func @host_target
-// CHECK: return
-// CHECK: func.func @none_target
-// CHECK: return
-// CHECK: func.func @host_target_call
-// CHECK-NOT: call @none_target
-// CHECK: %[[UNDEF:.*]] = fir.undefined i32
-// CHECK: return %[[UNDEF]] : i32
-module attributes {omp.is_target_device = true} {
-  func.func @any() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (any), capture_clause = (to)>
-      } {
-    func.return
-  }
-  func.func @nohost() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
-      } {
-    func.return
-  }
-  func.func @host() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (host), capture_clause = (to)>
-      } {
-    func.return
-  }
-  func.func @none() -> () {
-    func.return
-  }
-  func.func @nohost_target() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
-      } {
-    omp.target kernel_type(generic) {}
-    func.return
-  }
-  func.func @host_target() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (host), capture_clause = (to)>
-      } {
-    omp.target kernel_type(generic) {}
-    func.return
-  }
-  func.func @none_target() -> i32 {
-    omp.target kernel_type(generic) {}
-    %0 = arith.constant 25 : i32
-    func.return %0 : i32
-  }
-  func.func @host_target_call() -> i32
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (host), capture_clause = (to)>
-      } {
-    omp.target kernel_type(generic) {}
-    %0 = call @none_target() : () -> i32
-    func.return %0 : i32
-  }
-}
-
-// -----
-
-// CHECK: func.func @any
-// CHECK: return
-// CHECK: func.func @nohost
-// CHECK: return
-// CHECK: func.func @host
-// CHECK: return
-// CHECK: func.func @none
-// CHECK: return
-// CHECK: func.func @nohost_target
-// CHECK: return
-// CHECK: func.func @host_target
-// CHECK: return
-// CHECK: func.func @none_target
-// CHECK: return
-module attributes {omp.is_target_device = false} {
-  func.func @any() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (any), capture_clause = (to)>
-      } {
-    func.return
-  }
-  func.func @nohost() -> ()
-      attributes {
-          omp.declare_target =
-            #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
-      } {
-    func.return
-  }
-  func.func @host() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (host), capture_clause = (to)>
-      } {
-    func.return
-  }
-  func.func @none() -> () {
-    func.return
-  }
-  func.func @nohost_target() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
-      } {
-    omp.target kernel_type(generic) {}
-    func.return
-  }
-  func.func @host_target() -> ()
-      attributes {
-        omp.declare_target =
-          #omp.declaretarget<device_type = (host), capture_clause = (to)>
-      } {
-    omp.target kernel_type(generic) {}
-    func.return
-  }
-  func.func @none_target() -> () {
-    omp.target kernel_type(generic) {}
-    func.return
-  }
-}
diff --git a/flang/test/Transforms/omp-function-filtering-todo.mlir b/flang/test/Transforms/omp-function-filtering-todo.mlir
index 280671ff774bc..3b98b1447653f 100644
--- a/flang/test/Transforms/omp-function-filtering-todo.mlir
+++ b/flang/test/Transforms/omp-function-filtering-todo.mlir
@@ -1,8 +1,8 @@
-// RUN: not fir-opt --omp-function-filtering -o - %s 2>&1 | FileCheck %s
+// RUN: fir-opt --omp-unimplemented-device-check -verify-diagnostics %s
 
 module attributes {omp.is_gpu = true, omp.is_target_device = true} {
-  // CHECK: not yet implemented: Reduction of dynamically-shaped arrays are not supported yet on the GPU.
-  omp.declare_reduction @add_reduction_byref_box_heap_Uxi32 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>> attributes {byref_element_type = !fir.array<?xi32>} alloc {
+  // expected-error @below {{not yet implemented: Reduction of dynamically-shaped arrays on the GPU.}}
+  omp.declare_reduction @red1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>> attributes {byref_element_type = !fir.array<?xi32>} alloc {
     %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>>
     omp.yield(%0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
   } init {
@@ -13,13 +13,13 @@ module attributes {omp.is_gpu = true, omp.is_target_device = true} {
     omp.yield(%arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
   }
 
-  func.func @foo(%ia : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
+  func.func @f1(%ia : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
     %ia.map = omp.map.info var_ptr(%ia : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, !fir.box<!fir.heap<!fir.array<?xi32>>>) map_clauses(always, implicit, to) capture(ByRef) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>> {name = "ia"}
 
     omp.target kernel_type(spmd) map_entries(%ia.map -> %arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
       omp.parallel {
         %c1_i32 = arith.constant 1 : i32
-        omp.wsloop reduction(byref @add_reduction_byref_box_heap_Uxi32 %arg0 -> %arg1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
+        omp.wsloop reduction(byref @red1 %arg0 -> %arg1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
           omp.loop_nest (%arg2) : i32 = (%c1_i32) to (%c1_i32) inclusive step (%c1_i32) {
             omp.yield
           }
@@ -28,6 +28,68 @@ module attributes {omp.is_gpu = true, omp.is_target_device = true} {
       } {omp.combined}
       omp.terminator
     } {omp.combined}
+
+    return
+  }
+
+  // expected-error @below {{not yet implemented: Reduction of dynamically-shaped arrays on the GPU.}}
+  omp.declare_reduction @red2 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>> attributes {byref_element_type = !fir.array<?xi32>} alloc {
+    %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>>
+    omp.yield(%0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  } init {
+  ^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>):
+    omp.yield(%arg1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  } combiner {
+  ^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>):
+    omp.yield(%arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  }
+
+  // expected-error @below {{not yet implemented: Reduction of dynamically-shaped arrays on the GPU.}}
+  omp.declare_reduction @red3 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>> attributes {byref_element_type = !fir.array<?xi32>} alloc {
+    %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>>
+    omp.yield(%0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  } init {
+  ^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>):
+    omp.yield(%arg1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  } combiner {
+  ^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>):
+    omp.yield(%arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  }
+
+  func.func @f2(%ia : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false>} {
+    %c1_i32 = arith.constant 1 : i32
+    omp.wsloop reduction(byref @red2 %ia -> %arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
+      omp.loop_nest (%arg1) : i32 = (%c1_i32) to (%c1_i32) inclusive step (%c1_i32) {
+        omp.yield
+      }
+    }
+    omp.wsloop reduction(byref @red3 %ia -> %arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
+      omp.loop_nest (%arg1) : i32 = (%c1_i32) to (%c1_i32) inclusive step (%c1_i32) {
+        omp.yield
+      }
+    }
+    return
+  }
+
+  // This emits no errors, as it's not accessed from target device code.
+  omp.declare_reduction @red4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>> attributes {byref_element_type = !fir.array<?xi32>} alloc {
+    %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>>
+    omp.yield(%0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  } init {
+  ^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>):
+    omp.yield(%arg1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  } combiner {
+  ^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>):
+    omp.yield(%arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
+  }
+
+  func.func @f3(%ia : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
+    %c1_i32 = arith.constant 1 : i32
+    omp.wsloop reduction(byref @red4 %ia -> %arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) {
+      omp.loop_nest (%arg1) : i32 = (%c1_i32) to (%c1_i32) inclusive step (%c1_i32) {
+        omp.yield
+      }
+    }
     return
   }
 }
diff --git a/mlir/include/mlir/Dialect/OpenMP/Transforms/Passes.td b/mlir/include/mlir/Dialect/OpenMP/Transforms/Passes.td
index da90ef508ce64..3825eeb286e72 100644
--- a/mlir/include/mlir/Dialect/OpenMP/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/OpenMP/Transforms/Passes.td
@@ -11,6 +11,12 @@
 
 include "mlir/Pass/PassBase.td"
 
+def FunctionFilteringPass : Pass<"omp-function-filter"> {
+  let summary = "Filters out functions intended for the host when compiling "
+                "for an OpenMP target device.";
+  let dependentDialects = ["LLVM::LLVMDialect"];
+}
+
 def HostOpFilteringPass : Pass<"omp-host-op-filter"> {
   let summary = "Removes all non target-related ops from host functions.";
   let description = [{
diff --git a/mlir/lib/Dialect/OpenMP/Transforms/CMakeLists.txt b/mlir/lib/Dialect/OpenMP/Transforms/CMakeLists.txt
index bbdbc556fc52f..04fba34ae625c 100644
--- a/mlir/lib/Dialect/OpenMP/Transforms/CMakeLists.txt
+++ b/mlir/lib/Dialect/OpenMP/Transforms/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_mlir_dialect_library(MLIROpenMPTransforms
+  FunctionFiltering.cpp
   HostOpFiltering.cpp
   MarkDeclareTarget.cpp
   OpenMPOffloadPrivatizationPrepare.cpp
diff --git a/mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp b/mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp
new file mode 100644
index 0000000000000..1d4eaaa0fb6de
--- /dev/null
+++ b/mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp
@@ -0,0 +1,109 @@
+//===- FunctionFiltering.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Filter out functions intended for the host when compiling for a target
+// device.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/OpenMP/Transforms/Passes.h"
+
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
+#include "mlir/IR/Operation.h"
+#include "mlir/Interfaces/FunctionInterfaces.h"
+
+namespace mlir {
+namespace omp {
+
+#define GEN_PASS_DEF_FUNCTIONFILTERINGPASS
+#include "mlir/Dialect/OpenMP/Transforms/Passes.h.inc"
+
+} // namespace omp
+} // namespace mlir
+
+using namespace mlir;
+
+namespace {
+
+class FunctionFilteringPass
+    : public omp::impl::FunctionFilteringPassBase<FunctionFilteringPass> {
+
+  void runOnOperation() override {
+    auto op = dyn_cast<omp::OffloadModuleInterface>(getOperation());
+    if (!op || !op.getIsTargetDevice())
+      return;
+
+    OpBuilder opBuilder(&getContext());
+    op->walk<WalkOrder::PreOrder>([&](FunctionOpInterface funcOp) {
+      // Do not filter functions with target regions inside, because they have
+      // to be available for both host and device so that regular and reverse
+      // offloading can be supported.
+      bool hasTargetRegion =
+          funcOp
+              ->walk<WalkOrder::PreOrder>([&](omp::TargetOp targetOp) {
+                return WalkResult::interrupt();
+              })
+              .wasInterrupted();
+
+      omp::DeclareTargetDeviceType declareType =
+          omp::DeclareTargetDeviceType::host;
+      auto declareTargetOp =
+          dyn_cast<omp::DeclareTargetInterface>(funcOp.getOperation());
+      if (declareTargetOp && declareTargetOp.isDeclareTarget())
+        declareType = declareTargetOp.getDeclareTargetDeviceType();
+
+      // Filtering a function here means deleting it if it doesn't contain a
+      // target region. Else we explicitly set the omp.declare_target
+      // attribute. The second stage of function filtering at the MLIR to LLVM
+      // IR translation level will remove functions that contain the target
+      // region from the generated LLVM IR.
+      if (declareType != omp::DeclareTargetDeviceType::host)
+        return WalkResult::advance();
+
+      SymbolTable::UseRange funcUses = *funcOp.getSymbolUses(op);
+      for (SymbolTable::SymbolUse use : funcUses) {
+        Operation *callOp = use.getUser();
+
+        // Do not delete other functions (which may be device functions) holding
+        // the symbol of a host function as an attribute. The remaining
+        // attribute will point to an undefined symbol after this pass.
+        if (isa<FunctionOpInterface>(callOp))
+          continue;
+
+        // If the callOp has users then replace them with poison values before
+        // removing it. These should get removed before translation to LLVM IR
+        // by the host op filtering pass.
+        if (!callOp->use_empty()) {
+          SmallVector<Value> poisonResults;
+          for (Value res : callOp->getResults()) {
+            opBuilder.setInsertionPoint(callOp);
+            poisonResults.emplace_back(
+                LLVM::PoisonOp::create(opBuilder, res.getLoc(), res.getType()));
+          }
+          callOp->replaceAllUsesWith(poisonResults);
+        }
+
+        callOp->erase();
+      }
+
+      if (!hasTargetRegion) {
+        funcOp.erase();
+        return WalkResult::skip();
+      }
+
+      if (declareTargetOp)
+        declareTargetOp.setDeclareTarget(
+            declareType, omp::DeclareTargetCaptureClause::to,
+            declareTargetOp.getDeclareTargetAutomap());
+      return WalkResult::advance();
+    });
+  }
+};
+
+} // namespace
diff --git a/mlir/test/Dialect/OpenMP/function-filter.mlir b/mlir/test/Dialect/OpenMP/function-filter.mlir
new file mode 100644
index 0000000000000..b228850c5a543
--- /dev/null
+++ b/mlir/test/Dialect/OpenMP/function-filter.mlir
@@ -0,0 +1,152 @@
+// RUN: mlir-opt -split-input-file --omp-function-filter %s | FileCheck %s
+
+// CHECK: llvm.func @any
+// CHECK: llvm.return
+// CHECK: llvm.func @nohost
+// CHECK: llvm.return
+// CHECK-NOT: llvm.func {{.*}}}} @host
+// CHECK-NOT: llvm.func {{.*}}}} @none
+// CHECK: llvm.func @nohost_target
+// CHECK: llvm.return
+// CHECK: llvm.func @host_target
+// CHECK: llvm.return
+// CHECK: llvm.func @none_target
+// CHECK: llvm.return
+// CHECK: llvm.func @host_target_call
+// CHECK-NOT: llvm.call @none_target
+// CHECK: %[[UNDEF:.*]] = llvm.mlir.poison : i32
+// CHECK: llvm.return %[[UNDEF]] : i32
+module attributes {omp.is_target_device = true} {
+  llvm.func @any() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (any), capture_clause = (to)>
+      } {
+    llvm.return
+  }
+  llvm.func @nohost() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
+      } {
+    llvm.return
+  }
+  llvm.func @host() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (host), capture_clause = (to)>
+      } {
+    llvm.return
+  }
+  llvm.func @none() -> () {
+    llvm.return
+  }
+  llvm.func @nohost_target() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
+      } {
+    omp.target kernel_type(generic) {
+      omp.terminator
+    }
+    llvm.return
+  }
+  llvm.func @host_target() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (host), capture_clause = (to)>
+      } {
+    omp.target kernel_type(generic) {
+      omp.terminator
+    }
+    llvm.return
+  }
+  llvm.func @none_target() -> i32 {
+    omp.target kernel_type(generic) {
+      omp.terminator
+    }
+    %0 = arith.constant 25 : i32
+    llvm.return %0 : i32
+  }
+  llvm.func @host_target_call() -> i32
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (host), capture_clause = (to)>
+      } {
+    omp.target kernel_type(generic) {
+      omp.terminator
+    }
+    %0 = llvm.call @none_target() : () -> i32
+    llvm.return %0 : i32
+  }
+}
+
+// -----
+
+// CHECK: llvm.func @any
+// CHECK: llvm.return
+// CHECK: llvm.func @nohost
+// CHECK: llvm.return
+// CHECK: llvm.func @host
+// CHECK: llvm.return
+// CHECK: llvm.func @none
+// CHECK: llvm.return
+// CHECK: llvm.func @nohost_target
+// CHECK: llvm.return
+// CHECK: llvm.func @host_target
+// CHECK: llvm.return
+// CHECK: llvm.func @none_target
+// CHECK: llvm.return
+module attributes {omp.is_target_device = false} {
+  llvm.func @any() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (any), capture_clause = (to)>
+      } {
+    llvm.return
+  }
+  llvm.func @nohost() -> ()
+      attributes {
+          omp.declare_target =
+            #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
+      } {
+    llvm.return
+  }
+  llvm.func @host() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (host), capture_clause = (to)>
+      } {
+    llvm.return
+  }
+  llvm.func @none() -> () {
+    llvm.return
+  }
+  llvm.func @nohost_target() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (nohost), capture_clause = (to)>
+      } {
+    omp.target kernel_type(generic) {
+      omp.terminator
+    }
+    llvm.return
+  }
+  llvm.func @host_target() -> ()
+      attributes {
+        omp.declare_target =
+          #omp.declaretarget<device_type = (host), capture_clause = (to)>
+      } {
+    omp.target kernel_type(generic) {
+      omp.terminator
+    }
+    llvm.return
+  }
+  llvm.func @none_target() -> () {
+    omp.target kernel_type(generic) {
+      omp.terminator
+    }
+    llvm.return
+  }
+}
+

>From 929c54594cace49fe8963781742fde856874dfcc Mon Sep 17 00:00:00 2001
From: Sergio Afonso <Sergio.AfonsoFumero at amd.com>
Date: Mon, 10 Aug 2026 13:13:11 +0100
Subject: [PATCH 2/2] address review comments

---
 mlir/test/Dialect/OpenMP/function-filter.mlir | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mlir/test/Dialect/OpenMP/function-filter.mlir b/mlir/test/Dialect/OpenMP/function-filter.mlir
index b228850c5a543..eec48d24ef963 100644
--- a/mlir/test/Dialect/OpenMP/function-filter.mlir
+++ b/mlir/test/Dialect/OpenMP/function-filter.mlir
@@ -4,8 +4,8 @@
 // CHECK: llvm.return
 // CHECK: llvm.func @nohost
 // CHECK: llvm.return
-// CHECK-NOT: llvm.func {{.*}}}} @host
-// CHECK-NOT: llvm.func {{.*}}}} @none
+// CHECK-NOT: llvm.func {{.*}}@host
+// CHECK-NOT: llvm.func {{.*}}@none
 // CHECK: llvm.func @nohost_target
 // CHECK: llvm.return
 // CHECK: llvm.func @host_target



More information about the flang-commits mailing list