[Mlir-commits] [flang] [mlir] [Flang][OpenMP] Don't generate code for unreachable target regions. (PR #178937)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Jan 30 10:31:41 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-llvm

@llvm/pr-subscribers-flang-fir-hlfir

Author: Abid Qadeer (abidh)

<details>
<summary>Changes</summary>

When a target region is placed inside a constant false condition (e.g., `if (.false.)`), the dead code gets eliminated on the host side, removing the `omp.target` operation entirely. However, the device-side compilation pipeline is unaware of this elimination and attempts to generate kernel code. Since the host never created offload metadata for the eliminated target, the device-side kernel function lacks the "kernel" attribute, causing `OpenMPOpt` to fail with an assertion when it expects all outlined kernels to have this attribute. The problem can be seen with the following code:

```fortran
program cele
  implicit none
  real :: V
  integer :: i
  if (.false.) then
    !$omp target teams distribute parallel do
    do i = 1, 5
      V = V * 2
    end do
    !$omp end target teams distribute parallel do
  end if
end program
```

It currently fails with the following assertion:

```
Assertion `omp::isOpenMPKernel(*Kernel) && "Expected kernel function!"' failed.
llvm/lib/Transforms/IPO/OpenMPOpt.cpp:4291
```

This PR adds `MarkUnreachableTargetsPass` that identifies `omp.target` operations in unreachable code blocks and marks them with `omp.target_unreachable` attribute. This attribute is later used in `FunctionFilteringPass` and in `OpenMPToLLVMIRTranslation` to prevent generation of code for such op.

---

Patch is 21.50 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/178937.diff


9 Files Affected:

- (modified) flang/include/flang/Optimizer/OpenMP/Passes.td (+12) 
- (modified) flang/lib/Optimizer/OpenMP/CMakeLists.txt (+1) 
- (modified) flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp (+7-2) 
- (added) flang/lib/Optimizer/OpenMP/MarkUnreachableTargets.cpp (+166) 
- (modified) flang/lib/Optimizer/Passes/Pipelines.cpp (+5) 
- (added) flang/test/Lower/OpenMP/target-dead-code.f90 (+82) 
- (added) flang/test/Transforms/OpenMP/mark-unreachable-targets.mlir (+331) 
- (modified) mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp (+5) 
- (added) mlir/test/Target/LLVMIR/omptarget-unreachable-region.mlir (+21) 


``````````diff
diff --git a/flang/include/flang/Optimizer/OpenMP/Passes.td b/flang/include/flang/Optimizer/OpenMP/Passes.td
index f17b1e3794908..793d575b7da8e 100644
--- a/flang/include/flang/Optimizer/OpenMP/Passes.td
+++ b/flang/include/flang/Optimizer/OpenMP/Passes.td
@@ -41,6 +41,18 @@ def MarkDeclareTargetPass
   let dependentDialects = ["mlir::omp::OpenMPDialect"];
 }
 
+def MarkUnreachableTargetsPass
+    : Pass<"omp-mark-unreachable-targets", "mlir::ModuleOp"> {
+  let summary = "Marks OpenMP target operations in unreachable code";
+  let description = [{
+    Identifies OpenMP target operations that reside in unreachable code
+    (e.g., inside if(.false.) blocks) and marks them with an attribute.
+    This allows device compilation to skip generating code for targets
+    that were eliminated on the host side.
+  }];
+  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.";
diff --git a/flang/lib/Optimizer/OpenMP/CMakeLists.txt b/flang/lib/Optimizer/OpenMP/CMakeLists.txt
index 23a7dc8f08399..136b9d9ea9313 100644
--- a/flang/lib/Optimizer/OpenMP/CMakeLists.txt
+++ b/flang/lib/Optimizer/OpenMP/CMakeLists.txt
@@ -8,6 +8,7 @@ add_flang_library(FlangOpenMPTransforms
   MapsForPrivatizedSymbols.cpp
   MapInfoFinalization.cpp
   MarkDeclareTarget.cpp
+  MarkUnreachableTargets.cpp
   LowerWorkdistribute.cpp
   LowerWorkshare.cpp
   LowerNontemporal.cpp
diff --git a/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp b/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
index 0acee8991e372..f7828df21182e 100644
--- a/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
+++ b/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
@@ -120,10 +120,15 @@ class FunctionFilteringPass
       // 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.
+      // However, skip target regions marked as unreachable.
       bool hasTargetRegion =
           funcOp
-              ->walk<WalkOrder::PreOrder>(
-                  [&](omp::TargetOp) { return WalkResult::interrupt(); })
+              ->walk<WalkOrder::PreOrder>([&](omp::TargetOp targetOp) {
+                // Skip targets marked as unreachable
+                if (targetOp->hasAttr("omp.target_unreachable"))
+                  return WalkResult::advance();
+                return WalkResult::interrupt();
+              })
               .wasInterrupted();
 
       omp::DeclareTargetDeviceType declareType =
diff --git a/flang/lib/Optimizer/OpenMP/MarkUnreachableTargets.cpp b/flang/lib/Optimizer/OpenMP/MarkUnreachableTargets.cpp
new file mode 100644
index 0000000000000..06423f32a77f0
--- /dev/null
+++ b/flang/lib/Optimizer/OpenMP/MarkUnreachableTargets.cpp
@@ -0,0 +1,166 @@
+//===- MarkUnreachableTargets.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 marks OpenMP target operations that are in unreachable code
+// with an attribute. This allows device compilation to skip generating code
+// for such ops.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/OpenMP/Passes.h"
+
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+#include "mlir/Dialect/Func/IR/FuncOps.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/SmallSet.h"
+
+namespace flangomp {
+#define GEN_PASS_DEF_MARKUNREACHABLETARGETSPASS
+#include "flang/Optimizer/OpenMP/Passes.h.inc"
+} // namespace flangomp
+
+using namespace mlir;
+
+namespace {
+
+/// Check if an operation is nested inside a fir.if with a constant false
+/// condition.
+static bool isInUnreachableIfBlock(Operation *op) {
+  Operation *current = op;
+
+  // Walk up through parent operations
+  while (current) {
+    Operation *parentOp = current->getParentOp();
+    if (!parentOp)
+      break;
+
+    // Check for fir.if with constant false condition
+    if (auto firIf = dyn_cast<fir::IfOp>(parentOp)) {
+      if (auto constOp =
+              firIf.getCondition().getDefiningOp<arith::ConstantOp>()) {
+        if (auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue())) {
+          // If condition is false (0) and op is in the "then" region
+          if (intAttr.getInt() == 0 &&
+              current->getParentRegion() == &firIf.getThenRegion())
+            return true;
+          // If condition is true (non-zero) and op is in the "else" region
+          if (intAttr.getInt() != 0 && !firIf.getElseRegion().empty() &&
+              current->getParentRegion() == &firIf.getElseRegion())
+            return true;
+        }
+      }
+    }
+
+    current = parentOp;
+  }
+
+  return false;
+}
+
+/// Check if a block is unreachable due to constant condition branches.
+/// A block is unreachable only if ALL predecessors lead to it through
+/// unreachable paths (i.e., constant false conditions).
+/// This handles patterns like:
+///   %false = arith.constant false
+///   cf.cond_br %false, ^bb1, ^bb2
+/// where ^bb1 is unreachable.
+static bool isBlockUnreachable(Block *block) {
+  // Entry blocks and blocks with no predecessors are reachable
+  if (block->hasNoPredecessors())
+    return false;
+
+  // Check all predecessors - block is unreachable only if ALL paths are
+  // provably unreachable via constant conditions
+  for (Block *pred : block->getPredecessors()) {
+    Operation *terminator = pred->getTerminator();
+
+    // Check if this is a cf.cond_br with constant condition
+    if (auto condBr = dyn_cast<cf::CondBranchOp>(terminator)) {
+      // Try to get the constant value of the condition
+      if (auto constOp =
+              condBr.getCondition().getDefiningOp<arith::ConstantOp>()) {
+        if (auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue())) {
+          bool condIsTrue = intAttr.getInt() != 0;
+
+          // If condition is false and block is the true destination,
+          // this path is unreachable - continue checking other predecessors
+          if (!condIsTrue && block == condBr.getTrueDest())
+            continue;
+          // If condition is true and block is the false destination,
+          // this path is unreachable - continue checking other predecessors
+          if (condIsTrue && block == condBr.getFalseDest())
+            continue;
+          // Otherwise, this path IS reachable (condition matches destination)
+          return false;
+        }
+      }
+    }
+
+    // If we reach here, this predecessor either:
+    // - is not a CondBranchOp, OR
+    // - doesn't have a constant condition
+    // Either way, this path could be taken, so block is reachable
+    return false;
+  }
+
+  // All predecessors lead to this block through unreachable paths
+  return true;
+}
+
+/// Recursively check if an operation is in an unreachable block.
+/// This walks up the block hierarchy to check if any containing block
+/// is unreachable, handling both fir.if and cf.cond_br patterns.
+static bool isOperationUnreachable(Operation *op) {
+  // First check for fir.if patterns (before SCF lowering)
+  if (isInUnreachableIfBlock(op))
+    return true;
+
+  // Then check for cf.cond_br patterns (after SCF lowering)
+  Block *currentBlock = op->getBlock();
+
+  // Walk up through nested regions checking each block
+  while (currentBlock) {
+    if (isBlockUnreachable(currentBlock))
+      return true;
+
+    // Move to parent operation's block
+    Operation *parentOp = currentBlock->getParentOp();
+    if (!parentOp || isa<ModuleOp>(parentOp) || isa<func::FuncOp>(parentOp))
+      break;
+
+    currentBlock = parentOp->getBlock();
+  }
+
+  return false;
+}
+
+class MarkUnreachableTargetsPass
+    : public flangomp::impl::MarkUnreachableTargetsPassBase<
+          MarkUnreachableTargetsPass> {
+public:
+  MarkUnreachableTargetsPass() = default;
+
+  void runOnOperation() override {
+    MLIRContext *context = &getContext();
+    auto module = getOperation();
+
+    // Walk all target operations and mark those that are unreachable
+    module.walk([&](omp::TargetOp targetOp) {
+      if (isOperationUnreachable(targetOp.getOperation()))
+        targetOp->setAttr("omp.target_unreachable", UnitAttr::get(context));
+    });
+  }
+};
+
+} // namespace
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 6054675643c64..a73a3f9d2325c 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -341,6 +341,11 @@ void createOpenMPFIRPassPipeline(mlir::PassManager &pm,
   pm.addPass(flangomp::createAutomapToTargetDataPass());
   pm.addPass(flangomp::createMapInfoFinalizationPass());
   pm.addPass(flangomp::createMarkDeclareTargetPass());
+
+  // Mark unreachable target operations before FunctionFilteringPass
+  // extracts them.
+  pm.addPass(flangomp::createMarkUnreachableTargetsPass());
+
   pm.addPass(flangomp::createGenericLoopConversionPass());
   if (opts.isTargetDevice)
     pm.addPass(flangomp::createFunctionFilteringPass());
diff --git a/flang/test/Lower/OpenMP/target-dead-code.f90 b/flang/test/Lower/OpenMP/target-dead-code.f90
new file mode 100644
index 0000000000000..82932ca82858f
--- /dev/null
+++ b/flang/test/Lower/OpenMP/target-dead-code.f90
@@ -0,0 +1,82 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s --check-prefix=FIR
+
+! Test that OpenMP target regions in dead code are marked for elimination
+
+! Test 1: if (.false.) with target - should be marked unreachable
+! FIR-LABEL: func.func @_QPtest_dead_simple
+! FIR: %[[FALSE:.*]] = arith.constant false
+! FIR: fir.if %[[FALSE]] {
+! FIR: omp.target
+! FIR: } {omp.target_unreachable}
+subroutine test_dead_simple()
+  real :: v
+  if (.false.) then
+    !$omp target map(tofrom:v)
+    v = 1.0
+    !$omp end target
+  end if
+end subroutine
+
+! Test 2: Live target - should NOT be marked
+! FIR-LABEL: func.func @_QPtest_live_simple
+! FIR: omp.target
+! FIR-NOT: omp.target_unreachable
+subroutine test_live_simple()
+  real :: v
+  !$omp target map(tofrom:v)
+  v = 2.0
+  !$omp end target
+end subroutine
+
+! Test 3: Mixed dead and live
+! FIR-LABEL: func.func @_QPtest_mixed
+subroutine test_mixed()
+  real :: v
+  ! Dead - should be marked
+  ! FIR: fir.if %{{.*}} {
+  if (.false.) then
+    !$omp target map(tofrom:v)
+    ! FIR: omp.target
+    ! FIR: } {omp.target_unreachable}
+    v = 3.0
+    !$omp end target
+  end if
+  ! Live - should NOT be marked
+  !$omp target map(tofrom:v)
+  ! FIR: omp.target
+  ! FIR-NOT: omp.target_unreachable
+  v = 4.0
+  !$omp end target
+end subroutine
+
+! Test 4: Nested - outer false
+! FIR-LABEL: func.func @_QPtest_nested_outer_false
+subroutine test_nested_outer_false()
+  real :: v
+  ! FIR: fir.if %{{.*}} {
+  if (.false.) then
+    ! FIR: fir.if %{{.*}} {
+    if (.true.) then
+      ! FIR: omp.target
+      ! FIR: } {omp.target_unreachable}
+      !$omp target map(tofrom:v)
+      v = 5.0
+      !$omp end target
+    end if
+  end if
+end subroutine
+
+! Test 5: Parameter constant
+! FIR-LABEL: func.func @_QPtest_parameter
+subroutine test_parameter()
+  real :: v
+  logical, parameter :: DEAD = .false.
+  ! FIR: fir.if %{{.*}} {
+  if (DEAD) then
+    ! FIR: omp.target
+    ! FIR: } {omp.target_unreachable}
+    !$omp target map(tofrom:v)
+    v = 6.0
+    !$omp end target
+  end if
+end subroutine
diff --git a/flang/test/Transforms/OpenMP/mark-unreachable-targets.mlir b/flang/test/Transforms/OpenMP/mark-unreachable-targets.mlir
new file mode 100644
index 0000000000000..66f7e607a65fd
--- /dev/null
+++ b/flang/test/Transforms/OpenMP/mark-unreachable-targets.mlir
@@ -0,0 +1,331 @@
+// RUN: fir-opt --omp-mark-unreachable-targets %s | FileCheck %s
+
+// CHECK-LABEL: func.func @test_if_false_simple
+func.func @test_if_false_simple() {
+  %false = arith.constant false
+  // CHECK: fir.if %{{.*}} {
+  fir.if %false {
+    // CHECK: omp.target
+    // CHECK-NEXT: omp.terminator
+    // CHECK-NEXT: } {omp.target_unreachable}
+    omp.target {
+      omp.terminator
+    }
+  }
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @test_if_true_simple
+func.func @test_if_true_simple() {
+  %true = arith.constant true
+  // CHECK: fir.if %{{.*}} {
+  fir.if %true {
+    // CHECK: omp.target {
+    // CHECK-NEXT: omp.terminator
+    // CHECK-NEXT: }
+    // CHECK-NOT: omp.target_unreachable
+    omp.target {
+      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
+  // CHECK: fir.if %{{.*}} {
+  fir.if %false {
+    // CHECK: fir.if %{{.*}} {
+    fir.if %true {
+      // CHECK: omp.target
+      // CHECK-NEXT: omp.terminator
+      // CHECK-NEXT: } {omp.target_unreachable}
+      omp.target {
+        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
+  // CHECK: fir.if %{{.*}} {
+  fir.if %true {
+    // CHECK: fir.if %{{.*}} {
+    fir.if %false {
+      // CHECK: omp.target
+      // CHECK: } {omp.target_unreachable}
+      omp.target {
+        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: fir.if %{{.*}} {
+  fir.if %true1 {
+    // CHECK: fir.if %{{.*}} {
+    fir.if %true2 {
+      // CHECK: omp.target {
+      // CHECK-NEXT: omp.terminator
+      // CHECK-NEXT: }
+      // CHECK-NOT: omp.target_unreachable
+      omp.target {
+        omp.terminator
+      }
+    }
+  }
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @test_mixed_targets
+func.func @test_mixed_targets() {
+  %false = arith.constant false
+  %true = arith.constant true
+
+  // Dead target
+  // CHECK: fir.if %{{.*}} {
+  fir.if %false {
+    // CHECK: omp.target
+    // CHECK: } {omp.target_unreachable}
+    omp.target {
+      omp.terminator
+    }
+  }
+
+  // Live target - should NOT have unreachable attribute
+  // CHECK: omp.target {
+  // CHECK-NEXT: omp.terminator
+  // CHECK-NEXT: }
+  // CHECK-NOT: omp.target_unreachable
+  omp.target {
+    omp.terminator
+  }
+
+  // Another live target in if (true)
+  // CHECK: fir.if %{{.*}} {
+  fir.if %true {
+    // CHECK: omp.target {
+    // CHECK-NEXT: omp.terminator
+    // CHECK-NEXT: }
+    // CHECK-NOT: omp.target_unreachable
+    omp.target {
+      omp.terminator
+    }
+  }
+
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @test_multiple_dead_targets
+func.func @test_multiple_dead_targets() {
+  %false = arith.constant false
+
+  // CHECK: fir.if %{{.*}} {
+  fir.if %false {
+    // CHECK: omp.target
+    // CHECK: } {omp.target_unreachable}
+    omp.target {
+      omp.terminator
+    }
+
+    // CHECK: omp.target
+    // CHECK: } {omp.target_unreachable}
+    omp.target {
+      omp.terminator
+    }
+
+    // CHECK: omp.target
+    // CHECK: } {omp.target_unreachable}
+    omp.target {
+      omp.terminator
+    }
+  }
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @test_if_else_false
+func.func @test_if_else_false() {
+  %false = arith.constant false
+
+  // CHECK: fir.if %{{.*}} {
+  fir.if %false {
+    // CHECK: omp.target
+    // CHECK: } {omp.target_unreachable}
+    omp.target {
+      omp.terminator
+    }
+  } else {
+    // Else branch should not be marked (it's reachable)
+    // CHECK: omp.target {
+    // CHECK-NEXT: omp.terminator
+    // CHECK-NEXT: }
+    // CHECK-NOT: omp.target_unreachable
+    omp.target {
+      omp.terminator
+    }
+  }
+  return
+}
+
+// -----
+
+// Test with cf.cond_br
+// CHECK-LABEL: func.func @test_cf_cond_br_false
+func.func @test_cf_cond_br_false() {
+  %false = arith.constant false
+  // CHECK: cf.cond_br %{{.*}}, ^bb1, ^bb2
+  cf.cond_br %false, ^bb1, ^bb2
+^bb1:
+  // CHECK: omp.target
+  // CHECK: } {omp.target_unreachable}
+  omp.target {
+    omp.terminator
+  }
+  cf.br ^bb2
+^bb2:
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @test_cf_cond_br_true
+func.func @test_cf_cond_br_true() {
+  %true = arith.constant true
+  // CHECK: cf.cond_br %{{.*}}, ^bb1, ^bb2
+  cf.cond_br %true, ^bb1, ^bb2
+^bb1:
+  // CHECK: omp.target {
+  // CHECK-NEXT: omp.terminator
+  // CHECK-NEXT: }
+  // CHECK-NOT: omp.target_unreachable
+  omp.target {
+    omp.terminator
+  }
+  cf.br ^bb2
+^bb2:
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @test_runtime_condition
+func.func @test_runtime_condition(%arg0: i1) {
+  // CHECK: fir.if %arg0 {
+  fir.if %arg0 {
+    // Runtime condition - should NOT be marked
+    // CHECK: omp.target {
+    // CHECK-NEXT: omp.terminator
+    // CHECK-NEXT: }
+    // CHECK-NOT: omp.target_unreachable
+    omp.target {
+      omp.terminator
+    }
+  }
+  return
+}
+
+// -----
+
+// Test for multiple predecessors - one reachable, one unreachable
+// The block should NOT be marked as unreachable if ANY path is reachable
+// CHECK-LABEL: func.func @test_multiple_predecessors
+func.func @test_multiple_predecessors() {
+  %false = arith.constant false
+  cf.cond_br %false, ^bb2, ^bb1
+^bb1:
+  // Reachable path to bb2
+  cf.br ^bb2
+^bb2:
+  // This block has two predecessors:
+  // - bb0 with false condition (unreachable path)
+  // - bb1 with unconditional branch (reachable path)
+  // Target should NOT be marked unreachable because bb1 provides a reachable path
+  // CHECK: omp.target {
+  // CHECK-NEXT: omp.terminator
+  // CHECK-NEXT: }
+  // CHECK-NOT: omp.target_unreachable
+  omp.target {
+    omp.terminator
+  }
+  return
+}
+
+// -----
+
+// Test for multiple predecessors - ALL unreachable
+// CHECK-LABEL: func.func @test_multiple_predecessors_all_unreachable
+func.func @test_multiple_predecessors_all_unreachable() {
+  %false1 = arith.constant false
+  %false2 = arith.constant false
+  cf.cond_br %false1, ^bb3, ^bb1
+^bb1:
+  cf.cond_br %false2, ^bb3, ^bb2
+^bb2:
+  cf.br ^bb4
+^bb3:
+  // This block has two predecessors:
+  // - bb0 with false condition to bb3 (unreachable)
+  // - bb1 with false condition to bb3 (unreachable)
+  // Target SHOULD be marked unreachable because ALL paths are unreachable
+  // CHECK: omp.target
+  // CHECK-NEXT: omp.terminator
+  // CHECK-NEXT: } {omp.target_unreachable}
+  omp.target {
+    omp.terminator
+  }
+  cf.br ^bb4
+^bb4:
+  return
+}
+
+// -----
+
+// Test for multiple predecessors with mixed constant and runtime conditions
+// CHECK-LABEL: func.func @test_multiple_predecessors_mixed
+func.func @test_multiple_predecessors_mixed(%arg0: i1) {
+  %false = arith.constant false
+  cf.cond_br %false, ^bb2, ^bb1
+^bb1:
+  // Runtime condition - could branch to bb2
+  cf.cond_br %arg0, ^bb2, ^bb3
+^bb2:
+  // This block has two predecessors:
+  // - bb0 with false condition (unreachable)
+  // - bb1 with runtime condition (potentially reachable)
+  // Target should NOT be marked because we can't prove bb1 path is unreachable
+  // CHECK: omp.target {
+  // CHECK-NEXT: omp.terminator
+  // CHECK-NEXT: }
+  // CHECK-NOT: omp.target_unreachable
+  omp.target {
+    omp.terminator
+  }
+  cf.br ^bb3
+^bb3:
+  return
+}
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index f04d614633965..acb6145628799 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -6370,6 +6370,11 @@ static LogicalResult
 convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
                  LLVM::ModuleTranslation &modul...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/178937


More information about the Mlir-commits mailing list