[llvm-branch-commits] [flang] [Flang][OpenMP] Remove the DeleteUnreachableTargets pass (PR #214183)

Sergio Afonso via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Aug 10 06:35:40 PDT 2026


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

>From c008226020ee05307108218eb29042e222a7bdfa Mon Sep 17 00:00:00 2001
From: Sergio Afonso <Sergio.AfonsoFumero at amd.com>
Date: Tue, 4 Aug 2026 11:04:00 +0100
Subject: [PATCH 1/2] [Flang][OpenMP] Remove the DeleteUnreachableTargets pass

The `DeleteUnreachableTargets` pass was initially added to work around
a problem caused by the interaction between the function filtering and
host op filtering passes and generic MLIR optimizations.

Specifically, by running function and host op filtering before these
optimizations, we would lose the ability to detect unreachable
`omp.target` operations when compiling for the device. This would cause
GPU kernels being created for them, for which no host counterpart
existed.

By now having moved both passes towards the end of the compilation
pipeline, after FIR to LLVM lowering, removal of unreachable host and
device code is no longer impacted by them. This makes the
`DeleteUnreachableTargets` pass redundant.
---
 .../include/flang/Optimizer/OpenMP/Passes.td  |  12 -
 flang/lib/Optimizer/OpenMP/CMakeLists.txt     |   1 -
 .../OpenMP/DeleteUnreachableTargets.cpp       |  76 -----
 flang/lib/Optimizer/Passes/Pipelines.cpp      |   3 -
 flang/test/Lower/OpenMP/target-dead-code.f90  | 229 +++++++++++--
 .../OpenMP/delete-unreachable-targets.mlir    | 322 ------------------
 6 files changed, 202 insertions(+), 441 deletions(-)
 delete mode 100644 flang/lib/Optimizer/OpenMP/DeleteUnreachableTargets.cpp
 delete mode 100644 flang/test/Transforms/OpenMP/delete-unreachable-targets.mlir

diff --git a/flang/include/flang/Optimizer/OpenMP/Passes.td b/flang/include/flang/Optimizer/OpenMP/Passes.td
index b81ff842948c3..08272d24252ee 100644
--- a/flang/include/flang/Optimizer/OpenMP/Passes.td
+++ b/flang/include/flang/Optimizer/OpenMP/Passes.td
@@ -35,18 +35,6 @@ def MapsForPrivatizedSymbolsPass
   let dependentDialects = ["mlir::omp::OpenMPDialect"];
 }
 
