[flang-commits] [flang] [flang][cuda] Honor component CUDA attrs in assignment copies (PR #220398)
Valentin Clement バレンタイン クレメン via flang-commits
flang-commits at lists.llvm.org
Tue Sep 1 14:50:09 PDT 2026
https://github.com/clementval created https://github.com/llvm/llvm-project/pull/220398
A device component of a managed object was classified as managed because IsCUDADataTransfer counted every symbol in the reference. Classify each side from the designated data so a component attribute hides the base.
>From 228ce5211d104993e0cd145a145bf0246e1d6627 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Tue, 1 Sep 2026 14:46:09 -0700
Subject: [PATCH] [flang][cuda] Honor component CUDA attrs in assignment copies
A device component of a managed object was classified as managed because
IsCUDADataTransfer counted every symbol in the reference. Classify each
side from the designated data so a component attribute hides the base.
---
flang/include/flang/Evaluate/tools.h | 72 +++++++++++++++++--
flang/test/Lower/CUDA/cuda-managed-assign.cuf | 39 ++++++++++
2 files changed, 107 insertions(+), 4 deletions(-)
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 8cfc6adfcc941..e8847f50549d1 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
@@ -1470,15 +1529,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