[flang-commits] [flang] d63124f - [flang][cuda] Look through associate names for managed and unified data (#228206)
via flang-commits
flang-commits at lists.llvm.org
Thu Oct 1 17:58:03 PDT 2026
Author: Valentin Clement (バレンタイン クレメン)
Date: 2026-10-02T00:57:52Z
New Revision: d63124ff120577621c7a56a2bf1f47437cb3e9c7
URL: https://github.com/llvm/llvm-project/commit/d63124ff120577621c7a56a2bf1f47437cb3e9c7
DIFF: https://github.com/llvm/llvm-project/commit/d63124ff120577621c7a56a2bf1f47437cb3e9c7.diff
LOG: [flang][cuda] Look through associate names for managed and unified data (#228206)
IsCUDADeviceSymbol looks through an associate name: the name is device
data when its selector has device symbols. The managed and unified
predicates only handled object entities, so an associate name whose
selector is managed data was counted as device data that is not managed.
In host code, an element assignment such as
```
associate(px => g%x, py => g%y)
px%a(i,j) = r + px%s * real(py%n, 8)
end associate
```
where `a` and `y` are managed, was then classified as a data transfer.
This
emitted a `cuf.data_transfer` from a scalar value, which the verifier
rejects.
IsCUDADataAttrSymbol now gives an associate name the attribute of the
variable its selector designates. As in CollectEffectiveCudaSymbols, the
attribute of a component prevails over the one of its base.
IsCUDAManagedOrUnifiedSymbol is now built on it.
Added:
Modified:
flang/include/flang/Evaluate/tools.h
flang/lib/Evaluate/tools.cpp
flang/test/Lower/CUDA/cuda-associate-data-transfer.cuf
Removed:
################################################################################
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 3a7d234f903ad..a57c91472e053 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1080,6 +1080,10 @@ template <typename A> SymbolVector GetSymbolVector(const A &x) {
return GetSymbolVectorHelper{}(x);
}
+// The selector of an associate name when it is a variable that is not a
+// pointer returned by a function, else nullptr.
+const Expr<SomeType> *GetVariableSelector(const Symbol &);
+
// GetLastTarget() returns the rightmost symbol in an object designator's
// SymbolVector that has the POINTER or TARGET attribute, or a null pointer
// when none is found.
@@ -1326,24 +1330,13 @@ std::vector<SymbolVector> GetSymbolVectors(const Expr<SomeType> &expr);
bool IsCUDADeviceSymbol(const Symbol &sym);
bool IsCUDADeviceOnlySymbol(const Symbol &sym);
-inline bool IsCUDAManagedOrUnifiedSymbol(const Symbol &sym) {
- if (const auto *details =
- sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) {
- if (details->cudaDataAttr() &&
- (*details->cudaDataAttr() == common::CUDADataAttr::Managed ||
- *details->cudaDataAttr() == common::CUDADataAttr::Unified)) {
- return true;
- }
- }
- return false;
-}
+// True if the data designated by the symbol has the CUDA data attribute. An
+// associate name takes the attribute of the variable its selector designates.
+bool IsCUDADataAttrSymbol(const Symbol &sym, common::CUDADataAttr attr);
-inline bool IsCUDADataAttrSymbol(const Symbol &sym, common::CUDADataAttr attr) {
- if (const auto *details =
- sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) {
- return details->cudaDataAttr() && *details->cudaDataAttr() == attr;
- }
- return false;
+inline bool IsCUDAManagedOrUnifiedSymbol(const Symbol &sym) {
+ return IsCUDADataAttrSymbol(sym, common::CUDADataAttr::Managed) ||
+ IsCUDADataAttrSymbol(sym, common::CUDADataAttr::Unified);
}
inline bool IsCUDAManagedSymbol(const Symbol &sym) {
@@ -1360,6 +1353,11 @@ inline bool HasCUDADataAttr(const Symbol &sym) {
return details && details->cudaDataAttr().has_value();
}
+// Replace each associate name whose selector is a variable by the CUDA symbols
+// of its selector, the same way GetSymbolVector expands it.
+semantics::UnorderedSymbolSet ExpandCudaAssociations(
+ semantics::UnorderedSymbolSet &&symbols);
+
// 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
@@ -1367,7 +1365,10 @@ inline bool HasCUDADataAttr(const Symbol &sym) {
// that a component with an attribute hides.
template <typename A>
semantics::UnorderedSymbolSet CollectEffectiveCudaSymbols(const A &expr) {
- semantics::UnorderedSymbolSet result{CollectCudaSymbols(expr)};
+ // Associate names are expanded so that the set holds the symbols that
+ // GetSymbolVector lists, which the hiding below relies on.
+ semantics::UnorderedSymbolSet result{
+ ExpandCudaAssociations(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.
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 347993d892466..4af26628e8e44 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1138,13 +1138,21 @@ bool IsNullPointerOrAllocatable(const Expr<SomeType> *x) {
}
// GetSymbolVector()
-auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
- if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
- if (IsVariable(details->expr()) && !UnwrapProcedureRef(*details->expr())) {
- // associate(x => variable that is not a pointer returned by a function)
- return (*this)(details->expr());
+const Expr<SomeType> *GetVariableSelector(const Symbol &sym) {
+ if (const auto *details{
+ sym.GetUltimate().detailsIf<semantics::AssocEntityDetails>()}) {
+ if (const auto &expr{details->expr()};
+ expr && IsVariable(*expr) && !UnwrapProcedureRef(*expr)) {
+ return &*expr;
}
}
+ return nullptr;
+}
+
+auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
+ if (const auto *selector{GetVariableSelector(x)}) {
+ return (*this)(*selector);
+ }
return {x.GetUltimate()};
}
auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result {
@@ -1222,6 +1230,22 @@ template semantics::UnorderedSymbolSet CollectCudaSymbols(
template semantics::UnorderedSymbolSet CollectCudaSymbols(
const Expr<SubscriptInteger> &);
+semantics::UnorderedSymbolSet ExpandCudaAssociations(
+ semantics::UnorderedSymbolSet &&symbols) {
+ semantics::UnorderedSymbolSet result;
+ for (SymbolRef sym : symbols) {
+ if (const auto *selector{GetVariableSelector(*sym)}) {
+ for (SymbolRef selectorSym :
+ ExpandCudaAssociations(CollectCudaSymbols(*selector))) {
+ result.insert(selectorSym);
+ }
+ } else {
+ result.insert(sym);
+ }
+ }
+ return result;
+}
+
std::vector<SymbolVector> GetSymbolVectors(const Expr<SomeType> &expr) {
SymbolVector symbols{GetSymbolVector(expr)};
std::reverse(symbols.begin(), symbols.end());
@@ -1333,6 +1357,33 @@ bool IsCUDADeviceSymbol(const Symbol &sym) {
return false;
}
+static std::optional<common::CUDADataAttr> GetDesignatedCUDADataAttr(
+ const Symbol &sym) {
+ const Symbol &ultimate{sym.GetUltimate()};
+ if (const auto *details{
+ ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {
+ return details->cudaDataAttr();
+ }
+ if (const auto *selector{GetVariableSelector(ultimate)}) {
+ // The attribute of a component prevails over the one of its base.
+ SymbolVector symbols{GetSymbolVector(*selector)};
+ for (auto it{symbols.rbegin()}; it != symbols.rend(); ++it) {
+ if (auto attr{GetDesignatedCUDADataAttr(*it)}) {
+ return attr;
+ }
+ if (!it->get().owner().IsDerivedType()) {
+ break;
+ }
+ }
+ }
+ return std::nullopt;
+}
+
+bool IsCUDADataAttrSymbol(const Symbol &sym, common::CUDADataAttr attr) {
+ auto symAttr{GetDesignatedCUDADataAttr(sym)};
+ return symAttr && *symAttr == attr;
+}
+
bool IsCUDADeviceOnlySymbol(const Symbol &sym) {
if (const auto *details =
sym.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()) {
diff --git a/flang/test/Lower/CUDA/cuda-associate-data-transfer.cuf b/flang/test/Lower/CUDA/cuda-associate-data-transfer.cuf
index af850d5842443..e4629c6d42556 100644
--- a/flang/test/Lower/CUDA/cuda-associate-data-transfer.cuf
+++ b/flang/test/Lower/CUDA/cuda-associate-data-transfer.cuf
@@ -19,3 +19,70 @@ end subroutine
! CHECK: %[[D_DECL:.*]]:2 = hlfir.declare %[[D]](%{{.*}}) {data_attr = #cuf.cuda<device>, uniq_name = "_QMmEd"} : (!fir.ref<!fir.array<10x10x10xf64>>, !fir.shape<3>) -> (!fir.ref<!fir.array<10x10x10xf64>>, !fir.ref<!fir.array<10x10x10xf64>>)
! CHECK: %[[D1_DECL:.*]]:2 = hlfir.declare %[[D_DECL]]#0(%4) {uniq_name = "_QFfooEd1"} : (!fir.ref<!fir.array<10x10x10xf64>>, !fir.shape<3>) -> (!fir.ref<!fir.array<10x10x10xf64>>, !fir.ref<!fir.array<10x10x10xf64>>)
! CHECK: cuf.data_transfer %{{.*}} to %[[D1_DECL]]#0 {transfer_kind = #cuf.cuda_transfer<host_device>} : f64, !fir.ref<!fir.array<10x10x10xf64>>
+
+module m2
+ type :: t1
+ real(8), allocatable, managed :: a(:,:)
+ real(8) :: s
+ end type
+ type :: t2
+ integer :: n
+ end type
+ type :: t3
+ type(t1) :: x
+ type(t2), allocatable, managed :: y
+ end type
+end module m2
+
+! An associate name designating a managed component is managed data: the
+! element assignment is done on the host.
+subroutine bar(g, i, j, r)
+ use m2
+ type(t3) :: g
+ integer :: i, j
+ real(8) :: r
+ associate(px => g%x, py => g%y)
+ px%a(i,j) = r + px%s * real(i + py%n, 8)
+ end associate
+end subroutine
+
+! CHECK-LABEL: func.func @_QPbar(
+! CHECK-NOT: cuf.data_transfer
+! CHECK: hlfir.assign %{{.*}} to %{{.*}} : f64, !fir.ref<f64>
+! CHECK-NOT: cuf.data_transfer
+! CHECK: return
+
+! An associate name designating a managed object is managed data, but its
+! device component is device data: the element assignment is a transfer.
+subroutine baz(g)
+ type :: t
+ real, allocatable, device :: d(:)
+ end type
+ type(t), managed :: g
+ associate(p => g)
+ p%d(1) = 1.0
+ end associate
+end subroutine
+
+! CHECK-LABEL: func.func @_QPbaz(
+! CHECK: cuf.data_transfer %{{.*}} to %{{.*}} {hasManagedOrUnifedSymbols, transfer_kind = #cuf.cuda_transfer<host_device>} : f32, !fir.ref<f32>
+
+subroutine nested_association(g)
+ type :: t
+ real, allocatable, managed :: m(:)
+ real, allocatable, device :: d(:)
+ end type
+ type(t), managed :: g
+
+ associate(p => g)
+ associate(q => p)
+ q%m(1) = 2.0 ! Host assignment
+ q%d(1) = 1.0 ! Host-to-device transfer
+ end associate
+ end associate
+end subroutine
+
+! CHECK-LABEL: func.func @_QPnested_association(
+! CHECK-NOT: cuf.data_transfer
+! CHECK: hlfir.assign %{{.*}} to %{{.*}} : f32, !fir.ref<f32>
+! CHECK: cuf.data_transfer %{{.*}} to %{{.*}} {hasManagedOrUnifedSymbols, transfer_kind = #cuf.cuda_transfer<host_device>} : f32, !fir.ref<f32>
More information about the flang-commits
mailing list