[flang-commits] [flang] [flang][cuda] Limit mem:managed generic matching to allocatable and pointer (PR #223801)
Valentin Clement バレンタイン クレメン via flang-commits
flang-commits at lists.llvm.org
Tue Sep 15 14:03:56 PDT 2026
https://github.com/clementval updated https://github.com/llvm/llvm-project/pull/223801
>From 2dc9a5a76b78ec781db5c05a33e639c7a34aedf6 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Tue, 15 Sep 2026 12:46:17 -0700
Subject: [PATCH] [flang][cuda] Limit mem:managed generic matching to
allocatable and pointer
managed memory mode only puts allocatable and pointer allocations in
managed memory. Static and automatic host objects stay host-resident, so
they must not match a device/managed/unified dummy during generic
resolution.
Keep the -gpu=mem:unified relaxation for all host variables, which are
device-accessible in that mode.
This stops host calls such as sum(host_array(:)) from resolving to a
CUDA Fortran device-dummy specific.
---
flang/include/flang/Support/Fortran.h | 2 +-
flang/lib/Semantics/check-call.cpp | 12 +++-
flang/lib/Semantics/expression.cpp | 11 ++-
flang/lib/Support/Fortran.cpp | 18 +++--
.../CUDA/cuf-matching-managed-static.cuf | 69 +++++++++++++++++++
5 files changed, 103 insertions(+), 9 deletions(-)
create mode 100644 flang/test/Semantics/CUDA/cuf-matching-managed-static.cuf
diff --git a/flang/include/flang/Support/Fortran.h b/flang/include/flang/Support/Fortran.h
index 95290e7a36b20..315924e5bd9ab 100644
--- a/flang/include/flang/Support/Fortran.h
+++ b/flang/include/flang/Support/Fortran.h
@@ -105,7 +105,7 @@ bool AreCompatibleCUDADataAttrs(std::optional<CUDADataAttr>,
std::optional<CUDADataAttr>, IgnoreTKRSet, bool allowUnifiedMatchingRule,
bool isHostDeviceProcedure,
const LanguageFeatureControl *features = nullptr,
- bool actualIsVariable = true);
+ bool actualIsVariable = true, bool actualIsAllocatableOrPointer = false);
// Format vector type as Fortran string
std::string FormatVectorTypeAsFortran(
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 784ca0468a487..2fd7ff5ac1a5c 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -1211,13 +1211,23 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
bool isHostDeviceProc{procedure.cudaSubprogramAttrs &&
*procedure.cudaSubprogramAttrs ==
common::CUDASubprogramAttrs::HostDevice};
+ bool actualIsAllocatableOrPointer{false};
+ if (actualIsVariable) {
+ for (const Symbol &s : evaluate::GetSymbolVector(actual)) {
+ if (IsAllocatableOrPointer(ResolveAssociations(s))) {
+ actualIsAllocatableOrPointer = true;
+ break;
+ }
+ }
+ }
// TYPE(*) assumed-size/rank dummies are opaque buffers (e.g. MPI) and do
// not impose a CUDA address space on their actual argument.
bool skipCudaDataAttrCheck{IsCUDAAddressSpaceAgnostic(dummy)};
if (!skipCudaDataAttrCheck &&
!common::AreCompatibleCUDADataAttrs(dummyDataAttr, actualDataAttr,
dummy.ignoreTKR, /*allowUnifiedMatchingRule=*/true,
- isHostDeviceProc, &context.languageFeatures(), actualIsVariable)) {
+ isHostDeviceProc, &context.languageFeatures(), actualIsVariable,
+ actualIsAllocatableOrPointer)) {
auto toStr{[](std::optional<common::CUDADataAttr> x) {
return x ? "ATTRIBUTES("s +
parser::ToUpperCaseLetters(common::EnumToString(*x)) + ")"s
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 91e98d5c8018c..deead9391e9f5 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -2983,17 +2983,22 @@ static int GetMatchingDistance(const common::LanguageFeatureControl &features,
std::optional<common::CUDADataAttr> actualDataAttr, dummyDataAttr;
// True when an unattributed actual may use the implicit CUDA memory mode
- // matching enabled by -gpu=mem:unified or -gpu=mem:managed.
+ // matching enabled by -gpu=mem:unified (any variable) or -gpu=mem:managed
+ // (allocatable/pointer objects only).
bool actualCanUseImplicitCudaMemoryMode{false};
if (actual) {
if (auto *expr{actual->UnwrapExpr()}) {
if (evaluate::IsVariable(*expr)) {
- actualCanUseImplicitCudaMemoryMode = true;
+ bool actualIsAllocatableOrPointer{false};
// Match check-call.cpp: walk the whole designator so e.g. b%a picks up
// ATTRIBUTES(DEVICE) from the base b when the component a has no CUDA
// attribute (OpenACC use_device(b) + doit(b%a)), not only from the
// last symbol (GetLastSymbol would only see a).
for (const Symbol &s : evaluate::GetSymbolVector(*expr)) {
+ if (semantics::IsAllocatableOrPointer(
+ semantics::ResolveAssociations(s))) {
+ actualIsAllocatableOrPointer = true;
+ }
if (const auto *object{
s.detailsIf<semantics::ObjectEntityDetails>()}) {
if (auto cudaAttr{object->cudaDataAttr()}) {
@@ -3001,6 +3006,8 @@ static int GetMatchingDistance(const common::LanguageFeatureControl &features,
}
}
}
+ actualCanUseImplicitCudaMemoryMode =
+ isCudaUnified || (isCudaManaged && actualIsAllocatableOrPointer);
} else if (const auto *actualLastSymbol{evaluate::GetLastSymbol(*expr)}) {
// Propagate any explicit CUDA data attribute from the referenced
// symbol (e.g. a device array operand inside RESHAPE()) so that
diff --git a/flang/lib/Support/Fortran.cpp b/flang/lib/Support/Fortran.cpp
index 80243d2b10b22..5b9acd3256afd 100644
--- a/flang/lib/Support/Fortran.cpp
+++ b/flang/lib/Support/Fortran.cpp
@@ -108,13 +108,19 @@ std::string AsFortran(IgnoreTKRSet tkr) {
bool AreCompatibleCUDADataAttrs(std::optional<CUDADataAttr> x,
std::optional<CUDADataAttr> y, IgnoreTKRSet ignoreTKR,
bool allowUnifiedMatchingRule, bool isHostDeviceProcedure,
- const LanguageFeatureControl *features, bool actualIsVariable) {
+ const LanguageFeatureControl *features, bool actualIsVariable,
+ bool actualIsAllocatableOrPointer) {
bool isCudaManaged{features
? features->IsEnabled(common::LanguageFeature::CudaManaged)
: false};
bool isCudaUnified{features
? features->IsEnabled(common::LanguageFeature::CudaUnified)
: false};
+ // -gpu=mem:unified makes ordinary host variables device-accessible.
+ // -gpu=mem:managed only puts allocatable/pointer allocations in managed
+ // memory; static and automatic host objects remain host-resident.
+ bool actualHasImplicitCudaMemory{actualIsVariable &&
+ (isCudaUnified || (isCudaManaged && actualIsAllocatableOrPointer))};
if (ignoreTKR.test(common::IgnoreTKR::Device)) {
return true;
}
@@ -163,18 +169,20 @@ bool AreCompatibleCUDADataAttrs(std::optional<CUDADataAttr> x,
// Non-variable actuals (expression results, intrinsic call results)
// are host temporaries whose storage is not accessible from device
// code even under unified memory, so the relaxation does not apply.
- if (!y && (isCudaUnified || isCudaManaged) &&
- !ignoreTKR.test(IgnoreTKR::Managed) && actualIsVariable) {
+ // Under -gpu=mem:managed, only allocatable/pointer actuals have
+ // implicit managed storage.
+ if (!y && actualHasImplicitCudaMemory &&
+ !ignoreTKR.test(IgnoreTKR::Managed)) {
return true;
}
} else if (*x == CUDADataAttr::Managed) {
if ((y && *y == CUDADataAttr::Unified) ||
- (!y && (isCudaUnified || isCudaManaged) && actualIsVariable)) {
+ (!y && actualHasImplicitCudaMemory)) {
return true;
}
} else if (*x == CUDADataAttr::Unified) {
if ((y && *y == CUDADataAttr::Managed) ||
- (!y && (isCudaUnified || isCudaManaged) && actualIsVariable)) {
+ (!y && actualHasImplicitCudaMemory)) {
return true;
}
}
diff --git a/flang/test/Semantics/CUDA/cuf-matching-managed-static.cuf b/flang/test/Semantics/CUDA/cuf-matching-managed-static.cuf
new file mode 100644
index 0000000000000..1daf5c6d11f35
--- /dev/null
+++ b/flang/test/Semantics/CUDA/cuf-matching-managed-static.cuf
@@ -0,0 +1,69 @@
+! RUN: bbc -emit-hlfir -fcuda -gpu=managed %s -o - | FileCheck %s --check-prefix=MANAGED
+! RUN: bbc -emit-hlfir -fcuda -gpu=unified %s -o - | FileCheck %s --check-prefix=UNIFIED
+
+! Under -gpu=mem:managed, only allocatable and pointer objects are
+! implicitly managed. A static host array (and a section of one) must
+! keep matching the host specific, not a device, managed, or unified dummy.
+! Under -gpu=mem:unified, every host variable is device-accessible, so the
+! same static array matches the unified specific.
+
+module m
+ integer :: global_host_static(2)
+ integer, allocatable :: global_host_alloc(:)
+ integer, pointer :: global_host_ptr(:)
+ interface gen
+ module procedure sub_host
+ module procedure sub_device
+ module procedure sub_managed
+ module procedure sub_unified
+ end interface
+contains
+ subroutine sub_host(x)
+ integer :: x(:)
+ end subroutine
+ subroutine sub_device(x)
+ integer, device :: x(:)
+ end subroutine
+ subroutine sub_managed(x)
+ integer, managed :: x(:)
+ end subroutine
+ subroutine sub_unified(x)
+ integer, unified :: x(:)
+ end subroutine
+end module
+
+subroutine driver
+ use m
+ integer :: host_static(2)
+ integer, allocatable :: host_alloc(:)
+ integer, pointer :: host_ptr(:)
+ allocate(host_alloc(2), host_ptr(2))
+
+ call gen(host_static)
+ call gen(host_static(:))
+ call gen(host_alloc)
+ call gen(host_alloc(:))
+ call gen(host_ptr)
+ call gen(global_host_static)
+ call gen(global_host_alloc)
+ call gen(global_host_ptr)
+end subroutine
+
+! MANAGED-LABEL: func.func @_QPdriver
+! MANAGED: fir.call @_QMmPsub_host
+! MANAGED: fir.call @_QMmPsub_host
+! MANAGED: fir.call @_QMmPsub_managed
+! MANAGED: fir.call @_QMmPsub_managed
+! MANAGED: fir.call @_QMmPsub_managed
+! MANAGED: fir.call @_QMmPsub_host
+! MANAGED: fir.call @_QMmPsub_managed
+! MANAGED: fir.call @_QMmPsub_managed
+
+! UNIFIED-LABEL: func.func @_QPdriver
+! UNIFIED: fir.call @_QMmPsub_unified
+! UNIFIED: fir.call @_QMmPsub_unified
+! UNIFIED: fir.call @_QMmPsub_unified
+! UNIFIED: fir.call @_QMmPsub_unified
+! UNIFIED: fir.call @_QMmPsub_unified
+
+
More information about the flang-commits
mailing list