[flang-commits] [flang] 34dc057 - [flang][cuda] Honor component CUDA attrs in assignment copies (#220398)

via flang-commits flang-commits at lists.llvm.org
Wed Sep 2 09:16:16 PDT 2026


Author: Valentin Clement (バレンタイン クレメン)
Date: 2026-09-02T09:16:11-07:00
New Revision: 34dc057c4c095dc98cbbd0b1d16c8bf6b2e18c09

URL: https://github.com/llvm/llvm-project/commit/34dc057c4c095dc98cbbd0b1d16c8bf6b2e18c09
DIFF: https://github.com/llvm/llvm-project/commit/34dc057c4c095dc98cbbd0b1d16c8bf6b2e18c09.diff

LOG: [flang][cuda] Honor component CUDA attrs in assignment copies (#220398)

Added: 
    

Modified: 
    flang/include/flang/Evaluate/tools.h
    flang/test/Lower/CUDA/cuda-managed-assign.cuf

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 8cfc6adfcc941..71544a3ba5597 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -22,6 +22,7 @@
 #include "flang/Semantics/attr.h"
 #include "flang/Semantics/scope.h"
 #include "flang/Semantics/symbol.h"
+#include <algorithm>
 #include <array>
 #include <optional>
 #include <set>
@@ -1353,6 +1354,64 @@ inline bool IsCUDAUnifiedSymbol(const Symbol &sym) {
   return IsCUDADataAttrSymbol(sym, common::CUDADataAttr::Unified);
 }
 
+inline bool HasCUDADataAttr(const Symbol &sym) {
+  const auto *details{
+      sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()};
+  return details && details->cudaDataAttr().has_value();
+}
+
+// The data attribute of a component describes the data that the component
+// designates, so it hides the attribute of the object that the component is
+// taken from: in a%b, where a is managed and b is device, a%b designates
+// device data. Collect the symbols of the expression, leaving out the ones
+// that a component with an attribute hides.
+template <typename A>
+semantics::UnorderedSymbolSet CollectEffectiveCudaSymbols(const A &expr) {
+  semantics::UnorderedSymbolSet result{CollectCudaSymbols(expr)};
+  SymbolVector symbols{GetSymbolVector(expr)};
+  // GetSymbolVector lists the base of a component chain before its components.
+  // Reverse it to visit the innermost component of a chain first.
+  std::reverse(symbols.begin(), symbols.end());
+  bool hidden{false};
+  for (const Symbol &sym : symbols) {
+    bool isComponent{sym.owner().IsDerivedType()};
+    if (hidden) {
+      result.erase(sym);
+    } else if (isComponent && HasCUDADataAttr(sym)) {
+      hidden = true;
+    }
+    if (!isComponent) {
+      hidden = false; // The base ends the component chain.
+    }
+  }
+  return result;
+}
+
+// Get the number of symbols with the CUDA managed attribute in a set.
+inline int CountCUDAManagedSymbols(
+    const semantics::UnorderedSymbolSet &symbols) {
+  int count{0};
+  for (const Symbol &sym : symbols) {
+    if (IsCUDAManagedSymbol(sym)) {
+      ++count;
+    }
+  }
+  return count;
+}
+
+// Get the number of symbols with a CUDA device attribute other than unified in
+// a set.
+inline int CountCUDANonUnifiedSymbols(
+    const semantics::UnorderedSymbolSet &symbols) {
+  int count{0};
+  for (const Symbol &sym : symbols) {
+    if (IsCUDADeviceSymbol(sym) && !IsCUDAUnifiedSymbol(sym)) {
+      ++count;
+    }
+  }
+  return count;
+}
+
 // Non-allocatable module-level managed/unified variables use pointer
 // indirection through a companion global in __nv_managed_data__.
 // Explicit data transfers (cudaMemcpy) must be avoided for these
@@ -1403,48 +1462,12 @@ inline int GetNbOfCUDAManagedOrUnifiedSymbols(const A &expr) {
   return symbols.size();
 }
 
-// Get the number of distinct symbols with the CUDA managed attribute in the
-// expression.
-template <typename A> inline int GetNbOfCUDAManagedSymbols(const A &expr) {
-  semantics::UnorderedSymbolSet symbols;
-  for (const Symbol &sym : CollectCudaSymbols(expr)) {
-    if (IsCUDAManagedSymbol(sym)) {
-      symbols.insert(sym);
-    }
-  }
-  return symbols.size();
-}
-
-// Get the number of distinct symbols with a CUDA device attribute other than
-// unified in the expression.
-template <typename A> inline int GetNbOfCUDANonUnifiedSymbols(const A &expr) {
-  semantics::UnorderedSymbolSet symbols;
-  for (const Symbol &sym : CollectCudaSymbols(expr)) {
-    if (IsCUDADeviceSymbol(sym) && !IsCUDAUnifiedSymbol(sym)) {
-      symbols.insert(sym);
-    }
-  }
-  return symbols.size();
-}
-
 // Check if any of the symbols part of the expression has a CUDA device
 // attribute.
 template <typename A> inline bool HasCUDADeviceAttrs(const A &expr) {
   return GetNbOfCUDADeviceSymbols(expr) > 0;
 }
 
-// Check if any of the symbols part of the expression has the CUDA managed
-// attribute.
-template <typename A> inline bool HasCUDAManagedSymbols(const A &expr) {
-  return GetNbOfCUDAManagedSymbols(expr) > 0;
-}
-
-// Check if any of the symbols part of the expression has a CUDA device
-// attribute other than unified.
-template <typename A> inline bool HasCUDANonUnifiedSymbols(const A &expr) {
-  return GetNbOfCUDANonUnifiedSymbols(expr) > 0;
-}
-
 // True for a whole reference to a managed array: a whole array variable, or a
 // whole array component that itself has the managed attribute (a%b where b is
 // managed). An array section, an array element, a component of a managed object
@@ -1470,15 +1493,20 @@ template <typename A> inline bool IsWholeManagedArray(const A &expr) {
 // Unified data is host memory that the device can also access, so it takes the
 // place of host data in the rules above and an assignment between unified sides
 // is host code.
+// The side of an assignment is classified from the data it designates, so the
+// attribute of a component prevails over the attribute of the object it is
+// taken from.
 // Return true if the assignment is one of the copies above.
 template <typename A, typename B>
 inline bool IsCUDADataTransfer(const A &lhs, const B &rhs) {
+  semantics::UnorderedSymbolSet lhsSymbols{CollectEffectiveCudaSymbols(lhs)};
+  semantics::UnorderedSymbolSet rhsSymbols{CollectEffectiveCudaSymbols(rhs)};
   // Unified data is left out of these counts and checks so that it is handled
   // as host data.
-  bool lhsHasManaged{HasCUDAManagedSymbols(lhs)};
-  bool lhsIsHost{!HasCUDANonUnifiedSymbols(lhs)};
-  int rhsNbManagedSymbols{GetNbOfCUDAManagedSymbols(rhs)};
-  int rhsNbSymbols{GetNbOfCUDANonUnifiedSymbols(rhs)};
+  bool lhsHasManaged{CountCUDAManagedSymbols(lhsSymbols) > 0};
+  bool lhsIsHost{CountCUDANonUnifiedSymbols(lhsSymbols) == 0};
+  int rhsNbManagedSymbols{CountCUDAManagedSymbols(rhsSymbols)};
+  int rhsNbSymbols{CountCUDANonUnifiedSymbols(rhsSymbols)};
 
   if (HasNonAllocatableModuleCUDAManagedSymbols(lhs))
     return false;

diff  --git a/flang/test/Lower/CUDA/cuda-managed-assign.cuf b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
index 09bfcd5296c89..2a201f56dedce 100644
--- a/flang/test/Lower/CUDA/cuda-managed-assign.cuf
+++ b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
@@ -64,6 +64,45 @@ end subroutine
 ! CHECK-LABEL: func.func @_QPmanaged_component_attr_assign()
 ! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
 
+! The attribute of a component describes the data the component designates, so it
+! prevails over the attribute of the object the component is taken from. A device
+! component of a managed object is device data, which the host cannot write in
+! place, whatever the form of the reference.
+subroutine device_component_of_managed(n, i)
+  integer :: n, i
+  type :: t
+    integer(4), allocatable, device :: dcomp(:)
+  end type
+  type(t), allocatable, managed :: cm(:)
+  integer(4) :: ha(3)
+  allocate(cm(n))
+  allocate(cm(i)%dcomp(3))
+  cm(i)%dcomp = 10         ! device component = constant
+  cm(i)%dcomp = ha         ! device component = host array
+  cm(i)%dcomp(1:2) = 10    ! device component section = constant
+  ha = cm(i)%dcomp         ! host array = device component
+end subroutine
+
+! CHECK-LABEL: func.func @_QPdevice_component_of_managed
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+! The object's attribute still applies to a component that has none.
+subroutine host_component_of_managed(n, i)
+  integer :: n, i
+  type :: t
+    integer(4) :: m(16)
+  end type
+  type(t), allocatable, managed :: cm(:)
+  allocate(cm(n))
+  cm(i)%m = 10
+end subroutine
+
+! CHECK-LABEL: func.func @_QPhost_component_of_managed
+! CHECK-NOT: cuf.data_transfer
+
 ! One whole managed side is enough: the other may be a section.
 subroutine managed_mixed_whole_section()
   integer(4), managed :: ma(4), mb(4,16)


        


More information about the flang-commits mailing list