[flang-commits] [flang] [flang][acc] Recover acc routine bind info in CUDA Fortran module reads (PR #223595)
Valentin Clement バレンタイン クレメン via flang-commits
flang-commits at lists.llvm.org
Mon Sep 14 20:45:58 PDT 2026
https://github.com/clementval created https://github.com/llvm/llvm-project/pull/223595
CUDA Fortran device code can call a procedure whose device-side symbol
is given by `acc routine bind(...)` in the module that declares it.
The using translation unit is often compiled with CUDA Fortran enabled
and without an OpenACC target, so the `$acc` sentinel was never
recognized when re-parsing the module file and the bind clause was
dropped.
Enable OpenACC while reading module files under CUDA Fortran as well,
and resolve the recovered directives onto the imported symbols. User
`$acc` in the main source remains ignored without `-fopenacc`; only
directives that already survived into the `.mod` are honored.
A bind(C) host name and an acc bind device name on the same procedure
is the dual-name rule in OpenACC §2.15.1.
>From 7cc2d389b7a026e5b4fd075a40ba213ffb09a14b Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Mon, 14 Sep 2026 20:43:57 -0700
Subject: [PATCH] [flang][acc] Recover acc routine bind info in CUDA Fortran
module reads
---
flang/lib/Semantics/mod-file.cpp | 7 ++-
flang/lib/Semantics/resolve-directives.cpp | 8 +++-
.../OpenACC/acc-routine-bind-cuda-modfile.cuf | 45 +++++++++++++++++++
3 files changed, 58 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Lower/OpenACC/acc-routine-bind-cuda-modfile.cuf
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index edbca4a9015aa..e17bfb2ee3f32 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -1754,7 +1754,12 @@ Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
parser::Options options;
options.isModuleFile = true;
options.features.Enable(common::LanguageFeature::BackslashEscapes);
- if (context_.languageFeatures().IsEnabled(common::LanguageFeature::OpenACC)) {
+ // CUDA Fortran device code can call procedures whose device-side call target
+ // is described by an `acc routine bind(...)` directive in the module that
+ // declares them, so the sentinel must be recognized here even when this
+ // compilation itself was not given an OpenACC target.
+ if (context_.languageFeatures().IsEnabled(common::LanguageFeature::OpenACC) ||
+ context_.languageFeatures().IsEnabled(common::LanguageFeature::CUDA)) {
options.features.Enable(common::LanguageFeature::OpenACC);
}
options.features.Enable(common::LanguageFeature::OpenMP);
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 143fe385768d7..f7a9e252d0d49 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -1058,7 +1058,13 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
void ResolveAccParts(SemanticsContext &context, const parser::ProgramUnit &node,
Scope *topScope) {
- if (context.IsEnabled(common::LanguageFeature::OpenACC)) {
+ // A CUDA Fortran compilation that was not given an OpenACC target still has
+ // to resolve the directives recovered from module files, because they can
+ // describe the device-side call target of a procedure through
+ // `acc routine bind(...)`. The sentinel is not recognized in the main source
+ // in that case, so no user directive can reach this point.
+ if (context.IsEnabled(common::LanguageFeature::OpenACC) ||
+ context.IsEnabled(common::LanguageFeature::CUDA)) {
AccAttributeVisitor{context, topScope}.Walk(node);
}
}
diff --git a/flang/test/Lower/OpenACC/acc-routine-bind-cuda-modfile.cuf b/flang/test/Lower/OpenACC/acc-routine-bind-cuda-modfile.cuf
new file mode 100644
index 0000000000000..87adadcefcdf8
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-routine-bind-cuda-modfile.cuf
@@ -0,0 +1,45 @@
+! CUDA Fortran can consume `acc routine bind(...)` recovered from a module
+! file even when this translation unit is compiled without -fopenacc.
+
+! RUN: split-file %s %t
+! RUN: bbc -fopenacc -fcuda -emit-hlfir %t/mod.cuf -o %t/mod.mlir --module=%t
+! RUN: bbc -fcuda -emit-hlfir %t/use.cuf -o - -I %t | FileCheck %s
+
+//--- mod.cuf
+module m
+ interface
+ attributes(host,device) function my_device_func(x) bind(c) result(res)
+ integer(4), value :: x
+ integer(4) :: res
+ end function
+ end interface
+
+ interface
+ attributes(host,device) subroutine my_device_sub(x, res) bind(c)
+ integer(4), value :: x
+ integer(4) :: res
+ end subroutine
+ end interface
+
+ !$acc routine(my_device_func) seq bind('__wrapper_my_device_func')
+ !$acc routine(my_device_sub) seq bind('__wrapper_my_device_sub')
+end module
+
+//--- use.cuf
+attributes(device) subroutine test
+ use m
+ implicit none
+ integer(4) :: a, b
+
+ b = my_device_func(a)
+ call my_device_sub(a, b)
+end subroutine test
+
+! CHECK-DAG: acc.routine @[[ROUT_FUNC:.*]] func(@my_device_func) bind("__wrapper_my_device_func") seq
+! CHECK-DAG: acc.routine @[[ROUT_SUB:.*]] func(@my_device_sub) bind("__wrapper_my_device_sub") seq
+! CHECK-LABEL: func.func @_QPtest()
+! CHECK-SAME: attributes {cuf.proc_attr = #cuf.cuda_proc<device>}
+! CHECK: fir.call @my_device_func(
+! CHECK: fir.call @my_device_sub(
+! CHECK: func.func private @my_device_func{{.*}}acc.routine_info = #acc.routine_info<[@[[ROUT_FUNC]]]>
+! CHECK: func.func private @my_device_sub{{.*}}acc.routine_info = #acc.routine_info<[@[[ROUT_SUB]]]>
More information about the flang-commits
mailing list