[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