[llvm-branch-commits] [flang] [flang][acc] Emit acc.on_device operation for acc_on_device call (PR #208098)
Ivan R. Ivanov via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jul 8 07:57:11 PDT 2026
https://github.com/ivanradanov updated https://github.com/llvm/llvm-project/pull/208098
>From ef030255af3f253a71a4bbed912d2917cff31b15 Mon Sep 17 00:00:00 2001
From: Ivan Radanov Ivanov <iivanov at nvidia.com>
Date: Tue, 7 Jul 2026 12:29:36 -0700
Subject: [PATCH 1/2] [flang][acc] Emit acc.on_device operation for
acc_on_device call
It is important we recognize acc_on_device calls as they need to be
folded during compilation. Emitting this operation helps with the
recognition of the runtime call in the optimizer.
---
.../Optimizer/Builder/CUDAIntrinsicCall.h | 3 +-
.../flang/Optimizer/Builder/IntrinsicCall.h | 16 +++++-
.../Optimizer/Builder/OpenACCIntrinsicCall.h | 32 ++++++++++++
.../Optimizer/Builder/PPCIntrinsicCall.h | 3 +-
flang/lib/Lower/ConvertCall.cpp | 15 +++---
flang/lib/Optimizer/Builder/CMakeLists.txt | 1 +
.../Optimizer/Builder/CUDAIntrinsicCall.cpp | 20 ++------
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 42 ++++++++-------
.../Builder/OpenACCIntrinsicCall.cpp | 51 +++++++++++++++++++
.../Optimizer/Builder/PPCIntrinsicCall.cpp | 6 ++-
flang/test/Lower/OpenACC/acc-on-device.f90 | 14 +++++
11 files changed, 158 insertions(+), 45 deletions(-)
create mode 100644 flang/include/flang/Optimizer/Builder/OpenACCIntrinsicCall.h
create mode 100644 flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp
create mode 100644 flang/test/Lower/OpenACC/acc-on-device.f90
diff --git a/flang/include/flang/Optimizer/Builder/CUDAIntrinsicCall.h b/flang/include/flang/Optimizer/Builder/CUDAIntrinsicCall.h
index 6167a876f7b62..2974d774b2d1a 100644
--- a/flang/include/flang/Optimizer/Builder/CUDAIntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/CUDAIntrinsicCall.h
@@ -108,7 +108,8 @@ struct CUDAIntrinsicLibrary : IntrinsicLibrary {
mlir::Value genVoteSync(mlir::Type, llvm::ArrayRef<mlir::Value>);
};
-const IntrinsicHandler *findCUDAIntrinsicHandler(llvm::StringRef name);
+const IntrinsicHandler *findCUDAIntrinsicHandler(llvm::StringRef name,
+ bool isBindcCall = false);
} // namespace fir
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index ca14b62c2ed48..53c975e4b3f66 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -796,7 +796,8 @@ mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
/// \p intrinsicName.
std::optional<IntrinsicHandlerEntry>
lookupIntrinsicHandler(fir::FirOpBuilder &, llvm::StringRef intrinsicName,
- std::optional<mlir::Type> resultType);
+ std::optional<mlir::Type> resultType,
+ bool isBindcCall = false);
/// Generate a TODO error message for an as yet unimplemented intrinsic.
void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name);
@@ -846,6 +847,19 @@ mlir::Value genDivC(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value genPow(fir::FirOpBuilder &, mlir::Location, mlir::Type resultType,
mlir::Value x, mlir::Value y);
+template <std::size_t N>
+static constexpr bool isSorted(const IntrinsicHandler (&array)[N]) {
+ // Replace by std::sorted when C++20 is default (will be constexpr).
+ const IntrinsicHandler *lastSeen{nullptr};
+ bool isSorted{true};
+ for (const auto &x : array) {
+ if (lastSeen)
+ isSorted &= std::string_view{lastSeen->name} < std::string_view{x.name};
+ lastSeen = &x;
+ }
+ return isSorted;
+}
+
} // namespace fir
#endif // FORTRAN_LOWER_INTRINSICCALL_H
diff --git a/flang/include/flang/Optimizer/Builder/OpenACCIntrinsicCall.h b/flang/include/flang/Optimizer/Builder/OpenACCIntrinsicCall.h
new file mode 100644
index 0000000000000..585c50bf12edc
--- /dev/null
+++ b/flang/include/flang/Optimizer/Builder/OpenACCIntrinsicCall.h
@@ -0,0 +1,32 @@
+//===-- Builder/OpenACCIntrinsicCall.h - OpenACC intrinsics -----*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_LOWER_OPENACCINTRINSICCALL_H
+#define FORTRAN_LOWER_OPENACCINTRINSICCALL_H
+
+#include "flang/Optimizer/Builder/IntrinsicCall.h"
+
+namespace fir {
+
+struct OpenACCIntrinsicLibrary : IntrinsicLibrary {
+
+ explicit OpenACCIntrinsicLibrary(fir::FirOpBuilder &builder,
+ mlir::Location loc)
+ : IntrinsicLibrary(builder, loc) {}
+ OpenACCIntrinsicLibrary() = delete;
+ OpenACCIntrinsicLibrary(const OpenACCIntrinsicLibrary &) = delete;
+
+ mlir::Value genACCOnDevice(mlir::Type, llvm::ArrayRef<mlir::Value>);
+};
+
+const IntrinsicHandler *findOpenACCIntrinsicHandler(llvm::StringRef name,
+ bool isBindcCall = false);
+
+} // namespace fir
+
+#endif // FORTRAN_LOWER_OPENACCINTRINSICCALL_H
diff --git a/flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h b/flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h
index 5ae32f70a11a7..75d69c89636ea 100644
--- a/flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/PPCIntrinsicCall.h
@@ -320,7 +320,8 @@ struct PPCIntrinsicLibrary : IntrinsicLibrary {
llvm::ArrayRef<fir::ExtendedValue> args);
};
-const IntrinsicHandler *findPPCIntrinsicHandler(llvm::StringRef name);
+const IntrinsicHandler *findPPCIntrinsicHandler(llvm::StringRef name,
+ bool isBindcCall = false);
std::pair<const MathOperation *, const MathOperation *>
checkPPCMathOperationsRange(llvm::StringRef name);
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 4e862e8ece18a..92547cd982288 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -3135,16 +3135,17 @@ genProcedureRef(CallContext &callContext) {
fir::FirOpBuilder &builder = callContext.getBuilder();
if (auto *intrinsic = callContext.procRef.proc().GetSpecificIntrinsic())
return genIntrinsicRef(intrinsic, callContext);
- // Intercept non BIND(C) module procedure reference that have lowering
- // handlers defined for there name. Otherwise, lower them as user
- // procedure calls and expect the implementation to be part of
- // runtime libraries with the proper name mangling.
- if (Fortran::lower::isIntrinsicModuleProcRef(callContext.procRef) &&
- !callContext.isBindcCall())
+ // Intercept module procedure references that have lowering handlers defined
+ // for their name. Otherwise, lower them as user procedure calls and expect
+ // the implementation to be part of runtime libraries with the proper name
+ // mangling.
+ if (Fortran::lower::isIntrinsicModuleProcRef(callContext.procRef)) {
+ const bool isBindcCall = callContext.isBindcCall();
if (std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =
fir::lookupIntrinsicHandler(builder, callContext.getProcedureName(),
- callContext.resultType))
+ callContext.resultType, isBindcCall))
return genIntrinsicRef(nullptr, *intrinsicEntry, callContext);
+ }
if (callContext.isStatementFunctionCall())
return genStmtFunctionRef(loc, callContext.converter, callContext.symMap,
diff --git a/flang/lib/Optimizer/Builder/CMakeLists.txt b/flang/lib/Optimizer/Builder/CMakeLists.txt
index c82ad8e9160f4..6a710977a5e02 100644
--- a/flang/lib/Optimizer/Builder/CMakeLists.txt
+++ b/flang/lib/Optimizer/Builder/CMakeLists.txt
@@ -14,6 +14,7 @@ add_flang_library(FIRBuilder
LowLevelIntrinsics.cpp
MIFCommon.cpp
MutableBox.cpp
+ OpenACCIntrinsicCall.cpp
PPCIntrinsicCall.cpp
Runtime/Allocatable.cpp
Runtime/ArrayConstructor.cpp
diff --git a/flang/lib/Optimizer/Builder/CUDAIntrinsicCall.cpp b/flang/lib/Optimizer/Builder/CUDAIntrinsicCall.cpp
index 0e0cb4cd5bc19..61c5e8131216e 100644
--- a/flang/lib/Optimizer/Builder/CUDAIntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/CUDAIntrinsicCall.cpp
@@ -640,22 +640,12 @@ static constexpr IntrinsicHandler cudaHandlers[]{
{{}},
/*isElemental=*/false},
};
+static_assert(fir::isSorted(cudaHandlers) && "map must be sorted");
-template <std::size_t N>
-static constexpr bool isSorted(const IntrinsicHandler (&array)[N]) {
- // Replace by std::sorted when C++20 is default (will be constexpr).
- const IntrinsicHandler *lastSeen{nullptr};
- bool isSorted{true};
- for (const auto &x : array) {
- if (lastSeen)
- isSorted &= std::string_view{lastSeen->name} < std::string_view{x.name};
- lastSeen = &x;
- }
- return isSorted;
-}
-static_assert(isSorted(cudaHandlers) && "map must be sorted");
-
-const IntrinsicHandler *findCUDAIntrinsicHandler(llvm::StringRef name) {
+const IntrinsicHandler *findCUDAIntrinsicHandler(llvm::StringRef name,
+ bool isBindcCall) {
+ if (isBindcCall)
+ return nullptr;
auto compare = [](const IntrinsicHandler &cudaHandler, llvm::StringRef name) {
return name.compare(cudaHandler.name) > 0;
};
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index a2abcba0b81f2..bff561064bc70 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -22,6 +22,7 @@
#include "flang/Optimizer/Builder/Complex.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/MutableBox.h"
+#include "flang/Optimizer/Builder/OpenACCIntrinsicCall.h"
#include "flang/Optimizer/Builder/PPCIntrinsicCall.h"
#include "flang/Optimizer/Builder/Runtime/Allocatable.h"
#include "flang/Optimizer/Builder/Runtime/CUDA/Descriptor.h"
@@ -881,22 +882,12 @@ static constexpr IntrinsicHandler handlers[]{
{"kind", asValue}}},
/*isElemental=*/true},
};
+static_assert(fir::isSorted(handlers) && "map must be sorted");
-template <std::size_t N>
-static constexpr bool isSorted(const IntrinsicHandler (&array)[N]) {
- // Replace by std::sorted when C++20 is default (will be constexpr).
- const IntrinsicHandler *lastSeen{nullptr};
- bool isSorted{true};
- for (const auto &x : array) {
- if (lastSeen)
- isSorted &= std::string_view{lastSeen->name} < std::string_view{x.name};
- lastSeen = &x;
- }
- return isSorted;
-}
-static_assert(isSorted(handlers) && "map must be sorted");
-
-static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
+static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name,
+ bool isBindcCall = false) {
+ if (isBindcCall)
+ return nullptr;
auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) {
return name.compare(handler.name) > 0;
};
@@ -1931,21 +1922,30 @@ lookupRuntimeGenerator(llvm::StringRef name, bool isPPCTarget) {
std::optional<IntrinsicHandlerEntry>
lookupIntrinsicHandler(fir::FirOpBuilder &builder,
llvm::StringRef intrinsicName,
- std::optional<mlir::Type> resultType) {
+ std::optional<mlir::Type> resultType, bool isBindcCall) {
llvm::StringRef name = genericName(intrinsicName);
- if (const IntrinsicHandler *handler = findIntrinsicHandler(name))
+ if (const IntrinsicHandler *handler = findIntrinsicHandler(name, isBindcCall))
return std::make_optional<IntrinsicHandlerEntry>(handler);
bool isPPCTarget = fir::getTargetTriple(builder.getModule()).isPPC();
// If targeting PowerPC, check PPC intrinsic handlers.
if (isPPCTarget)
- if (const IntrinsicHandler *ppcHandler = findPPCIntrinsicHandler(name))
+ if (const IntrinsicHandler *ppcHandler =
+ findPPCIntrinsicHandler(name, isBindcCall))
return std::make_optional<IntrinsicHandlerEntry>(ppcHandler);
// TODO: Look for CUDA intrinsic handlers only if CUDA is enabled.
- if (const IntrinsicHandler *cudaHandler = findCUDAIntrinsicHandler(name))
+ if (const IntrinsicHandler *cudaHandler =
+ findCUDAIntrinsicHandler(name, isBindcCall))
return std::make_optional<IntrinsicHandlerEntry>(cudaHandler);
+ // TODO: Look for OpenACC intrinsic handlers only if OpenACC is enabled.
+ if (const IntrinsicHandler *openaccHandler =
+ findOpenACCIntrinsicHandler(name, isBindcCall))
+ return std::make_optional<IntrinsicHandlerEntry>(openaccHandler);
// Subroutines should have a handler.
if (!resultType)
return std::nullopt;
+ // BIND(C) intrinsic module procedures must not fall back to runtime lookup.
+ if (isBindcCall)
+ return std::nullopt;
// Try the runtime if no special handler was defined for the
// intrinsic being called. Maths runtime only has numerical elemental.
if (auto runtimeGeneratorRange = lookupRuntimeGenerator(name, isPPCTarget))
@@ -9381,6 +9381,10 @@ getIntrinsicArgumentLowering(llvm::StringRef specificName) {
if (const IntrinsicHandler *cudaHandler = findCUDAIntrinsicHandler(name))
if (!cudaHandler->argLoweringRules.hasDefaultRules())
return &cudaHandler->argLoweringRules;
+ if (const IntrinsicHandler *openaccHandler =
+ findOpenACCIntrinsicHandler(name))
+ if (!openaccHandler->argLoweringRules.hasDefaultRules())
+ return &openaccHandler->argLoweringRules;
return nullptr;
}
diff --git a/flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp b/flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp
new file mode 100644
index 0000000000000..684eda58652ed
--- /dev/null
+++ b/flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp
@@ -0,0 +1,51 @@
+//===-- OpenACCIntrinsicCall.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/OpenACCIntrinsicCall.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+
+namespace fir {
+
+using OAI = OpenACCIntrinsicLibrary;
+
+static constexpr IntrinsicHandler openaccHandlers[]{
+ {"acc_on_device",
+ static_cast<OpenACCIntrinsicLibrary::ElementalGenerator>(
+ &OAI::genACCOnDevice),
+ {{{"devtype", asValue}}},
+ /*isElemental=*/false},
+};
+static_assert(fir::isSorted(openaccHandlers) && "map must be sorted");
+
+const IntrinsicHandler *findOpenACCIntrinsicHandler(llvm::StringRef name,
+ bool isBindcCall) {
+ if (!isBindcCall)
+ return nullptr;
+ auto compare = [](const IntrinsicHandler &openaccHandler,
+ llvm::StringRef name) {
+ return name.compare(openaccHandler.name) > 0;
+ };
+ auto result = llvm::lower_bound(openaccHandlers, name, compare);
+ return result != std::end(openaccHandlers) && result->name == name ? result
+ : nullptr;
+}
+
+mlir::Value
+OpenACCIntrinsicLibrary::genACCOnDevice(mlir::Type resultType,
+ llvm::ArrayRef<mlir::Value> args) {
+ assert(args.size() == 1);
+ mlir::Value deviceType = args[0];
+ if (deviceType.getType() != builder.getI32Type())
+ deviceType = builder.createConvert(loc, builder.getI32Type(), deviceType);
+ mlir::Value onDevice =
+ mlir::acc::OnDeviceOp::create(builder, loc, deviceType).getResult();
+ return builder.createConvert(loc, resultType, onDevice);
+}
+
+} // namespace fir
diff --git a/flang/lib/Optimizer/Builder/PPCIntrinsicCall.cpp b/flang/lib/Optimizer/Builder/PPCIntrinsicCall.cpp
index c17ca6112218b..cf8c7ce4f8d4e 100644
--- a/flang/lib/Optimizer/Builder/PPCIntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/PPCIntrinsicCall.cpp
@@ -777,6 +777,7 @@ static constexpr IntrinsicHandler ppcHandlers[]{
{{{"arg1", asValue}, {"arg2", asValue}, {"arg3", asAddr}}},
/*isElemental=*/false},
};
+static_assert(fir::isSorted(ppcHandlers) && "map must be sorted");
static constexpr MathOperation ppcMathOperations[] = {
// fcfi is just another name for fcfid, there is no llvm.ppc.fcfi.
@@ -932,7 +933,10 @@ static constexpr MathOperation ppcMathOperations[] = {
genLibCall},
};
-const IntrinsicHandler *findPPCIntrinsicHandler(llvm::StringRef name) {
+const IntrinsicHandler *findPPCIntrinsicHandler(llvm::StringRef name,
+ bool isBindcCall) {
+ if (isBindcCall)
+ return nullptr;
auto compare = [](const IntrinsicHandler &ppcHandler, llvm::StringRef name) {
return name.compare(ppcHandler.name) > 0;
};
diff --git a/flang/test/Lower/OpenACC/acc-on-device.f90 b/flang/test/Lower/OpenACC/acc-on-device.f90
new file mode 100644
index 0000000000000..b41959496d0af
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-on-device.f90
@@ -0,0 +1,14 @@
+! This test checks lowering of the OpenACC acc_on_device intrinsic.
+
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
+
+subroutine acc_on_device_test
+ use openacc
+ implicit none
+ logical :: on_host
+
+ on_host = acc_on_device(acc_device_host)
+!CHECK: [[HOST:%.*]] = fir.load %{{.*}} : !fir.ref<i32>
+!CHECK: acc.on_device [[HOST]] : i32 -> i1
+!CHECK-NOT: fir.call @acc_on_device
+end subroutine acc_on_device_test
>From 5824ebcf030b00846e56c244abc60d9589e33c02 Mon Sep 17 00:00:00 2001
From: Ivan Radanov Ivanov <iivanov at nvidia.com>
Date: Wed, 8 Jul 2026 07:56:12 -0700
Subject: [PATCH 2/2] simplify builder
---
flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp b/flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp
index 684eda58652ed..7e023b0ae419a 100644
--- a/flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/OpenACCIntrinsicCall.cpp
@@ -40,11 +40,8 @@ mlir::Value
OpenACCIntrinsicLibrary::genACCOnDevice(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args) {
assert(args.size() == 1);
- mlir::Value deviceType = args[0];
- if (deviceType.getType() != builder.getI32Type())
- deviceType = builder.createConvert(loc, builder.getI32Type(), deviceType);
mlir::Value onDevice =
- mlir::acc::OnDeviceOp::create(builder, loc, deviceType).getResult();
+ mlir::acc::OnDeviceOp::create(builder, loc, args[0]).getResult();
return builder.createConvert(loc, resultType, onDevice);
}
More information about the llvm-branch-commits
mailing list