[flang-commits] [flang] [mlir] [mlir][OpenACC] Skip implicit routine marking for host-only calls (PR #227758)
Delaram Talaashrafi via flang-commits
flang-commits at lists.llvm.org
Fri Oct 2 15:50:32 PDT 2026
https://github.com/delaram-talaashrafi updated https://github.com/llvm/llvm-project/pull/227758
>From 638c959d27a05139db0715bcc61781b29fcff439 Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Wed, 30 Sep 2026 08:38:24 -0700
Subject: [PATCH 1/5] [mlir][OpenACC] Skip implicit routine marking for
host-only calls
Calls nested in a host-only branch of `acc.on_device` do not run on the
device. Do not try to attach implicit acc routine information to them.
---
.../OpenACC/acc-implicit-routine.fir | 49 ++++++++++++++++
.../mlir/Dialect/OpenACC/OpenACCUtils.h | 7 +++
.../OpenACC/Transforms/ACCImplicitRoutine.cpp | 5 ++
.../Dialect/OpenACC/Utils/OpenACCUtils.cpp | 58 +++++++++++++++++++
.../Dialect/OpenACC/acc-implicit-routine.mlir | 49 ++++++++++++++++
5 files changed, 168 insertions(+)
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-routine.fir b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
index 44ba96e3d6b04..cb071f5b12ac2 100644
--- a/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
+++ b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
@@ -147,3 +147,52 @@ module {
return
}
}
+
+// -----
+
+// Calls in a host-only branch of acc.on_device do not get an implicit routine.
+// acc_device_host = 2: the then branch is host-only.
+// acc_device_not_host = 3: the else branch is host-only.
+module {
+ func.func @_QPhost_then() {
+ return
+ }
+ func.func @_QPdevice_else() {
+ return
+ }
+ func.func @_QPdevice_then() {
+ return
+ }
+ func.func @_QPhost_else() {
+ return
+ }
+ func.func @_QPtest_host_branch() {
+ %host = arith.constant 2 : i32
+ %not_host = arith.constant 3 : i32
+ %on_host = acc.on_device %host : i32 -> i1
+ %on_not_host = acc.on_device %not_host : i32 -> i1
+ acc.serial {
+ fir.if %on_host {
+ fir.call @_QPhost_then() : () -> ()
+ } else {
+ fir.call @_QPdevice_else() : () -> ()
+ }
+ fir.if %on_not_host {
+ fir.call @_QPdevice_then() : () -> ()
+ } else {
+ fir.call @_QPhost_else() : () -> ()
+ }
+ acc.yield
+ }
+ return
+ }
+}
+
+// CHECK-NOT: acc.routine @{{.*}} func(@_QPhost_then)
+// CHECK-NOT: acc.routine @{{.*}} func(@_QPhost_else)
+// CHECK: acc.routine @acc_routine_0 func(@_QPdevice_else) implicit
+// CHECK: func.func @_QPdevice_else() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
+// CHECK: acc.routine @acc_routine_1 func(@_QPdevice_then) implicit
+// CHECK: func.func @_QPdevice_then() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_1]>}
+// CHECK-NOT: acc.routine @{{.*}} func(@_QPhost_then)
+// CHECK-NOT: acc.routine @{{.*}} func(@_QPhost_else)
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index 2d8780ecf4b16..fc16d2726164c 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -140,6 +140,13 @@ getDominatingDataClauses(mlir::Operation *computeConstructOp,
mlir::DominanceInfo &domInfo,
mlir::PostDominanceInfo &postDomInfo);
+/// Returns true if `op` is nested in a branch that executes only on the host.
+///
+/// The branch is the `then` of a condition equivalent to
+/// `acc.on_device(acc_device_host)`, or the `else` of a condition equivalent
+/// to `acc.on_device(acc_device_not_host)`.
+bool isInHostBranch(mlir::Operation *op);
+
/// Emit an OpenACC remark with lazy message generation.
///
/// The messageFn is only invoked if remarks are enabled, allowing callers
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
index 16b71189a4f94..2592903cf3066 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
@@ -49,6 +49,7 @@
#include "mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/BuiltinOps.h"
@@ -139,6 +140,8 @@ class ACCImplicitRoutine
acc::OpenACCSupport &accSupport) {
LogicalResult result = success();
op->walk([&](CallOpInterface callOp) {
+ if (acc::isInHostBranch(callOp.getOperation()))
+ return;
if (!callOp.getCallableForCallee())
return;
@@ -196,6 +199,8 @@ class ACCImplicitRoutine
auto func = symTab.lookup<FunctionOpInterface>(
currentRoutine.getFuncName().getLeafReference());
func.walk([&](CallOpInterface callOp) {
+ if (acc::isInHostBranch(callOp.getOperation()))
+ return;
if (!callOp.getCallableForCallee())
return;
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 268bbea3bee8f..5da987cd3df99 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -8,8 +8,10 @@
#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
+#include "mlir/Analysis/SliceAnalysis.h"
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/Dialect/Utils/StaticValueUtils.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/BuiltinTypes.h"
@@ -251,6 +253,7 @@ bool mlir::acc::isValidSymbolUse(mlir::Operation *user,
// Check if the defining op is a function
if (auto func =
mlir::dyn_cast_if_present<mlir::FunctionOpInterface>(definingOp)) {
+
// If this symbol is actually an acc routine or a specialized acc routine -
// then it is expected for it to be offloaded - therefore it is valid.
if (func->hasDiscardableAttr(mlir::acc::getRoutineInfoAttrName()) ||
@@ -455,6 +458,61 @@ mlir::acc::getDominatingDataClauses(mlir::Operation *computeConstructOp,
return dominatingDataClauses.takeVector();
}
+// OpenACC `acc_device_t` values passed to `acc.on_device`. These differ from
+// `acc::DeviceType`.
+static constexpr int64_t kAccDeviceHost = 2;
+static constexpr int64_t kAccDeviceNotHost = 3;
+
+static mlir::Value getIfCondition(mlir::Operation *op) {
+ if (auto ifOp = mlir::dyn_cast<mlir::scf::IfOp>(op))
+ return ifOp.getCondition();
+
+ if (!mlir::isa<mlir::RegionBranchOpInterface>(op) ||
+ mlir::isa<mlir::LoopLikeOpInterface>(op))
+ return {};
+ if (op->getNumOperands() != 1 || op->getNumRegions() != 2 ||
+ !op->getOperand(0).getType().isSignlessInteger(1))
+ return {};
+ return op->getOperand(0);
+}
+
+bool mlir::acc::isInHostBranch(mlir::Operation *op) {
+ for (mlir::Operation *parent = op->getParentOp();
+ parent &&
+ !mlir::isa<ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::ComputeRegionOp,
+ mlir::FunctionOpInterface>(parent);
+ parent = parent->getParentOp()) {
+ mlir::Value condition = getIfCondition(parent);
+ if (!condition)
+ continue;
+
+ // Include the condition's defining operation so a direct `acc.on_device`
+ // result is part of the slice.
+ mlir::BackwardSliceOptions sliceOptions;
+ sliceOptions.inclusive = true;
+ sliceOptions.omitBlockArguments = true;
+ llvm::SetVector<mlir::Operation *> slice;
+ if (failed(mlir::getBackwardSlice(condition, &slice, sliceOptions)))
+ continue;
+
+ for (mlir::Operation *sliceOp : slice) {
+ auto onDeviceOp = mlir::dyn_cast<mlir::acc::OnDeviceOp>(sliceOp);
+ if (!onDeviceOp)
+ continue;
+
+ int64_t deviceTypeValue =
+ *mlir::getConstantIntValue(onDeviceOp.getDeviceType());
+
+ bool inThen = parent->getRegion(0).isAncestor(op->getParentRegion());
+ bool inElse = parent->getRegion(1).isAncestor(op->getParentRegion());
+ if ((deviceTypeValue == kAccDeviceHost && inThen) ||
+ (deviceTypeValue == kAccDeviceNotHost && inElse))
+ return true;
+ }
+ }
+ return false;
+}
+
mlir::remark::detail::InFlightRemark
mlir::acc::emitRemark(mlir::Operation *op,
const std::function<std::string()> &messageFn,
diff --git a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
index cef8fc663291e..da93abdbffc98 100644
--- a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
@@ -299,3 +299,52 @@ module {
return
}
}
+
+// -----
+
+// Calls in a host-only branch of acc.on_device do not get an implicit routine.
+// acc_device_host = 2: the then branch is host-only.
+// acc_device_not_host = 3: the else branch is host-only.
+module {
+ func.func @host_then() {
+ return
+ }
+ func.func @device_else() {
+ return
+ }
+ func.func @device_then() {
+ return
+ }
+ func.func @host_else() {
+ return
+ }
+ func.func @test_host_branch_in_compute() {
+ %host = arith.constant 2 : i32
+ %not_host = arith.constant 3 : i32
+ %on_host = acc.on_device %host : i32 -> i1
+ %on_not_host = acc.on_device %not_host : i32 -> i1
+ acc.serial {
+ scf.if %on_host {
+ func.call @host_then() : () -> ()
+ } else {
+ func.call @device_else() : () -> ()
+ }
+ scf.if %on_not_host {
+ func.call @device_then() : () -> ()
+ } else {
+ func.call @host_else() : () -> ()
+ }
+ acc.yield
+ }
+ return
+ }
+}
+
+// CHECK-NOT: acc.routine @{{.*}} func(@host_then)
+// CHECK-NOT: acc.routine @{{.*}} func(@host_else)
+// CHECK: acc.routine @acc_routine_0 func(@device_else) implicit
+// CHECK: func.func @device_else() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
+// CHECK: acc.routine @acc_routine_1 func(@device_then) implicit
+// CHECK: func.func @device_then() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_1]>}
+// CHECK-NOT: acc.routine @{{.*}} func(@host_then)
+// CHECK-NOT: acc.routine @{{.*}} func(@host_else)
>From e69ceaacb0caafb98f254e72bc52ea199e5fcad7 Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Thu, 1 Oct 2026 13:52:56 -0700
Subject: [PATCH 2/5] Reviews
---
.../OpenACC/acc-implicit-routine.fir | 8 +++---
.../mlir/Dialect/OpenACC/OpenACCUtils.h | 13 ++++++----
.../mlir/Dialect/OpenACC/Transforms/Passes.td | 20 ++++++++++----
.../OpenACC/Transforms/ACCImplicitRoutine.cpp | 8 ++++--
.../Dialect/OpenACC/Utils/OpenACCUtils.cpp | 20 +++++++-------
.../Dialect/OpenACC/acc-implicit-routine.mlir | 26 ++++++++++++++++---
6 files changed, 65 insertions(+), 30 deletions(-)
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-routine.fir b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
index cb071f5b12ac2..b12fea9ec64c3 100644
--- a/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
+++ b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
@@ -1,4 +1,4 @@
-// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-routine)" -split-input-file -verify-diagnostics | FileCheck %s
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-routine{the-device-types=3,4})" -split-input-file -verify-diagnostics | FileCheck %s
// -----
@@ -150,9 +150,9 @@ module {
// -----
-// Calls in a host-only branch of acc.on_device do not get an implicit routine.
-// acc_device_host = 2: the then branch is host-only.
-// acc_device_not_host = 3: the else branch is host-only.
+// Calls that do not execute for the-device-types=3,4 do not get an implicit
+// routine. 3 is acc_device_not_host and 4 is acc_device_nvidia, so the then
+// of acc_on_device(2) and the else of acc_on_device(3) stay off the target.
module {
func.func @_QPhost_then() {
return
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index fc16d2726164c..f7a6339b2e928 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -140,12 +140,15 @@ getDominatingDataClauses(mlir::Operation *computeConstructOp,
mlir::DominanceInfo &domInfo,
mlir::PostDominanceInfo &postDomInfo);
-/// Returns true if `op` is nested in a branch that executes only on the host.
+/// Returns true if `op` is nested in a branch that does not execute when
+/// `acc.on_device` is true only for `deviceTypes`.
///
-/// The branch is the `then` of a condition equivalent to
-/// `acc.on_device(acc_device_host)`, or the `else` of a condition equivalent
-/// to `acc.on_device(acc_device_not_host)`.
-bool isInHostBranch(mlir::Operation *op);
+/// `deviceTypes` holds the runtime `acc_device_t` values for which
+/// `acc.on_device` is true on the target. The branch is the `then` of
+/// `acc.on_device` whose operand is outside that list, or the `else` of
+/// `acc.on_device` whose operand is in the list. A non-constant operand is
+/// ignored. An empty list means no `acc_device_t` value is true on the target.
+bool isInHostBranch(mlir::Operation *op, llvm::ArrayRef<int64_t> deviceTypes);
/// Emit an OpenACC remark with lazy message generation.
///
diff --git a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
index 9a81070200e77..eeb963d7f69f8 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
@@ -27,6 +27,12 @@ def AccDeviceTypeOption : Option<"deviceType", "device-type",
clEnumValN(mlir::acc::DeviceType::Radeon, "radeon", "radeon"))
}]>;
+// Runtime `acc_device_t` values for which `acc.on_device` is true on the
+// target device. These values are the integers carried by `acc.on_device`.
+def AccTheDeviceTypesOption : ListOption<"theDeviceTypes", "the-device-types",
+ "int64_t",
+ "Runtime acc_device_t values for which acc.on_device is true on the target">;
+
//===----------------------------------------------------------------------===//
// Pass definitions
//===----------------------------------------------------------------------===//
@@ -143,8 +149,15 @@ def ACCImplicitRoutine : Pass<"acc-implicit-routine", "mlir::ModuleOp"> {
3. Recursively walking through all existing `acc routine` and creating
implicit routine operations for function calls within these routines,
while avoiding infinite recursion through proper tracking.
+
+ Calls in a branch that does not execute on the target device are skipped.
+ `the-device-types` lists the runtime `acc_device_t` values for which
+ `acc.on_device` is true on that device. A call in the `then` of
+ `acc.on_device` whose operand is outside the list, or in the `else` of
+ `acc.on_device` whose operand is in the list, does not get an implicit
+ routine. An empty list means no `acc_device_t` value is true on the target.
}];
- let options = [ AccDeviceTypeOption ];
+ let options = [ AccDeviceTypeOption, AccTheDeviceTypesOption ];
}
def ACCDeclareGPUModuleInsertion : Pass<"acc-declare-gpu-module-insertion", "mlir::ModuleOp"> {
@@ -317,10 +330,7 @@ def ACCSpecializeForDevice : Pass<"acc-specialize-for-device", "mlir::func::Func
- Runtime ops (erased): acc.init, acc.shutdown, acc.set, acc.wait
(folded if known): acc.on_device
}];
- let options = [
- ListOption<"theDeviceTypes", "the-device-types", "int64_t",
- "List of integers which represent the device we are specializing for">
- ];
+ let options = [ AccTheDeviceTypesOption ];
}
def ACCSpecializeForHost : Pass<"acc-specialize-for-host", "mlir::func::FuncOp"> {
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
index 2592903cf3066..2be79b361e1b2 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
@@ -32,6 +32,10 @@
// implicit routine operations for function calls within these routines,
// while avoiding infinite recursion through proper tracking.
//
+// Calls in a branch that does not execute for `the-device-types` are skipped.
+// That list holds the runtime `acc_device_t` values for which `acc.on_device`
+// is true on the target.
+//
// Requirements:
// -------------
// To use this pass in a pipeline, the following requirements must be met:
@@ -140,7 +144,7 @@ class ACCImplicitRoutine
acc::OpenACCSupport &accSupport) {
LogicalResult result = success();
op->walk([&](CallOpInterface callOp) {
- if (acc::isInHostBranch(callOp.getOperation()))
+ if (acc::isInHostBranch(callOp.getOperation(), *theDeviceTypes))
return;
if (!callOp.getCallableForCallee())
return;
@@ -199,7 +203,7 @@ class ACCImplicitRoutine
auto func = symTab.lookup<FunctionOpInterface>(
currentRoutine.getFuncName().getLeafReference());
func.walk([&](CallOpInterface callOp) {
- if (acc::isInHostBranch(callOp.getOperation()))
+ if (acc::isInHostBranch(callOp.getOperation(), *theDeviceTypes))
return;
if (!callOp.getCallableForCallee())
return;
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 5da987cd3df99..2a387f40f9a8d 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -458,11 +458,6 @@ mlir::acc::getDominatingDataClauses(mlir::Operation *computeConstructOp,
return dominatingDataClauses.takeVector();
}
-// OpenACC `acc_device_t` values passed to `acc.on_device`. These differ from
-// `acc::DeviceType`.
-static constexpr int64_t kAccDeviceHost = 2;
-static constexpr int64_t kAccDeviceNotHost = 3;
-
static mlir::Value getIfCondition(mlir::Operation *op) {
if (auto ifOp = mlir::dyn_cast<mlir::scf::IfOp>(op))
return ifOp.getCondition();
@@ -476,7 +471,8 @@ static mlir::Value getIfCondition(mlir::Operation *op) {
return op->getOperand(0);
}
-bool mlir::acc::isInHostBranch(mlir::Operation *op) {
+bool mlir::acc::isInHostBranch(mlir::Operation *op,
+ llvm::ArrayRef<int64_t> deviceTypes) {
for (mlir::Operation *parent = op->getParentOp();
parent &&
!mlir::isa<ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::ComputeRegionOp,
@@ -500,13 +496,17 @@ bool mlir::acc::isInHostBranch(mlir::Operation *op) {
if (!onDeviceOp)
continue;
- int64_t deviceTypeValue =
- *mlir::getConstantIntValue(onDeviceOp.getDeviceType());
+ std::optional<int64_t> deviceTypeValue =
+ mlir::getConstantIntValue(onDeviceOp.getDeviceType());
+ if (!deviceTypeValue)
+ continue;
+ bool onTarget = llvm::is_contained(deviceTypes, *deviceTypeValue);
bool inThen = parent->getRegion(0).isAncestor(op->getParentRegion());
bool inElse = parent->getRegion(1).isAncestor(op->getParentRegion());
- if ((deviceTypeValue == kAccDeviceHost && inThen) ||
- (deviceTypeValue == kAccDeviceNotHost && inElse))
+ // Off the target: the then of a device type outside `deviceTypes`, or
+ // the else of a device type in `deviceTypes`.
+ if ((!onTarget && inThen) || (onTarget && inElse))
return true;
}
}
diff --git a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
index da93abdbffc98..e3e9bef787dcd 100644
--- a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt %s -acc-implicit-routine -split-input-file -verify-diagnostics | FileCheck %s
+// RUN: mlir-opt %s -acc-implicit-routine=the-device-types=3,4 -split-input-file -verify-diagnostics | FileCheck %s
// -----
@@ -302,9 +302,10 @@ module {
// -----
-// Calls in a host-only branch of acc.on_device do not get an implicit routine.
-// acc_device_host = 2: the then branch is host-only.
-// acc_device_not_host = 3: the else branch is host-only.
+// Calls that do not execute for the-device-types=3,4 do not get an implicit
+// routine. 3 is acc_device_not_host and 4 is acc_device_nvidia, so:
+// acc_on_device(2) then, acc_on_device(3) else, and acc_on_device(5) then
+// stay off the target. The else of acc_on_device(5) still executes on it.
module {
func.func @host_then() {
return
@@ -318,11 +319,19 @@ module {
func.func @host_else() {
return
}
+ func.func @other_device_then() {
+ return
+ }
+ func.func @other_device_else() {
+ return
+ }
func.func @test_host_branch_in_compute() {
%host = arith.constant 2 : i32
%not_host = arith.constant 3 : i32
+ %radeon = arith.constant 5 : i32
%on_host = acc.on_device %host : i32 -> i1
%on_not_host = acc.on_device %not_host : i32 -> i1
+ %on_radeon = acc.on_device %radeon : i32 -> i1
acc.serial {
scf.if %on_host {
func.call @host_then() : () -> ()
@@ -334,6 +343,11 @@ module {
} else {
func.call @host_else() : () -> ()
}
+ scf.if %on_radeon {
+ func.call @other_device_then() : () -> ()
+ } else {
+ func.call @other_device_else() : () -> ()
+ }
acc.yield
}
return
@@ -342,9 +356,13 @@ module {
// CHECK-NOT: acc.routine @{{.*}} func(@host_then)
// CHECK-NOT: acc.routine @{{.*}} func(@host_else)
+// CHECK-NOT: acc.routine @{{.*}} func(@other_device_then)
// CHECK: acc.routine @acc_routine_0 func(@device_else) implicit
// CHECK: func.func @device_else() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
// CHECK: acc.routine @acc_routine_1 func(@device_then) implicit
// CHECK: func.func @device_then() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_1]>}
// CHECK-NOT: acc.routine @{{.*}} func(@host_then)
// CHECK-NOT: acc.routine @{{.*}} func(@host_else)
+// CHECK-NOT: acc.routine @{{.*}} func(@other_device_then)
+// CHECK: acc.routine @acc_routine_2 func(@other_device_else) implicit
+// CHECK: func.func @other_device_else() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_2]>}
>From fd52af469af61051eaa1b6d6a48d0c30e3f9b455 Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Fri, 2 Oct 2026 08:36:51 -0700
Subject: [PATCH 3/5] Fix
---
.../mlir/Dialect/OpenACC/OpenACCUtils.h | 14 +++----
.../mlir/Dialect/OpenACC/Transforms/Passes.td | 7 ----
.../Dialect/OpenACC/Utils/OpenACCUtils.cpp | 41 +++++++------------
3 files changed, 21 insertions(+), 41 deletions(-)
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index f7a6339b2e928..4d7cc3da0dd84 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -140,14 +140,12 @@ getDominatingDataClauses(mlir::Operation *computeConstructOp,
mlir::DominanceInfo &domInfo,
mlir::PostDominanceInfo &postDomInfo);
-/// Returns true if `op` is nested in a branch that does not execute when
-/// `acc.on_device` is true only for `deviceTypes`.
-///
-/// `deviceTypes` holds the runtime `acc_device_t` values for which
-/// `acc.on_device` is true on the target. The branch is the `then` of
-/// `acc.on_device` whose operand is outside that list, or the `else` of
-/// `acc.on_device` whose operand is in the list. A non-constant operand is
-/// ignored. An empty list means no `acc_device_t` value is true on the target.
+/// Returns true if `op` is in a branch that does not execute on the target.
+/// `deviceTypes` lists the `acc_device_t` values for which `acc.on_device` is
+/// true. That branch is the `then` of a constant `acc.on_device` outside the
+/// list, or the `else` of one in the list. A non-constant operand is ignored.
+/// An empty list makes every such condition false, so only its `then` branch
+/// is off the target.
bool isInHostBranch(mlir::Operation *op, llvm::ArrayRef<int64_t> deviceTypes);
/// Emit an OpenACC remark with lazy message generation.
diff --git a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
index eeb963d7f69f8..202028994003d 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
@@ -149,13 +149,6 @@ def ACCImplicitRoutine : Pass<"acc-implicit-routine", "mlir::ModuleOp"> {
3. Recursively walking through all existing `acc routine` and creating
implicit routine operations for function calls within these routines,
while avoiding infinite recursion through proper tracking.
-
- Calls in a branch that does not execute on the target device are skipped.
- `the-device-types` lists the runtime `acc_device_t` values for which
- `acc.on_device` is true on that device. A call in the `then` of
- `acc.on_device` whose operand is outside the list, or in the `else` of
- `acc.on_device` whose operand is in the list, does not get an implicit
- routine. An empty list means no `acc_device_t` value is true on the target.
}];
let options = [ AccDeviceTypeOption, AccTheDeviceTypesOption ];
}
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 2a387f40f9a8d..9089a3a9e11b9 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -8,7 +8,6 @@
#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
-#include "mlir/Analysis/SliceAnalysis.h"
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
@@ -482,33 +481,23 @@ bool mlir::acc::isInHostBranch(mlir::Operation *op,
if (!condition)
continue;
- // Include the condition's defining operation so a direct `acc.on_device`
- // result is part of the slice.
- mlir::BackwardSliceOptions sliceOptions;
- sliceOptions.inclusive = true;
- sliceOptions.omitBlockArguments = true;
- llvm::SetVector<mlir::Operation *> slice;
- if (failed(mlir::getBackwardSlice(condition, &slice, sliceOptions)))
+ // The condition must be the `acc.on_device` result itself.
+ auto onDeviceOp = condition.getDefiningOp<mlir::acc::OnDeviceOp>();
+ if (!onDeviceOp)
continue;
- for (mlir::Operation *sliceOp : slice) {
- auto onDeviceOp = mlir::dyn_cast<mlir::acc::OnDeviceOp>(sliceOp);
- if (!onDeviceOp)
- continue;
-
- std::optional<int64_t> deviceTypeValue =
- mlir::getConstantIntValue(onDeviceOp.getDeviceType());
- if (!deviceTypeValue)
- continue;
-
- bool onTarget = llvm::is_contained(deviceTypes, *deviceTypeValue);
- bool inThen = parent->getRegion(0).isAncestor(op->getParentRegion());
- bool inElse = parent->getRegion(1).isAncestor(op->getParentRegion());
- // Off the target: the then of a device type outside `deviceTypes`, or
- // the else of a device type in `deviceTypes`.
- if ((!onTarget && inThen) || (onTarget && inElse))
- return true;
- }
+ std::optional<int64_t> deviceTypeValue =
+ mlir::getConstantIntValue(onDeviceOp.getDeviceType());
+ if (!deviceTypeValue)
+ continue;
+
+ bool onTarget = llvm::is_contained(deviceTypes, *deviceTypeValue);
+ bool inThen = parent->getRegion(0).isAncestor(op->getParentRegion());
+ bool inElse = parent->getRegion(1).isAncestor(op->getParentRegion());
+ // Off the target: the then of a device type outside `deviceTypes`, or
+ // the else of a device type in `deviceTypes`.
+ if ((!onTarget && inThen) || (onTarget && inElse))
+ return true;
}
return false;
}
>From 925e7dd31d9b14971258e9fd9c632114ea648594 Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Fri, 2 Oct 2026 12:35:37 -0700
Subject: [PATCH 4/5] Empty list
---
mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h | 3 +--
mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp | 4 ++++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index 4d7cc3da0dd84..2539c54cba445 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -144,8 +144,7 @@ getDominatingDataClauses(mlir::Operation *computeConstructOp,
/// `deviceTypes` lists the `acc_device_t` values for which `acc.on_device` is
/// true. That branch is the `then` of a constant `acc.on_device` outside the
/// list, or the `else` of one in the list. A non-constant operand is ignored.
-/// An empty list makes every such condition false, so only its `then` branch
-/// is off the target.
+/// An empty list returns false.
bool isInHostBranch(mlir::Operation *op, llvm::ArrayRef<int64_t> deviceTypes);
/// Emit an OpenACC remark with lazy message generation.
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 9089a3a9e11b9..12f6e19b9a5bc 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -472,6 +472,10 @@ static mlir::Value getIfCondition(mlir::Operation *op) {
bool mlir::acc::isInHostBranch(mlir::Operation *op,
llvm::ArrayRef<int64_t> deviceTypes) {
+ // No device type is known to be true, so no branch can be classified.
+ if (deviceTypes.empty())
+ return false;
+
for (mlir::Operation *parent = op->getParentOp();
parent &&
!mlir::isa<ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::ComputeRegionOp,
>From 5513c5be00e659cd5bed6e8d38d420d0174ab6af Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Fri, 2 Oct 2026 15:49:54 -0700
Subject: [PATCH 5/5] Fix
---
.../OpenACC/acc-implicit-routine.fir | 3 --
.../mlir/Dialect/OpenACC/OpenACCUtils.h | 2 +-
.../OpenACC/Transforms/ACCImplicitRoutine.cpp | 4 +-
.../Dialect/OpenACC/Utils/OpenACCUtils.cpp | 3 +-
.../Dialect/OpenACC/acc-implicit-routine.mlir | 49 +++++++++++++++++++
5 files changed, 53 insertions(+), 8 deletions(-)
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-routine.fir b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
index b12fea9ec64c3..9a0b358379a64 100644
--- a/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
+++ b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
@@ -150,9 +150,6 @@ module {
// -----
-// Calls that do not execute for the-device-types=3,4 do not get an implicit
-// routine. 3 is acc_device_not_host and 4 is acc_device_nvidia, so the then
-// of acc_on_device(2) and the else of acc_on_device(3) stay off the target.
module {
func.func @_QPhost_then() {
return
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index 2539c54cba445..79aff45314626 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -145,7 +145,7 @@ getDominatingDataClauses(mlir::Operation *computeConstructOp,
/// true. That branch is the `then` of a constant `acc.on_device` outside the
/// list, or the `else` of one in the list. A non-constant operand is ignored.
/// An empty list returns false.
-bool isInHostBranch(mlir::Operation *op, llvm::ArrayRef<int64_t> deviceTypes);
+bool isInOffTargetBranch(mlir::Operation *op, llvm::ArrayRef<int64_t> deviceTypes);
/// Emit an OpenACC remark with lazy message generation.
///
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
index 2be79b361e1b2..c5a4e35616f86 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
@@ -144,7 +144,7 @@ class ACCImplicitRoutine
acc::OpenACCSupport &accSupport) {
LogicalResult result = success();
op->walk([&](CallOpInterface callOp) {
- if (acc::isInHostBranch(callOp.getOperation(), *theDeviceTypes))
+ if (acc::isInOffTargetBranch(callOp.getOperation(), *theDeviceTypes))
return;
if (!callOp.getCallableForCallee())
return;
@@ -203,7 +203,7 @@ class ACCImplicitRoutine
auto func = symTab.lookup<FunctionOpInterface>(
currentRoutine.getFuncName().getLeafReference());
func.walk([&](CallOpInterface callOp) {
- if (acc::isInHostBranch(callOp.getOperation(), *theDeviceTypes))
+ if (acc::isInOffTargetBranch(callOp.getOperation(), *theDeviceTypes))
return;
if (!callOp.getCallableForCallee())
return;
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 12f6e19b9a5bc..6ee4669207039 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -252,7 +252,6 @@ bool mlir::acc::isValidSymbolUse(mlir::Operation *user,
// Check if the defining op is a function
if (auto func =
mlir::dyn_cast_if_present<mlir::FunctionOpInterface>(definingOp)) {
-
// If this symbol is actually an acc routine or a specialized acc routine -
// then it is expected for it to be offloaded - therefore it is valid.
if (func->hasDiscardableAttr(mlir::acc::getRoutineInfoAttrName()) ||
@@ -470,7 +469,7 @@ static mlir::Value getIfCondition(mlir::Operation *op) {
return op->getOperand(0);
}
-bool mlir::acc::isInHostBranch(mlir::Operation *op,
+bool mlir::acc::isInOffTargetBranch(mlir::Operation *op,
llvm::ArrayRef<int64_t> deviceTypes) {
// No device type is known to be true, so no branch can be classified.
if (deviceTypes.empty())
diff --git a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
index e3e9bef787dcd..0f306509b0896 100644
--- a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
@@ -1,4 +1,5 @@
// RUN: mlir-opt %s -acc-implicit-routine=the-device-types=3,4 -split-input-file -verify-diagnostics | FileCheck %s
+// RUN: mlir-opt %s -acc-implicit-routine -split-input-file -verify-diagnostics | FileCheck %s --check-prefix=NODEVICE
// -----
@@ -366,3 +367,51 @@ module {
// CHECK-NOT: acc.routine @{{.*}} func(@other_device_then)
// CHECK: acc.routine @acc_routine_2 func(@other_device_else) implicit
// CHECK: func.func @other_device_else() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_2]>}
+
+// -----
+
+// With no the-device-types, no branch is classified as off the target, so both
+// sides of acc.on_device get an implicit routine.
+module {
+ func.func @no_dev_host_then() {
+ return
+ }
+ func.func @no_dev_host_else() {
+ return
+ }
+ func.func @no_dev_device_then() {
+ return
+ }
+ func.func @no_dev_device_else() {
+ return
+ }
+ func.func @test_no_device_types() {
+ %host = arith.constant 2 : i32
+ %not_host = arith.constant 3 : i32
+ %on_host = acc.on_device %host : i32 -> i1
+ %on_device = acc.on_device %not_host : i32 -> i1
+ acc.serial {
+ scf.if %on_host {
+ func.call @no_dev_host_then() : () -> ()
+ } else {
+ func.call @no_dev_host_else() : () -> ()
+ }
+ scf.if %on_device {
+ func.call @no_dev_device_then() : () -> ()
+ } else {
+ func.call @no_dev_device_else() : () -> ()
+ }
+ acc.yield
+ }
+ return
+ }
+}
+
+// NODEVICE: acc.routine @acc_routine_0 func(@no_dev_host_then) implicit
+// NODEVICE: func.func @no_dev_host_then() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
+// NODEVICE: acc.routine @acc_routine_1 func(@no_dev_host_else) implicit
+// NODEVICE: func.func @no_dev_host_else() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_1]>}
+// NODEVICE: acc.routine @acc_routine_2 func(@no_dev_device_then) implicit
+// NODEVICE: func.func @no_dev_device_then() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_2]>}
+// NODEVICE: acc.routine @acc_routine_3 func(@no_dev_device_else) implicit
+// NODEVICE: func.func @no_dev_device_else() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_3]>}
More information about the flang-commits
mailing list