[flang-commits] [flang] [flang][cuda] Apply implicit managed attribute to scalar pointers (PR #225934)

Valentin Clement バレンタイン クレメン via flang-commits flang-commits at lists.llvm.org
Wed Sep 23 21:11:22 PDT 2026


https://github.com/clementval updated https://github.com/llvm/llvm-project/pull/225934

>From 6b12a5ac5e0b1c625e50f1372fc0464388e05e15 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Wed, 23 Sep 2026 13:45:16 -0700
Subject: [PATCH 1/2] [flang][cuda] Apply implicit managed attribute to scalar
 pointers

Under -gpu=mem:managed, allocatables and array pointers already get an
implicit CUDA managed attribute, but scalar data pointers can still be
EntityDetails during FinishSpecificationPart and only become objects in
ConvertToObjectEntity. That left ALLOCATE of a scalar pointer on the
host heap, so present() failed to find it.

Factor the implicit managed/pinned attribution into
ApplyImplicitCUDADataAttr and invoke it from FinishSpecificationPart,
component declarations, and ConvertToObjectEntity. Skip it when
SetCUDADataAttr is applying an explicit attribute.
---
 flang/lib/Semantics/resolve-names.cpp      | 83 +++++++++++-----------
 flang/test/Lower/CUDA/cuda-gpu-managed.cuf | 21 ++++++
 2 files changed, 62 insertions(+), 42 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 5ddb8edf3af979..66950460e4d57c 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -781,6 +781,11 @@ class ScopeHandler : public ImplicitRulesVisitor {
   }
   void SetCUDADataAttr(SourceName, Symbol &,
       std::optional<common::CUDADataAttr>, bool isImplicit = false);
+  // Apply -gpu=mem:managed / -gpu=mem:pinned to an unattributed
+  // allocatable or data pointer. Explicit CUDA data attributes are left
+  // unchanged. CUDA Fortran must be enabled so a pure OpenACC compilation
+  // does not route every allocatable through the CUDA Fortran pipeline.
+  void ApplyImplicitCUDADataAttr(Symbol &);
 
 protected:
   FuncResultStack &funcResultStack() { return funcResultStack_; }
@@ -792,7 +797,7 @@ class ScopeHandler : public ImplicitRulesVisitor {
   const DeclTypeSpec *GetImplicitType(
       Symbol &, bool respectImplicitNoneType = true);
   void CheckEntryDummyUse(SourceName, Symbol *);
-  bool ConvertToObjectEntity(Symbol &);
+  bool ConvertToObjectEntity(Symbol &, bool applyImplicitCUDA = true);
   bool ConvertToProcEntity(Symbol &, std::optional<SourceName> = std::nullopt);
 
   const DeclTypeSpec &MakeNumericType(
@@ -3663,7 +3668,8 @@ void ScopeHandler::CheckEntryDummyUse(SourceName source, Symbol *symbol) {
 }
 
 // Convert symbol to be a ObjectEntity or return false if it can't be.
-bool ScopeHandler::ConvertToObjectEntity(Symbol &symbol) {
+bool ScopeHandler::ConvertToObjectEntity(
+    Symbol &symbol, bool applyImplicitCUDA) {
   if (symbol.has<ObjectEntityDetails>()) {
     // nothing to do
   } else if (symbol.has<UnknownDetails>()) {
@@ -3686,6 +3692,12 @@ bool ScopeHandler::ConvertToObjectEntity(Symbol &symbol) {
   } else {
     return false;
   }
+  // Scalar data pointers can still be EntityDetails during
+  // FinishSpecificationPart; they become objects here. Apply the implicit
+  // managed/pinned attribute after that conversion so they match arrays.
+  // Callers that are about to set an explicit CUDA attribute skip this.
+  if (applyImplicitCUDA)
+    ApplyImplicitCUDADataAttr(symbol);
   return true;
 }
 // Convert symbol to be a ProcEntity or return false if it can't be.
@@ -3846,10 +3858,34 @@ bool ScopeHandler::CheckDuplicatedAttrs(
   return ok;
 }
 
+void ScopeHandler::ApplyImplicitCUDADataAttr(Symbol &symbol) {
+  auto *object{symbol.detailsIf<ObjectEntityDetails>()};
+  if (!object || object->cudaDataAttr())
+    return;
+  if (!IsAllocatable(symbol) && !IsPointer(symbol))
+    return;
+  // Only 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::CUDA))
+    return;
+  if (context().languageFeatures().IsEnabled(
+          common::LanguageFeature::CudaManaged)) {
+    object->set_cudaDataAttr(common::CUDADataAttr::Managed);
+    object->set_cudaDataAttrIsImplicit();
+  } else if (IsAllocatable(symbol) &&
+      context().languageFeatures().IsEnabled(
+          common::LanguageFeature::CudaPinned)) {
+    // Implicit pinned remains allocatable-only.
+    object->set_cudaDataAttr(common::CUDADataAttr::Pinned);
+  }
+}
+
 void ScopeHandler::SetCUDADataAttr(SourceName source, Symbol &symbol,
     std::optional<common::CUDADataAttr> attr, bool isImplicit) {
   if (attr) {
-    ConvertToObjectEntity(symbol);
+    ConvertToObjectEntity(symbol, /*applyImplicitCUDA=*/false);
     if (auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
       if (*attr != object->cudaDataAttr().value_or(*attr)) {
         Say(source,
@@ -7618,20 +7654,7 @@ void DeclarationVisitor::Post(const parser::ComponentDecl &x) {
     auto &symbol{DeclareObjectEntity(name, attrs)};
     SetCUDADataAttr(
         name.source, symbol, cudaDataAttr(), cudaDataAttrIsImplicit());
-
-    // Implicitely attribute allocatable/pointer components with `managed`
-    // memory if CUDA and `-gpu=mem:managed` are enabled.
-    if (auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
-      if ((IsAllocatable(symbol) || IsPointer(symbol)) &&
-          !object->cudaDataAttr() &&
-          context().languageFeatures().IsEnabled(
-              common::LanguageFeature::CUDA) &&
-          context().languageFeatures().IsEnabled(
-              common::LanguageFeature::CudaManaged)) {
-        object->set_cudaDataAttr(common::CUDADataAttr::Managed);
-        object->set_cudaDataAttrIsImplicit();
-      }
-    }
+    ApplyImplicitCUDADataAttr(symbol);
     if (symbol.has<ObjectEntityDetails>()) {
       if (auto &init{std::get<std::optional<parser::Initialization>>(x.t)}) {
         Initialization(name, *init, /*inComponentDecl=*/true);
@@ -11011,31 +11034,7 @@ void ResolveNamesVisitor::FinishSpecificationPart(
       }
     }
 
-    if (auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
-      if ((IsAllocatable(symbol) || IsPointer(symbol)) &&
-          !object->cudaDataAttr()) {
-        // 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::CUDA)) {
-          if (context().languageFeatures().IsEnabled(
-                  common::LanguageFeature::CudaManaged)) {
-            object->set_cudaDataAttr(common::CUDADataAttr::Managed);
-            object->set_cudaDataAttrIsImplicit();
-          }
-          // 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);
-        }
-      }
-    }
+    ApplyImplicitCUDADataAttr(symbol);
   }
   // Type the deferred data-implied-do index variables now that the whole
   // specification part has been visited (F'2023 19.4 p5).  Plain
diff --git a/flang/test/Lower/CUDA/cuda-gpu-managed.cuf b/flang/test/Lower/CUDA/cuda-gpu-managed.cuf
index e9b2df1d71ed54..1042b265cffcce 100644
--- a/flang/test/Lower/CUDA/cuda-gpu-managed.cuf
+++ b/flang/test/Lower/CUDA/cuda-gpu-managed.cuf
@@ -79,6 +79,27 @@ end subroutine
 ! CHECK: %[[BOX_DECL:.*]]:2 = hlfir.declare %[[BOX]] {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtest_pointer_managedEptr"}
 ! CHECK: cuf.allocate %[[BOX_DECL]]#0 : {{.*}} {data_attr = #cuf.cuda<managed>, pointer}
 
+! -----------------------------------------------------------------------------
+! Test 5b: Scalar pointers are also implicitly managed (same as arrays)
+! -----------------------------------------------------------------------------
+subroutine test_scalar_pointer_managed()
+  integer, pointer :: p
+  type t
+    integer :: n
+  end type
+  type(t), pointer :: node
+  allocate(p)
+  allocate(node)
+  deallocate(p)
+  deallocate(node)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_scalar_pointer_managed()
+! CHECK: cuf.alloc {{.*}} {bindc_name = "node", data_attr = #cuf.cuda<managed>
+! CHECK: cuf.alloc !fir.box<!fir.ptr<i32>> {bindc_name = "p", data_attr = #cuf.cuda<managed>
+! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<managed>, pointer}
+! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<managed>, pointer}
+
 ! -----------------------------------------------------------------------------
 ! Test 6: Multiple allocatables - mix of implicit and explicit
 ! -----------------------------------------------------------------------------

>From da419df885f9e41236f2593b3cae359d2cd848ee Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Wed, 23 Sep 2026 15:18:03 -0700
Subject: [PATCH 2/2] Fix regression

---
 flang/lib/Semantics/resolve-names.cpp      | 45 ++++++++++++----------
 flang/test/Lower/CUDA/cuda-gpu-managed.cuf | 23 +++++++++++
 2 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 66950460e4d57c..3d06f7d1fa4952 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -797,7 +797,7 @@ class ScopeHandler : public ImplicitRulesVisitor {
   const DeclTypeSpec *GetImplicitType(
       Symbol &, bool respectImplicitNoneType = true);
   void CheckEntryDummyUse(SourceName, Symbol *);
-  bool ConvertToObjectEntity(Symbol &, bool applyImplicitCUDA = true);
+  bool ConvertToObjectEntity(Symbol &);
   bool ConvertToProcEntity(Symbol &, std::optional<SourceName> = std::nullopt);
 
   const DeclTypeSpec &MakeNumericType(
@@ -3668,8 +3668,7 @@ void ScopeHandler::CheckEntryDummyUse(SourceName source, Symbol *symbol) {
 }
 
 // Convert symbol to be a ObjectEntity or return false if it can't be.
-bool ScopeHandler::ConvertToObjectEntity(
-    Symbol &symbol, bool applyImplicitCUDA) {
+bool ScopeHandler::ConvertToObjectEntity(Symbol &symbol) {
   if (symbol.has<ObjectEntityDetails>()) {
     // nothing to do
   } else if (symbol.has<UnknownDetails>()) {
@@ -3692,12 +3691,6 @@ bool ScopeHandler::ConvertToObjectEntity(
   } else {
     return false;
   }
-  // Scalar data pointers can still be EntityDetails during
-  // FinishSpecificationPart; they become objects here. Apply the implicit
-  // managed/pinned attribute after that conversion so they match arrays.
-  // Callers that are about to set an explicit CUDA attribute skip this.
-  if (applyImplicitCUDA)
-    ApplyImplicitCUDADataAttr(symbol);
   return true;
 }
 // Convert symbol to be a ProcEntity or return false if it can't be.
@@ -3859,25 +3852,35 @@ bool ScopeHandler::CheckDuplicatedAttrs(
 }
 
 void ScopeHandler::ApplyImplicitCUDADataAttr(Symbol &symbol) {
-  auto *object{symbol.detailsIf<ObjectEntityDetails>()};
-  if (!object || object->cudaDataAttr())
-    return;
-  if (!IsAllocatable(symbol) && !IsPointer(symbol))
-    return;
   // Only 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::CUDA))
     return;
-  if (context().languageFeatures().IsEnabled(
-          common::LanguageFeature::CudaManaged)) {
+  if (!IsAllocatable(symbol) && !IsPointer(symbol))
+    return;
+  bool managed{context().languageFeatures().IsEnabled(
+      common::LanguageFeature::CudaManaged)};
+  // Implicit pinned remains allocatable-only.
+  bool pinned{!managed && IsAllocatable(symbol) &&
+      context().languageFeatures().IsEnabled(
+          common::LanguageFeature::CudaPinned)};
+  if (!managed && !pinned)
+    return;
+  // A scalar data pointer can still be an EntityDetails at this point, since
+  // only some attributes force an early conversion; arrays are always
+  // objects. Convert it now so both get the attribute. A procedure pointer is
+  // not an object and is left alone.
+  if (!ConvertToObjectEntity(symbol))
+    return;
+  auto *object{symbol.detailsIf<ObjectEntityDetails>()};
+  if (!object || object->cudaDataAttr())
+    return;
+  if (managed) {
     object->set_cudaDataAttr(common::CUDADataAttr::Managed);
     object->set_cudaDataAttrIsImplicit();
-  } else if (IsAllocatable(symbol) &&
-      context().languageFeatures().IsEnabled(
-          common::LanguageFeature::CudaPinned)) {
-    // Implicit pinned remains allocatable-only.
+  } else {
     object->set_cudaDataAttr(common::CUDADataAttr::Pinned);
   }
 }
@@ -3885,7 +3888,7 @@ void ScopeHandler::ApplyImplicitCUDADataAttr(Symbol &symbol) {
 void ScopeHandler::SetCUDADataAttr(SourceName source, Symbol &symbol,
     std::optional<common::CUDADataAttr> attr, bool isImplicit) {
   if (attr) {
-    ConvertToObjectEntity(symbol, /*applyImplicitCUDA=*/false);
+    ConvertToObjectEntity(symbol);
     if (auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
       if (*attr != object->cudaDataAttr().value_or(*attr)) {
         Say(source,
diff --git a/flang/test/Lower/CUDA/cuda-gpu-managed.cuf b/flang/test/Lower/CUDA/cuda-gpu-managed.cuf
index 1042b265cffcce..b30a4a677d3940 100644
--- a/flang/test/Lower/CUDA/cuda-gpu-managed.cuf
+++ b/flang/test/Lower/CUDA/cuda-gpu-managed.cuf
@@ -100,6 +100,29 @@ end subroutine
 ! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<managed>, pointer}
 ! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<managed>, pointer}
 
+! -----------------------------------------------------------------------------
+! Test 5c: Explicit CUDA attributes on scalars that convert to ObjectEntity
+! before SetCUDADataAttr (ALLOCATABLE / VOLATILE) must not collide with
+! implicit managed.
+! -----------------------------------------------------------------------------
+subroutine test_scalar_explicit_attrs()
+  real, allocatable, device :: x
+  real, allocatable, pinned :: y
+  integer, pointer, volatile, device :: p
+  integer, pointer, device :: q
+  allocate(x, y, p, q)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_scalar_explicit_attrs()
+! CHECK: cuf.alloc !fir.box<!fir.ptr<i32>> {bindc_name = "p", data_attr = #cuf.cuda<device>
+! CHECK: cuf.alloc !fir.box<!fir.ptr<i32>> {bindc_name = "q", data_attr = #cuf.cuda<device>
+! CHECK: cuf.alloc !fir.box<!fir.heap<f32>> {bindc_name = "x", data_attr = #cuf.cuda<device>
+! CHECK: cuf.alloc !fir.box<!fir.heap<f32>> {bindc_name = "y", data_attr = #cuf.cuda<pinned>
+! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<device>}
+! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<pinned>}
+! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<device>, pointer}
+! CHECK: cuf.allocate {{.*}} {data_attr = #cuf.cuda<device>, pointer}
+
 ! -----------------------------------------------------------------------------
 ! Test 6: Multiple allocatables - mix of implicit and explicit
 ! -----------------------------------------------------------------------------



More information about the flang-commits mailing list