-def DeleteUnreachableTargetsPass
-    : Pass<"omp-delete-unreachable-targets", "mlir::ModuleOp"> {
-  let summary = "Deletes OpenMP target operations in unreachable code";
-  let description = [{
-    Identifies and removes OpenMP target operations that reside in unreachable
-    code (e.g., inside if(.false.) blocks). This ensures consistency between
-    host and device compilation by preventing unreachable targets from being
-    processed on the device side.
-  }];
-  let dependentDialects = ["mlir::omp::OpenMPDialect"];
-}
-
 def DoConcurrentConversionPass : Pass<"omp-do-concurrent-conversion", "mlir::ModuleOp"> {
   let summary = "Map `DO CONCURRENT` loops to OpenMP worksharing loops.";
 
diff --git a/flang/lib/Optimizer/OpenMP/CMakeLists.txt b/flang/lib/Optimizer/OpenMP/CMakeLists.txt
index 9c14716f4f06a..f29ba86a8a28c 100644
--- a/flang/lib/Optimizer/OpenMP/CMakeLists.txt
+++ b/flang/lib/Optimizer/OpenMP/CMakeLists.txt
@@ -6,7 +6,6 @@ add_flang_library(FlangOpenMPTransforms
   GenericLoopConversion.cpp
   MapsForPrivatizedSymbols.cpp
   MapInfoFinalization.cpp
-  DeleteUnreachableTargets.cpp
   LowerWorkdistribute.cpp
   LowerWorkshare.cpp
   LowerNontemporal.cpp
diff --git a/flang/lib/Optimizer/OpenMP/DeleteUnreachableTargets.cpp b/flang/lib/Optimizer/OpenMP/DeleteUnreachableTargets.cpp
deleted file mode 100644
index de2b226f85f23..0000000000000
--- a/flang/lib/Optimizer/OpenMP/DeleteUnreachableTargets.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-//===- DeleteUnreachableTargets.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 pass removes OpenMP target operations that are in unreachable code.
-// This ensures host and device compilation have consistent target regions.
-//
-//===----------------------------------------------------------------------===//
-
-#include "flang/Optimizer/OpenMP/Passes.h"
-#include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h"
-#include "mlir/Analysis/DataFlow/Utils.h"
-#include "mlir/Analysis/DataFlowFramework.h"
-#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
-#include "mlir/IR/BuiltinOps.h"
-#include "mlir/Pass/Pass.h"
-#include "mlir/Support/LLVM.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace flangomp {
-#define GEN_PASS_DEF_DELETEUNREACHABLETARGETSPASS
-#include "flang/Optimizer/OpenMP/Passes.h.inc"
-} // namespace flangomp
-
-using namespace mlir;
-
-namespace {
-
-/// Check if an operation is unreachable using DeadCodeAnalysis.
-static bool isOperationUnreachable(Operation *op, DataFlowSolver &solver) {
-  Block *block = op->getBlock();
-  if (!block)
-    return false;
-
-  // Query DeadCodeAnalysis to check if the block is live (reachable).
-  ProgramPoint *point = solver.getProgramPointBefore(block);
-  const dataflow::Executable *executable =
-      solver.lookupState<dataflow::Executable>(point);
-
-  return (executable && !executable->isLive());
-}
-
-class DeleteUnreachableTargetsPass
-    : public flangomp::impl::DeleteUnreachableTargetsPassBase<
-          DeleteUnreachableTargetsPass> {
-public:
-  DeleteUnreachableTargetsPass() = default;
-
-  void runOnOperation() override {
-    auto module = getOperation();
-    DataFlowSolver solver;
-    dataflow::loadBaselineAnalyses(solver);
-
-    if (failed(solver.initializeAndRun(module))) {
-      signalPassFailure();
-      return;
-    }
-
-    // Collect unreachable target operations
-    SmallVector<omp::TargetOp> unreachableTargets;
-    module.walk([&](omp::TargetOp targetOp) {
-      if (isOperationUnreachable(targetOp.getOperation(), solver))
-        unreachableTargets.push_back(targetOp);
-    });
-
-    // Delete unreachable target operations
-    for (omp::TargetOp targetOp : unreachableTargets)
-      targetOp->erase();
-  }
-};
-
-} // namespace
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 2812d23bbec73..cc61237760178 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -372,9 +372,6 @@ void createOpenMPFIRPassPipeline(mlir::PassManager &pm,
   pm.addPass(flangomp::createAutomapToTargetDataPass());
   pm.addPass(flangomp::createMapInfoFinalizationPass());
 
-  // Delete unreachable target operations before FunctionFilteringPass
-  // extracts them.
-  pm.addPass(flangomp::createDeleteUnreachableTargetsPass());
   pm.addPass(flangomp::createGenericLoopConversionPass());
 }
 
diff --git a/flang/test/Lower/OpenMP/target-dead-code.f90 b/flang/test/Lower/OpenMP/target-dead-code.f90
index 876618d6d963c..1e668cf78ce54 100644
--- a/flang/test/Lower/OpenMP/target-dead-code.f90
+++ b/flang/test/Lower/OpenMP/target-dead-code.f90
@@ -1,12 +1,14 @@
-! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s --check-prefix=FIR
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | tco -test-gen | FileCheck %s --check-prefixes=HOST,ALL
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s --check-prefixes=DEVICE,ALL
 
-! Test that OpenMP target regions in dead code are deleted
+! Test that OpenMP target regions in dead code are deleted from both host and
+! target device.
 
 ! Test 1: if (.false.) with target - target should be deleted
-! FIR-LABEL: func.func @_QPtest_dead_simple
-! FIR: %[[FALSE:.*]] = arith.constant false
-! FIR: fir.if %[[FALSE]] {
-! FIR-NOT: omp.target
+! HOST-LABEL: llvm.func @_QPtest_dead_simple
+! HOST-NOT: omp.target
+! HOST: llvm.return
+! DEVICE-NOT: llvm.func @_QPtest_dead_simple
 subroutine test_dead_simple()
   real :: v
   if (.false.) then
@@ -17,39 +19,49 @@ subroutine test_dead_simple()
 end subroutine
 
 ! Test 2: Live target - should remain
-! FIR-LABEL: func.func @_QPtest_live_simple
-! FIR: omp.target
+! ALL-LABEL: llvm.func @_QPtest_live_simple
+! ALL: omp.target
+! ALL: llvm.return
 subroutine test_live_simple()
   real :: v
-  !$omp target map(tofrom:v)
-  v = 2.0
-  !$omp end target
+  if (.true.) then
+    !$omp target map(tofrom:v)
+    v = 2.0
+    !$omp end target
+  end if
 end subroutine
 
 ! Test 3: Mixed dead and live
-! FIR-LABEL: func.func @_QPtest_mixed
+! ALL-LABEL: llvm.func @_QPtest_mixed
 subroutine test_mixed()
   real :: v
   ! Dead - should be deleted
-  ! FIR: fir.if %{{.*}} {
+  ! ALL-NOT: {{.*}} = llvm.mlir.constant(3.0{{.*}} : f32)
   if (.false.) then
     !$omp target map(tofrom:v)
     v = 3.0
     !$omp end target
   end if
-  ! FIR-NOT: omp.target
-  ! Live - should remain (expect exactly 1 omp.target in function)
+
+  ! Live - should remain
   !$omp target map(tofrom:v)
-  ! FIR: omp.target
+  ! ALL: omp.target
+  ! ALL: {{.*}} = llvm.mlir.constant(4.0{{.*}} : f32)
   v = 4.0
   !$omp end target
+
+  ! Expect exactly 1 omp.target in function
+  ! ALL-NOT: omp.target
+  ! ALL: llvm.return
 end subroutine
 
 ! Test 4: Nested - outer false, target should be deleted
-! FIR-LABEL: func.func @_QPtest_nested_outer_false
+! HOST-LABEL: llvm.func @_QPtest_nested_outer_false
+! HOST-NOT: omp.target
+! HOST: llvm.return
+! DEVICE-NOT: llvm.func @_QPtest_nested_outer_false
 subroutine test_nested_outer_false()
   real :: v
-  ! FIR: fir.if %{{.*}} {
   if (.false.) then
     if (.true.) then
       !$omp target map(tofrom:v)
@@ -57,32 +69,195 @@ subroutine test_nested_outer_false()
       !$omp end target
     end if
   end if
-  ! FIR-NOT: omp.target
 end subroutine
 
-! Test 5: Parameter constant - target should be deleted
-! FIR-LABEL: func.func @_QPtest_parameter
+! Test 5: Nested - inner false, target should be deleted
+! HOST-LABEL: llvm.func @_QPtest_nested_inner_false
+! HOST-NOT: omp.target
+! HOST: llvm.return
+! DEVICE-NOT: llvm.func @_QPtest_nested_inner_false
+subroutine test_nested_inner_false()
+  real :: v
+  if (.true.) then
+    if (.false.) then
+      !$omp target map(tofrom:v)
+      v = 6.0
+      !$omp end target
+    end if
+  end if
+end subroutine
+
+! Test 6: Nested - both true, target should remain
+! ALL-LABEL: llvm.func @_QPtest_nested_both_true
+! ALL: omp.target
+! ALL: llvm.return
+subroutine test_nested_both_true()
+  real :: v
+  if (.true.) then
+    if (.true.) then
+      !$omp target map(tofrom:v)
+      v = 7.0
+      !$omp end target
+    end if
+  end if
+end subroutine
+
+! Test 7: Multiple dead targets in dead branch - all should be deleted
+! HOST-LABEL: llvm.func @_QPtest_multiple_dead_targets
+! HOST-NOT: omp.target
+! HOST: llvm.return
+! DEVICE-NOT: llvm.func @_QPtest_multiple_dead_targets
+subroutine test_multiple_dead_targets()
+  real :: v
+  if (.false.) then
+    !$omp target map(tofrom:v)
+    v = 8.0
+    !$omp end target
+    !$omp target map(tofrom:v)
+    v = 9.0
+    !$omp end target
+    !$omp target map(tofrom:v)
+    v = 10.0
+    !$omp end target
+  end if
+end subroutine
+
+! Test 8: Parameter constant - target should be deleted
+! HOST-LABEL: llvm.func @_QPtest_parameter
+! HOST-NOT: omp.target
+! HOST: llvm.return
+! DEVICE-NOT: llvm.func @_QPtest_parameter
 subroutine test_parameter()
   real :: v
   logical, parameter :: DEAD = .false.
-  ! FIR: fir.if %{{.*}} {
   if (DEAD) then
     !$omp target map(tofrom:v)
-    v = 6.0
+    v = 11.0
     !$omp end target
   end if
-  ! FIR-NOT: omp.target
 end subroutine
 
-! FIR-LABEL: func.func @_QPtest_outer
+! Test 9: Unused nested subroutine - target should be deleted
+! HOST-LABEL: llvm.func @_QPtest_outer
+! HOST-NOT: omp.target
+! HOST: llvm.return
+! DEVICE-NOT: llvm.func @_QPtest_outer
 subroutine test_outer
   implicit none
 contains
   subroutine unused_sub()
     real :: v
     !$omp target map(tofrom: v)
-      v = 5.0
+      v = 12.0
     !$omp end target
   end subroutine
-  ! FIR-NOT: omp.target
+end subroutine
+
+! Test 10: if (.false.) with else - then-branch target deleted, else-branch remains
+! ALL-LABEL: llvm.func @_QPtest_if_else_false
+subroutine test_if_else_false()
+  real :: v
+  ! Dead then-branch - target should be deleted
+  ! ALL-NOT: {{.*}} = llvm.mlir.constant(1.3{{.*}}e+01 : f32)
+  if (.false.) then
+    !$omp target map(tofrom:v)
+    v = 13.0
+    !$omp end target
+  else
+    ! Live else-branch - target should remain
+    !$omp target map(tofrom:v)
+    ! ALL: omp.target
+    ! ALL: {{.*}} = llvm.mlir.constant(1.4{{.*}}e+01 : f32)
+    v = 14.0
+    !$omp end target
+  end if
+  ! Expect exactly 1 omp.target in function
+  ! ALL-NOT: omp.target
+  ! ALL: llvm.return
+end subroutine
+
+! Test 11: Runtime condition - target should remain unchanged
+! ALL-LABEL: llvm.func @_QPtest_runtime_condition
+! ALL: omp.target
+! ALL: llvm.return
+subroutine test_runtime_condition(cond)
+  logical, intent(in) :: cond
+  real :: v
+  if (cond) then
+    !$omp target map(tofrom:v)
+    v = 15.0
+    !$omp end target
+  end if
+end subroutine
+
+! Test 12: Target nested in unreachable block - target should be deleted
+! ALL-LABEL: llvm.func @_QPtest_nested_in_unreachable_block
+subroutine test_nested_in_unreachable_block()
+  real :: v
+  go to 10
+  ! Unreachable block: even though condition is .true., the block itself is dead
+  ! ALL-NOT: {{.*}} = llvm.mlir.constant(1.6{{.*}}e+01 : f32)
+  if (.true.) then
+    !$omp target map(tofrom:v)
+    v = 16.0
+    !$omp end target
+  end if
+10 continue
+  ! Reachable - target should remain
+  !$omp target map(tofrom:v)
+  ! ALL: omp.target
+  ! ALL: {{.*}} = llvm.mlir.constant(1.7{{.*}}e+01 : f32)
+  v = 17.0
+  !$omp end target
+  ! Expect exactly 1 omp.target in function
+  ! ALL-NOT: omp.target
+  ! ALL: llvm.return
+end subroutine
+
+! Test 13: Multiple targets in unreachable blocks - all should be deleted
+! ALL-LABEL: llvm.func @_QPtest_multiple_unreachable_blocks
+subroutine test_multiple_unreachable_blocks()
+  real :: v
+  go to 30
+  ! First unreachable block - target should be deleted
+  ! ALL-NOT: {{.*}} = llvm.mlir.constant(1.8{{.*}}e+01 : f32)
+  !$omp target map(tofrom:v)
+  v = 18.0
+  !$omp end target
+  go to 20
+20 continue
+  ! Second unreachable block (only reachable from first unreachable block)
+  ! ALL-NOT: {{.*}} = llvm.mlir.constant(1.9{{.*}}e+01 : f32)
+  !$omp target map(tofrom:v)
+  v = 19.0
+  !$omp end target
+30 continue
+  ! Reachable from entry - target should remain
+  !$omp target map(tofrom:v)
+  ! ALL: omp.target
+  ! ALL: {{.*}} = llvm.mlir.constant(2.0{{.*}}e+01 : f32)
+  v = 20.0
+  !$omp end target
+  ! Expect exactly 1 omp.target in function
+  ! ALL-NOT: omp.target
+  ! ALL: llvm.return
+end subroutine
+
+! Test 14: Both branches reachable - all targets should remain
+! ALL-LABEL: llvm.func @_QPtest_both_branches_reachable
+! ALL: omp.target
+! ALL: omp.target
+! ALL: llvm.return
+subroutine test_both_branches_reachable(cond)
+  logical, intent(in) :: cond
+  real :: v
+  if (cond) then
+    !$omp target map(tofrom:v)
+    v = 21.0
+    !$omp end target
+  else
+    !$omp target map(tofrom:v)
+    v = 22.0
+    !$omp end target
+  end if
 end subroutine
diff --git a/flang/test/Transforms/OpenMP/delete-unreachable-targets.mlir b/flang/test/Transforms/OpenMP/delete-unreachable-targets.mlir
deleted file mode 100644
index 6db167b51039d..0000000000000
--- a/flang/test/Transforms/OpenMP/delete-unreachable-targets.mlir
+++ /dev/null
@@ -1,322 +0,0 @@
-// RUN: fir-opt --omp-delete-unreachable-targets %s | FileCheck %s
-
-// This test verifies that OpenMP target operations in unreachable code are
-// deleted.
-
-
-// CHECK-LABEL: func.func @test_if_false_simple
-func.func @test_if_false_simple() {
-  %false = arith.constant false
-  // The target in the dead branch should be removed
-  // CHECK: fir.if %false {
-  // CHECK-NOT: omp.target
-  // CHECK: }
-  fir.if %false {
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_if_true_simple
-func.func @test_if_true_simple() {
-  %true = arith.constant true
-  // The target should remain since the branch is reachable
-  // CHECK: omp.target
-  fir.if %true {
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_nested_outer_false
-func.func @test_nested_outer_false() {
-  %false = arith.constant false
-  %true = arith.constant true
-  // Outer false makes the whole nested structure unreachable
-  // CHECK: fir.if %false {
-  // CHECK-NOT: omp.target
-  // CHECK: }
-  fir.if %false {
-    fir.if %true {
-      omp.target kernel_type(generic) {
-        omp.terminator
-      }
-    }
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_nested_inner_false
-func.func @test_nested_inner_false() {
-  %false = arith.constant false
-  %true = arith.constant true
-  // Outer true, inner false - target should be removed
-  // CHECK: fir.if %true {
-  // CHECK: fir.if %false {
-  // CHECK-NOT: omp.target
-  // CHECK: }
-  fir.if %true {
-    fir.if %false {
-      omp.target kernel_type(generic) {
-        omp.terminator
-      }
-    }
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_nested_both_true
-func.func @test_nested_both_true() {
-  %true1 = arith.constant true
-  %true2 = arith.constant true
-  // CHECK: omp.target
-  fir.if %true1 {
-    fir.if %true2 {
-      omp.target kernel_type(generic) {
-        omp.terminator
-      }
-    }
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_mixed_targets
-func.func @test_mixed_targets() {
-  %false = arith.constant false
-  %true = arith.constant true
-
-  // Live target - should remain (expect 2 targets total in output)
-  // CHECK: omp.target
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-
-  // Another live target in if (true) - should remain
-  // CHECK: omp.target
-  fir.if %true {
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-
-  // Dead target - will be removed
-  // CHECK-NOT: omp.target
-  fir.if %false {
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_multiple_dead_targets
-func.func @test_multiple_dead_targets() {
-  %false = arith.constant false
-
-  // All targets inside dead branch should be removed
-  // CHECK-NOT: omp.target
-  fir.if %false {
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_if_else_false
-func.func @test_if_else_false() {
-  %false = arith.constant false
-
-  // CHECK: fir.if %false {
-  fir.if %false {
-    // Then branch is unreachable, target should be deleted
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  } else {
-    // CHECK-NOT: omp.target
-    // CHECK: } else {
-    // Else branch is reachable, target should remain
-    // CHECK: omp.target
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_runtime_condition
-func.func @test_runtime_condition(%arg0: i1) {
-  // Runtime condition - cannot be optimized, should remain unchanged
-  // CHECK: fir.if %arg0 {
-  fir.if %arg0 {
-    // CHECK: omp.target
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-  return
-}
-
-// -----
-
-// Test that targets nested in structured control flow within unreachable blocks
-// are correctly identified as unreachable
-// CHECK-LABEL: func.func @test_nested_in_unreachable_block
-func.func @test_nested_in_unreachable_block() {
-  cf.br ^bb2
-^bb1:
-  // This entire block is unreachable
-  // Even though the fir.if condition is true, the whole block is dead
-  %true = arith.constant true
-  // CHECK: ^bb1:
-  // CHECK-NOT: omp.target
-  // CHECK: cf.br ^bb2
-  fir.if %true {
-    omp.target kernel_type(generic) {
-      omp.terminator
-    }
-  }
-  cf.br ^bb2
-^bb2:
-  // CHECK: ^bb2:
-  // CHECK-NEXT: omp.target
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_unreachable_block_after_branch
-func.func @test_unreachable_block_after_branch() {
-  cf.br ^bb2
-^bb1:
-  // This block is unreachable - no predecessor branches to it
-  // CHECK: ^bb1:
-  // CHECK-NOT: omp.target
-  // CHECK: cf.br ^bb2
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  cf.br ^bb2
-^bb2:
-  // This block is reachable
-  // CHECK: ^bb2:
-  // CHECK-NEXT: omp.target
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_multiple_unreachable_blocks
-func.func @test_multiple_unreachable_blocks() {
-  cf.br ^bb3
-^bb1:
-  // Unreachable block - no predecessor branches to it
-  // CHECK: ^bb1:
-  // CHECK-NOT: omp.target
-  // CHECK: cf.br ^bb2
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  cf.br ^bb2
-^bb2:
-  // Also unreachable - only reachable from ^bb1 which is itself unreachable
-  // CHECK: ^bb2:
-  // CHECK-NOT: omp.target
-  // CHECK: return
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  return
-^bb3:
-  // Reachable from entry
-  // CHECK: ^bb3:
-  // CHECK-NEXT: omp.target
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_both_branches_reachable
-func.func @test_both_branches_reachable(%arg0: i1) {
-  cf.cond_br %arg0, ^bb1, ^bb2
-^bb1:
-  // CHECK: ^bb1:
-  // CHECK-NEXT: omp.target
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  cf.br ^bb3
-^bb2:
-  // CHECK: ^bb2:
-  // CHECK-NEXT: omp.target
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  cf.br ^bb3
-^bb3:
-  return
-}
-
-// -----
-
-// CHECK-LABEL: func.func @test_disconnected_block
-func.func @test_disconnected_block() {
-  // Entry goes directly to exit
-  cf.br ^bb2
-^bb1:
-  // This block is completely disconnected - no way to reach it
-  // CHECK: ^bb1:
-  // CHECK-NOT: omp.target
-  // CHECK: cf.br ^bb2
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  cf.br ^bb2
-^bb2:
-  // Reachable from entry
-  // CHECK: ^bb2:
-  // CHECK-NEXT: omp.target
-  omp.target kernel_type(generic) {
-    omp.terminator
-  }
-  return
-}

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

---
 .../{Lower => Integration}/OpenMP/target-dead-code.f90    | 8 ++++++++
 1 file changed, 8 insertions(+)
 rename flang/test/{Lower => Integration}/OpenMP/target-dead-code.f90 (93%)

diff --git a/flang/test/Lower/OpenMP/target-dead-code.f90 b/flang/test/Integration/OpenMP/target-dead-code.f90
similarity index 93%
rename from flang/test/Lower/OpenMP/target-dead-code.f90
rename to flang/test/Integration/OpenMP/target-dead-code.f90
index 1e668cf78ce54..cdcf7d39bb67c 100644
--- a/flang/test/Lower/OpenMP/target-dead-code.f90
+++ b/flang/test/Integration/OpenMP/target-dead-code.f90
@@ -1,3 +1,11 @@
+!===----------------------------------------------------------------------===!
+! This directory can be used to add Integration tests involving multiple
+! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
+! contain executable tests. We should only add tests here sparingly and only
+! if there is no other way to test. Repeat this message in each test that is
+! added to this directory and sub-directories.
+!===----------------------------------------------------------------------===!
+
 ! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | tco -test-gen | FileCheck %s --check-prefixes=HOST,ALL
 ! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-is-target-device %s -o - | tco -test-gen | FileCheck %s --check-prefixes=DEVICE,ALL
 



More information about the llvm-branch-commits mailing list