[flang-commits] [flang] [flang][cuda] Skip device-only specifics at host call sites in generic resolution (PR #234710)
Valentin Clement バレンタイン クレメン via flang-commits
flang-commits at lists.llvm.org
Thu Oct 22 11:51:09 PDT 2026
https://github.com/clementval updated https://github.com/llvm/llvm-project/pull/234710
>From 7ba61a696417bd6831cfc71f52c42aba8b1a1365 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Thu, 22 Oct 2026 10:29:36 -0700
Subject: [PATCH 1/4] [flang][cuda] Skip device-only specifics at host call
sites in generic resolution
---
flang/lib/Semantics/expression.cpp | 26 +++++++++++++++
...cuda-generic-host-call-device-specific.cuf | 33 +++++++++++++++++++
2 files changed, 59 insertions(+)
create mode 100644 flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index ec7a6646fd9939..644787122ad73e 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3305,6 +3305,15 @@ static bool IsCUDADeviceCallable(const Symbol &symbol) {
*attrs == common::CUDASubprogramAttrs::HostDevice);
}
+// A specific with only the DEVICE attribute cannot be referenced from host
+// code, whatever its dummy arguments' CUDA data attributes score.
+static bool IsCUDADeviceOnly(const Symbol &symbol) {
+ const auto *subprogram{
+ symbol.GetUltimate().detailsIf<semantics::SubprogramDetails>()};
+ auto attrs{subprogram ? subprogram->cudaSubprogramAttrs() : std::nullopt};
+ return attrs && *attrs == common::CUDASubprogramAttrs::Device;
+}
+
static bool IsCudaDeviceIntrinsicShadowedByHostProcedure(
const parser::CharBlock &callSite, semantics::SemanticsContext &context,
const Symbol *resolution, bool isSubroutine) {
@@ -3343,6 +3352,20 @@ auto ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
const auto *genericDetails{ultimate.detailsIf<semantics::GenericDetails>()};
if (genericDetails) {
std::optional<CudaMatchingDistance> crtMatchingDistance;
+ // CUF kernel and OpenACC regions share Scope::Kind::OpenACCConstruct and
+ // may hold device code in a host subprogram; leave those unfiltered.
+ bool inHostCode{
+ context_.languageFeatures().IsEnabled(common::LanguageFeature::CUDA) &&
+ !GetContextualMessages().at().empty()};
+ if (inHostCode) {
+ const semantics::Scope &scope{
+ context_.FindScope(GetContextualMessages().at())};
+ inHostCode = !semantics::FindCUDADeviceContext(&scope);
+ for (const semantics::Scope *s{&scope}; inHostCode && !s->IsGlobal();
+ s = &s->parent()) {
+ inHostCode = s->kind() != semantics::Scope::Kind::OpenACCConstruct;
+ }
+ }
for (const Symbol &specific0 : genericDetails->specificProcs()) {
const Symbol &specific1{BypassGeneric(specific0)};
if (isSubroutine != !IsFunction(specific1)) {
@@ -3352,6 +3375,9 @@ auto ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
if (!specific) {
continue;
}
+ if (inHostCode && IsCUDADeviceOnly(*specific)) {
+ continue;
+ }
if (std::optional<characteristics::Procedure> procedure{
characteristics::Procedure::Characterize(
ProcedureDesignator{*specific}, context_.foldingContext(),
diff --git a/flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf b/flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf
new file mode 100644
index 00000000000000..4ffba989ddfa99
--- /dev/null
+++ b/flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf
@@ -0,0 +1,33 @@
+! RUN: bbc -emit-hlfir -fcuda %s -o - | FileCheck %s
+
+! A host call site must not resolve to a device-only specific, even when
+! managed actuals score the DEVICE dummies as a closer match.
+
+module m
+ interface sg
+ module procedure s_host
+ module procedure s_dev
+ end interface
+ real(8), managed, allocatable :: a(:), y(:)
+contains
+ subroutine s_host(x, y)
+ real(8), intent(in) :: x
+ real(8), intent(out) :: y
+ y = 2.0d0*x
+ end subroutine
+
+ attributes(device) subroutine s_dev(x, y)
+ real(8), device :: x
+ real(8), device :: y
+ y = x*x
+ end subroutine
+
+ subroutine host_caller(i)
+ integer :: i
+ call sg(a(i), y(i))
+ end subroutine
+end module
+
+! CHECK-LABEL: func.func @_QMmPhost_caller
+! CHECK: fir.call @_QMmPs_host
+! CHECK-NOT: fir.call @_QMmPs_dev
>From 0b2c362ddbabb357cf20f391f44cab6b0380ecbe Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Thu, 22 Oct 2026 11:13:30 -0700
Subject: [PATCH 2/4] Add test for openacc routine
---
flang/lib/Semantics/expression.cpp | 13 ++++++---
...cuda-generic-host-call-device-specific.cuf | 27 +++++++++++++++++--
2 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 644787122ad73e..d780114892ef28 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3308,8 +3308,13 @@ static bool IsCUDADeviceCallable(const Symbol &symbol) {
// A specific with only the DEVICE attribute cannot be referenced from host
// code, whatever its dummy arguments' CUDA data attributes score.
static bool IsCUDADeviceOnly(const Symbol &symbol) {
- const auto *subprogram{
- symbol.GetUltimate().detailsIf<semantics::SubprogramDetails>()};
+ const Symbol *ultimate{&symbol.GetUltimate()};
+ // Type-bound specifics: inspect the bound procedure.
+ if (const auto *binding{
+ ultimate->detailsIf<semantics::ProcBindingDetails>()}) {
+ ultimate = &binding->symbol().GetUltimate();
+ }
+ const auto *subprogram{ultimate->detailsIf<semantics::SubprogramDetails>()};
auto attrs{subprogram ? subprogram->cudaSubprogramAttrs() : std::nullopt};
return attrs && *attrs == common::CUDASubprogramAttrs::Device;
}
@@ -3360,7 +3365,9 @@ auto ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
if (inHostCode) {
const semantics::Scope &scope{
context_.FindScope(GetContextualMessages().at())};
- inHostCode = !semantics::FindCUDADeviceContext(&scope);
+ // An OpenACC routine body may run on the device.
+ inHostCode = !semantics::FindCUDADeviceContext(&scope) &&
+ !semantics::HasOpenACCRoutineDirective(&scope);
for (const semantics::Scope *s{&scope}; inHostCode && !s->IsGlobal();
s = &s->parent()) {
inHostCode = s->kind() != semantics::Scope::Kind::OpenACCConstruct;
diff --git a/flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf b/flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf
index 4ffba989ddfa99..c9eae39e610304 100644
--- a/flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf
+++ b/flang/test/Lower/CUDA/cuda-generic-host-call-device-specific.cuf
@@ -1,4 +1,4 @@
-! RUN: bbc -emit-hlfir -fcuda %s -o - | FileCheck %s
+! RUN: bbc -emit-hlfir -fcuda -fopenacc %s -o - | FileCheck %s
! A host call site must not resolve to a device-only specific, even when
! managed actuals score the DEVICE dummies as a closer match.
@@ -9,6 +9,12 @@ module m
module procedure s_dev
end interface
real(8), managed, allocatable :: a(:), y(:)
+ type t
+ contains
+ procedure, nopass :: tb_host => s_host
+ procedure, nopass :: tb_dev => s_dev
+ generic :: tsg => tb_host, tb_dev
+ end type
contains
subroutine s_host(x, y)
real(8), intent(in) :: x
@@ -22,12 +28,29 @@ contains
y = x*x
end subroutine
+ subroutine acc_caller(i)
+ !$acc routine seq nohost
+ integer :: i
+ call sg(a(i), y(i))
+ end subroutine
+
+ subroutine tb_caller(i)
+ integer :: i
+ type(t) :: obj
+ call obj%tsg(a(i), y(i))
+ end subroutine
+
subroutine host_caller(i)
integer :: i
call sg(a(i), y(i))
end subroutine
end module
+! CHECK-LABEL: func.func @_QMmPacc_caller
+! CHECK: fir.call @_QMmPs_dev
+
+! CHECK-LABEL: func.func @_QMmPtb_caller
+! CHECK: fir.call @_QMmPs_host
+
! CHECK-LABEL: func.func @_QMmPhost_caller
! CHECK: fir.call @_QMmPs_host
-! CHECK-NOT: fir.call @_QMmPs_dev
>From 47e1b30cbef1df48cb7b1c30a3e5aa363021f01a Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Thu, 22 Oct 2026 11:35:31 -0700
Subject: [PATCH 3/4] add test
---
flang/lib/Semantics/expression.cpp | 41 ++++++++++++++++++++----------
1 file changed, 27 insertions(+), 14 deletions(-)
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index d780114892ef28..be85a02ad46da7 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3319,6 +3319,31 @@ static bool IsCUDADeviceOnly(const Symbol &symbol) {
return attrs && *attrs == common::CUDASubprogramAttrs::Device;
}
+// True when code in this scope may run on the device. FindCUDADeviceContext
+// reads the scope symbol's own CUDA attributes, which a separate module
+// procedure implementation does not carry: its interface does.
+static bool MayRunOnCUDADevice(const semantics::Scope &scope) {
+ if (semantics::FindCUDADeviceContext(&scope) ||
+ semantics::HasOpenACCRoutineDirective(&scope)) {
+ return true;
+ }
+ for (const semantics::Scope *s{&scope}; !s->IsGlobal(); s = &s->parent()) {
+ // CUF kernel and OpenACC regions share Scope::Kind::OpenACCConstruct and
+ // may hold device code in a host subprogram.
+ if (s->kind() == semantics::Scope::Kind::OpenACCConstruct) {
+ return true;
+ }
+ if (const Symbol *symbol{s->symbol()}) {
+ const auto *subp{symbol->detailsIf<semantics::SubprogramDetails>()};
+ if (subp && subp->moduleInterface() &&
+ IsCUDADeviceCallable(*subp->moduleInterface())) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
static bool IsCudaDeviceIntrinsicShadowedByHostProcedure(
const parser::CharBlock &callSite, semantics::SemanticsContext &context,
const Symbol *resolution, bool isSubroutine) {
@@ -3357,22 +3382,10 @@ auto ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
const auto *genericDetails{ultimate.detailsIf<semantics::GenericDetails>()};
if (genericDetails) {
std::optional<CudaMatchingDistance> crtMatchingDistance;
- // CUF kernel and OpenACC regions share Scope::Kind::OpenACCConstruct and
- // may hold device code in a host subprogram; leave those unfiltered.
bool inHostCode{
context_.languageFeatures().IsEnabled(common::LanguageFeature::CUDA) &&
- !GetContextualMessages().at().empty()};
- if (inHostCode) {
- const semantics::Scope &scope{
- context_.FindScope(GetContextualMessages().at())};
- // An OpenACC routine body may run on the device.
- inHostCode = !semantics::FindCUDADeviceContext(&scope) &&
- !semantics::HasOpenACCRoutineDirective(&scope);
- for (const semantics::Scope *s{&scope}; inHostCode && !s->IsGlobal();
- s = &s->parent()) {
- inHostCode = s->kind() != semantics::Scope::Kind::OpenACCConstruct;
- }
- }
+ !GetContextualMessages().at().empty() &&
+ !MayRunOnCUDADevice(context_.FindScope(GetContextualMessages().at()))};
for (const Symbol &specific0 : genericDetails->specificProcs()) {
const Symbol &specific1{BypassGeneric(specific0)};
if (isSubroutine != !IsFunction(specific1)) {
>From 6954e255b1d8702438fffabd7fa75d128f27fd2c Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Thu, 22 Oct 2026 11:50:41 -0700
Subject: [PATCH 4/4] Add global grid_global
---
flang/lib/Semantics/expression.cpp | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index be85a02ad46da7..9673dda9de102d 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3335,9 +3335,16 @@ static bool MayRunOnCUDADevice(const semantics::Scope &scope) {
}
if (const Symbol *symbol{s->symbol()}) {
const auto *subp{symbol->detailsIf<semantics::SubprogramDetails>()};
- if (subp && subp->moduleInterface() &&
- IsCUDADeviceCallable(*subp->moduleInterface())) {
- return true;
+ if (subp && subp->moduleInterface()) {
+ // Kernels (GLOBAL, GRID_GLOBAL) run device code too, so any attribute
+ // other than HOST makes this a device context.
+ const auto *interface{subp->moduleInterface()
+ ->GetUltimate()
+ .detailsIf<semantics::SubprogramDetails>()};
+ auto attrs{interface ? interface->cudaSubprogramAttrs() : std::nullopt};
+ if (attrs && *attrs != common::CUDASubprogramAttrs::Host) {
+ return true;
+ }
}
}
}
More information about the flang-commits
mailing list