[flang-commits] [flang] [flang][cuda] Implicitly attribute allocatables/pointers under -gpu=unified (PR #209292)
Vijay Kandiah via flang-commits
flang-commits at lists.llvm.org
Mon Jul 13 17:52:11 PDT 2026
https://github.com/VijayKandiah updated https://github.com/llvm/llvm-project/pull/209292
>From 0d922239d81054c502a221affa3a751d6ddb6679 Mon Sep 17 00:00:00 2001
From: Vijay Kandiah <vkandiah at nvidia.com>
Date: Mon, 13 Jul 2026 13:05:19 -0700
Subject: [PATCH 1/2] [flang][cuda] Implicitly attribute allocatables/pointers
under -gpu=unified
---
flang/lib/Semantics/resolve-names.cpp | 35 +++++++++++-----
.../CUDA/cuda-implicit-managed-alloc.cuf | 41 +++++++++++++++++++
2 files changed, 66 insertions(+), 10 deletions(-)
create mode 100644 flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 996c05d0e193b..5dfcc2c622453 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -10630,23 +10630,38 @@ void ResolveNamesVisitor::FinishSpecificationPart(
if (auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
if ((IsAllocatable(symbol) || IsPointer(symbol)) &&
!object->cudaDataAttr()) {
+ const bool cudaEnabled{context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CUDA)};
+ const bool cudaManaged{context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CudaManaged)};
+ const bool cudaUnified{context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CudaUnified)};
// Implicitly treat allocatable/pointer arrays as managed when feature
// is enabled. This is done after all explicit CUDA attributes have
// been processed. Only applies when CUDA Fortran is enabled; otherwise
// -gpu=mem:managed on a non-CUDA-Fortran translation unit (e.g. pure
// OpenACC) would incorrectly route every allocatable through the CUDA
- // Fortran managed descriptor pipeline.
- if (context().languageFeatures().IsEnabled(
- common::LanguageFeature::CudaManaged) &&
+ // Fortran managed descriptor pipeline. Under -gpu=mem:unified prefer
+ // the Unified attribute where it is legal (host subprogram, main
+ // program, or component) so generic resolution still selects the
+ // unified specific; fall back to Managed elsewhere (module scope,
+ // device subprograms), which uses the same allocator.
+ if (cudaEnabled && (cudaManaged || cudaUnified)) {
+ const Scope &owner{symbol.owner()};
+ const bool unifiedAllowed{!IsCUDADeviceContext(&owner) &&
+ (owner.IsDerivedType() ||
+ owner.kind() == Scope::Kind::MainProgram ||
+ owner.kind() == Scope::Kind::Subprogram)};
+ object->set_cudaDataAttr(cudaUnified && unifiedAllowed
+ ? common::CUDADataAttr::Unified
+ : common::CUDADataAttr::Managed);
+ // Implicitly treat allocatable arrays as pinned when feature is
+ // enabled.
+ } else if (IsAllocatable(symbol) &&
context().languageFeatures().IsEnabled(
- common::LanguageFeature::CUDA))
- object->set_cudaDataAttr(common::CUDADataAttr::Managed);
- // Implicitly treat allocatable arrays as pinned when feature is
- // enabled.
- else if (IsAllocatable(symbol) &&
- context().languageFeatures().IsEnabled(
- common::LanguageFeature::CudaPinned))
+ common::LanguageFeature::CudaPinned)) {
object->set_cudaDataAttr(common::CUDADataAttr::Pinned);
+ }
}
}
}
diff --git a/flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf b/flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf
new file mode 100644
index 0000000000000..4e052ad839970
--- /dev/null
+++ b/flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf
@@ -0,0 +1,41 @@
+! RUN: bbc -emit-hlfir -fcuda -gpu=unified %s -o - | FileCheck %s --check-prefix=UNI
+! RUN: bbc -emit-hlfir -fcuda -gpu=managed %s -o - | FileCheck %s --check-prefix=MAN
+
+! Under -gpu=unified and -gpu=managed, allocatables and pointers with no
+! explicit CUDA data attribute are implicitly attributed. -gpu=unified prefers
+! the Unified attribute where ATTRIBUTES(UNIFIED) is legal (host subprogram /
+! main program); module-scope objects fall back to Managed, which carries no
+! such scope restriction.
+
+module m
+ real, allocatable :: mod_arr(:)
+ real, pointer :: mod_ptr(:)
+contains
+ subroutine mod_sub()
+ real, allocatable :: loc_arr(:)
+ real, pointer :: loc_ptr(:)
+ end subroutine
+end module
+
+program p
+ real, allocatable :: prog_arr(:)
+end program
+
+! Host-subprogram locals: Unified under -gpu=unified, Managed under
+! -gpu=managed.
+! UNI: hlfir.declare {{.*}} {data_attr = #cuf.cuda<unified>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QMmFmod_subEloc_arr"}
+! UNI: hlfir.declare {{.*}} {data_attr = #cuf.cuda<unified>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QMmFmod_subEloc_ptr"}
+! MAN: hlfir.declare {{.*}} {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QMmFmod_subEloc_arr"}
+! MAN: hlfir.declare {{.*}} {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QMmFmod_subEloc_ptr"}
+
+! Main-program local: Unified under -gpu=unified, Managed under
+! -gpu=managed.
+! UNI: hlfir.declare {{.*}} {data_attr = #cuf.cuda<unified>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEprog_arr"}
+! MAN: hlfir.declare {{.*}} {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEprog_arr"}
+
+! Module-scope globals: Managed in both modes (ATTRIBUTES(UNIFIED) is not legal
+! at module scope, so the Unified case falls back to Managed).
+! UNI: fir.global @_QMmEmod_arr {data_attr = #cuf.cuda<managed>}
+! UNI: fir.global @_QMmEmod_ptr {data_attr = #cuf.cuda<managed>}
+! MAN: fir.global @_QMmEmod_arr {data_attr = #cuf.cuda<managed>}
+! MAN: fir.global @_QMmEmod_ptr {data_attr = #cuf.cuda<managed>}
>From f28e21e412d8ae11dbe69720d38a86d029d3f342 Mon Sep 17 00:00:00 2001
From: Vijay Kandiah <vkandiah at nvidia.com>
Date: Mon, 13 Jul 2026 17:51:59 -0700
Subject: [PATCH 2/2] [flang][cuda] Extend implicit -gpu=unified attr to
derived-type components
---
flang/lib/Semantics/resolve-names.cpp | 88 +++++++++++--------
.../CUDA/cuda-implicit-managed-alloc.cuf | 19 +++-
2 files changed, 67 insertions(+), 40 deletions(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 5dfcc2c622453..279ce2546087c 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1124,6 +1124,7 @@ class DeclarationVisitor : public ArraySpecVisitor,
protected:
bool BeginDecl();
void EndDecl();
+ void SetImplicitCUDADataAttr(Symbol &);
Symbol &DeclareObjectEntity(const parser::Name &, Attrs = Attrs{});
// Make sure that there's an entity in an enclosing scope called Name
Symbol &FindOrDeclareEnclosingEntity(const parser::Name &);
@@ -7125,6 +7126,11 @@ bool DeclarationVisitor::Pre(const parser::DerivedTypeDef &x) {
const auto &componentDefs{
std::get<std::list<parser::Statement<parser::ComponentDefStmt>>>(x.t)};
Walk(componentDefs);
+ // Components live in the derived type's own scope, which is not visited when
+ // the enclosing specification part is finished, so attribute them here.
+ for (auto &pair : scope) {
+ SetImplicitCUDADataAttr(*pair.second);
+ }
if (derivedTypeInfo_.sequence) {
details.set_sequence(true);
if (componentDefs.empty()) {
@@ -10584,6 +10590,50 @@ void ResolveNamesVisitor::CreateGeneric(const parser::GenericSpec &x) {
info.Resolve(&MakeSymbol(symbolName, Attrs{}, std::move(genericDetails)));
}
+// Applies the implicit CUDA data attribute (managed/unified/pinned) to an
+// allocatable or pointer object entity when the matching -gpu=mem: mode is
+// enabled. Used for locals and module variables as well as derived-type
+// components.
+void DeclarationVisitor::SetImplicitCUDADataAttr(Symbol &symbol) {
+ auto *object{symbol.detailsIf<ObjectEntityDetails>()};
+ if (!object || object->cudaDataAttr() ||
+ !(IsAllocatable(symbol) || IsPointer(symbol))) {
+ return;
+ }
+ const bool cudaEnabled{
+ context().languageFeatures().IsEnabled(common::LanguageFeature::CUDA)};
+ const bool cudaManaged{context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CudaManaged)};
+ const bool cudaUnified{context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CudaUnified)};
+ // Implicitly treat allocatable/pointer arrays as managed when feature
+ // is enabled. This is done after all explicit CUDA attributes have
+ // been processed. Only applies when CUDA Fortran is enabled; otherwise
+ // -gpu=mem:managed on a non-CUDA-Fortran translation unit (e.g. pure
+ // OpenACC) would incorrectly route every allocatable through the CUDA
+ // Fortran managed descriptor pipeline. Under -gpu=mem:unified prefer
+ // the Unified attribute where it is legal (host subprogram, main
+ // program, or component) so generic resolution still selects the
+ // unified specific; fall back to Managed elsewhere (module scope,
+ // device subprograms), which uses the same allocator.
+ if (cudaEnabled && (cudaManaged || cudaUnified)) {
+ const Scope &owner{symbol.owner()};
+ const bool unifiedAllowed{!IsCUDADeviceContext(&owner) &&
+ (owner.IsDerivedType() ||
+ owner.kind() == Scope::Kind::MainProgram ||
+ owner.kind() == Scope::Kind::Subprogram)};
+ object->set_cudaDataAttr(cudaUnified && unifiedAllowed
+ ? common::CUDADataAttr::Unified
+ : common::CUDADataAttr::Managed);
+ // Implicitly treat allocatable arrays as pinned when feature is
+ // enabled.
+ } else if (IsAllocatable(symbol) &&
+ context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CudaPinned)) {
+ object->set_cudaDataAttr(common::CUDADataAttr::Pinned);
+ }
+}
+
void ResolveNamesVisitor::FinishSpecificationPart(
const std::list<parser::DeclarationConstruct> &decls) {
misparsedStmtFuncFound_ = false;
@@ -10627,43 +10677,7 @@ void ResolveNamesVisitor::FinishSpecificationPart(
}
}
- if (auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
- if ((IsAllocatable(symbol) || IsPointer(symbol)) &&
- !object->cudaDataAttr()) {
- const bool cudaEnabled{context().languageFeatures().IsEnabled(
- common::LanguageFeature::CUDA)};
- const bool cudaManaged{context().languageFeatures().IsEnabled(
- common::LanguageFeature::CudaManaged)};
- const bool cudaUnified{context().languageFeatures().IsEnabled(
- common::LanguageFeature::CudaUnified)};
- // Implicitly treat allocatable/pointer arrays as managed when feature
- // is enabled. This is done after all explicit CUDA attributes have
- // been processed. Only applies when CUDA Fortran is enabled; otherwise
- // -gpu=mem:managed on a non-CUDA-Fortran translation unit (e.g. pure
- // OpenACC) would incorrectly route every allocatable through the CUDA
- // Fortran managed descriptor pipeline. Under -gpu=mem:unified prefer
- // the Unified attribute where it is legal (host subprogram, main
- // program, or component) so generic resolution still selects the
- // unified specific; fall back to Managed elsewhere (module scope,
- // device subprograms), which uses the same allocator.
- if (cudaEnabled && (cudaManaged || cudaUnified)) {
- const Scope &owner{symbol.owner()};
- const bool unifiedAllowed{!IsCUDADeviceContext(&owner) &&
- (owner.IsDerivedType() ||
- owner.kind() == Scope::Kind::MainProgram ||
- owner.kind() == Scope::Kind::Subprogram)};
- object->set_cudaDataAttr(cudaUnified && unifiedAllowed
- ? common::CUDADataAttr::Unified
- : common::CUDADataAttr::Managed);
- // Implicitly treat allocatable arrays as pinned when feature is
- // enabled.
- } else if (IsAllocatable(symbol) &&
- context().languageFeatures().IsEnabled(
- common::LanguageFeature::CudaPinned)) {
- object->set_cudaDataAttr(common::CUDADataAttr::Pinned);
- }
- }
- }
+ SetImplicitCUDADataAttr(symbol);
}
currScope().InstantiateDerivedTypes();
for (const auto &decl : decls) {
diff --git a/flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf b/flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf
index 4e052ad839970..ec75078527c50 100644
--- a/flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf
+++ b/flang/test/Lower/CUDA/cuda-implicit-managed-alloc.cuf
@@ -1,15 +1,21 @@
! RUN: bbc -emit-hlfir -fcuda -gpu=unified %s -o - | FileCheck %s --check-prefix=UNI
! RUN: bbc -emit-hlfir -fcuda -gpu=managed %s -o - | FileCheck %s --check-prefix=MAN
+! RUN: bbc -fcuda -gpu=unified --dump-symbols %s 2>&1 | FileCheck %s --check-prefix=UNI-SYM
+! RUN: bbc -fcuda -gpu=managed --dump-symbols %s 2>&1 | FileCheck %s --check-prefix=MAN-SYM
! Under -gpu=unified and -gpu=managed, allocatables and pointers with no
! explicit CUDA data attribute are implicitly attributed. -gpu=unified prefers
-! the Unified attribute where ATTRIBUTES(UNIFIED) is legal (host subprogram /
-! main program); module-scope objects fall back to Managed, which carries no
-! such scope restriction.
+! the Unified attribute where ATTRIBUTES(UNIFIED) is legal (host subprogram,
+! main program, or derived-type component); module-scope objects fall back to
+! Managed, which carries no such scope restriction.
module m
real, allocatable :: mod_arr(:)
real, pointer :: mod_ptr(:)
+ type :: t
+ real, allocatable :: comp_arr(:)
+ real, pointer :: comp_ptr(:)
+ end type
contains
subroutine mod_sub()
real, allocatable :: loc_arr(:)
@@ -39,3 +45,10 @@ end program
! UNI: fir.global @_QMmEmod_ptr {data_attr = #cuf.cuda<managed>}
! MAN: fir.global @_QMmEmod_arr {data_attr = #cuf.cuda<managed>}
! MAN: fir.global @_QMmEmod_ptr {data_attr = #cuf.cuda<managed>}
+
+! Derived-type components: Unified under -gpu=unified, Managed under
+! -gpu=managed. Checked via the symbol table.
+! UNI-SYM: comp_arr, ALLOCATABLE {{.*}} cudaDataAttr: Unified
+! UNI-SYM: comp_ptr, POINTER {{.*}} cudaDataAttr: Unified
+! MAN-SYM: comp_arr, ALLOCATABLE {{.*}} cudaDataAttr: Managed
+! MAN-SYM: comp_ptr, POINTER {{.*}} cudaDataAttr: Managed
More information about the flang-commits
mailing list