[flang-commits] [flang] [flang][acc] Don't treat a managed pointer descriptor as device data (PR #222769)
via flang-commits
flang-commits at lists.llvm.org
Thu Sep 10 13:54:38 PDT 2026
https://github.com/yebinchon created https://github.com/llvm/llvm-project/pull/222769
A `managed` CUDA data attribute on a pointer describes the storage of the descriptor itself, not the target it designates. The target is established by pointer assignment and may live in host memory, so isDeviceData must not report the designated data as accessible from the device.
Reporting such a descriptor as device data made the OpenACC implicit data pass emit `deviceptr` for it, so no mapping was created for the descriptor at all.
>From 9fa358b0a7c0c46387ec1b872422d96a5ad1a533 Mon Sep 17 00:00:00 2001
From: Yebin Chon <ychon at nvidia.com>
Date: Thu, 10 Sep 2026 13:40:44 -0700
Subject: [PATCH] [flang][acc] Don't treat a managed pointer descriptor as
device data
---
.../Support/FIROpenACCTypeInterfaces.cpp | 11 ++-
.../Transforms/OpenACC/acc-implicit-data.fir | 90 +++++++++++++++++++
2 files changed, 100 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index e070de2d908b2..1dfd5e5d2aab0 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -1783,8 +1783,17 @@ static bool isDeviceDataImpl(mlir::Value var) {
assert(defOp && "expected defining op for non-block-argument value");
// Check for CUDA attributes on the defining operation.
- if (cuf::hasDeviceDataAttr(defOp))
+ if (cuf::hasDeviceDataAttr(defOp)) {
+ // A `managed` attribute on a POINTER describes the storage of the
+ // descriptor, not the target it designates: the target is established by
+ // pointer assignment and may live in host memory. An ALLOCATABLE obtains
+ // its storage through the attributed entity, so there the attribute does
+ // describe the data.
+ if (cuf::hasDataAttr(defOp, cuf::DataAttribute::Managed) &&
+ fir::isPointerType(currentVal.getType()))
+ return false;
return true;
+ }
// Handle operations that access a partial entity - check if the base entity
// is device data.
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-data.fir b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
index 736c6f1e9bf2a..078e9f16889a6 100644
--- a/flang/test/Transforms/OpenACC/acc-implicit-data.fir
+++ b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
@@ -397,6 +397,96 @@ func.func private @_FortranAAllocatableSetBounds(!fir.ref<!fir.box<none>>, i32,
// -----
+// Test that a managed POINTER descriptor is not treated as device data. The
+// attribute describes the storage of the descriptor, not the target it
+// designates: the target is established by pointer assignment and may live in
+// host memory, so the descriptor needs a copy rather than a deviceptr.
+func.func @test_cuda_managed_pointer_no_implicit_deviceptr(%arg0: !fir.box<!fir.heap<!fir.array<?xf64>>>) {
+ %c0 = arith.constant 0 : index
+ %cst = arith.constant 2.500000e+00 : f64
+ %0 = cuf.alloc !fir.box<!fir.ptr<!fir.array<?xf64>>> {bindc_name = "p", data_attr = #cuf.cuda<managed>, uniq_name = "_QFEp"} -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ %1 = fir.declare %0 {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFEp"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ acc.serial {
+ %2:3 = fir.box_dims %arg0, %c0 : (!fir.box<!fir.heap<!fir.array<?xf64>>>, index) -> (index, index, index)
+ %3 = fir.shift %2#0 : (index) -> !fir.shift<1>
+ %4 = fir.rebox %arg0(%3) : (!fir.box<!fir.heap<!fir.array<?xf64>>>, !fir.shift<1>) -> !fir.box<!fir.ptr<!fir.array<?xf64>>>
+ fir.store %4 to %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ %5 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ %6 = fir.box_addr %5 : (!fir.box<!fir.ptr<!fir.array<?xf64>>>) -> !fir.ptr<!fir.array<?xf64>>
+ %7 = fir.coordinate_of %6, %c0 : (!fir.ptr<!fir.array<?xf64>>, index) -> !fir.ref<f64>
+ fir.store %cst to %7 : !fir.ref<f64>
+ acc.yield
+ }
+ cuf.free %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>> {data_attr = #cuf.cuda<managed>}
+ return
+}
+
+// CHECK-LABEL: func.func @test_cuda_managed_pointer_no_implicit_deviceptr
+// CHECK-NOT: acc.deviceptr
+// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>){{.*}}name("p")
+// CHECK: acc.copyout {{.*}}name("p")
+// CHECK-NOT: acc.deviceptr
+
+// -----
+
+// Test that a managed POINTER descriptor of polymorphic type is recognized:
+// a CLASS(..), POINTER descriptor is a fir.class rather than a fir.box.
+func.func @test_cuda_managed_class_pointer_no_implicit_deviceptr() {
+ %0 = cuf.alloc !fir.class<!fir.ptr<!fir.type<_QMmTt{i:i32}>>> {bindc_name = "p", data_attr = #cuf.cuda<managed>, uniq_name = "_QFEp"} -> !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{i:i32}>>>>
+ %1 = fir.declare %0 {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFEp"} : (!fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{i:i32}>>>>) -> !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{i:i32}>>>>
+ acc.serial {
+ %2 = fir.load %1 : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{i:i32}>>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @test_cuda_managed_class_pointer_no_implicit_deviceptr
+// CHECK-NOT: acc.deviceptr
+// CHECK: acc.copyin {{.*}}name("p")
+// CHECK-NOT: acc.deviceptr
+
+// -----
+
+// Test that a managed ALLOCATABLE descriptor is still device data: an
+// ALLOCATABLE obtains its storage through the attributed entity, so there the
+// attribute does describe the data.
+func.func @test_cuda_managed_allocatable_implicit_deviceptr() {
+ %0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf64>>> {bindc_name = "a", data_attr = #cuf.cuda<managed>, uniq_name = "_QFEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ %1 = fir.declare %0 {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ acc.serial {
+ %2 = fir.load %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @test_cuda_managed_allocatable_implicit_deviceptr
+// CHECK-NOT: acc.copyin
+// CHECK: acc.deviceptr {{.*}}name("a")
+// CHECK-NOT: acc.copyout
+
+// -----
+
+// Test that only the managed attribute is special-cased: a POINTER descriptor
+// in device memory is device data.
+func.func @test_cuda_device_pointer_implicit_deviceptr() {
+ %0 = cuf.alloc !fir.box<!fir.ptr<!fir.array<?xf64>>> {bindc_name = "p", data_attr = #cuf.cuda<device>, uniq_name = "_QFEp"} -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ %1 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFEp"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ acc.serial {
+ %2 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @test_cuda_device_pointer_implicit_deviceptr
+// CHECK-NOT: acc.copyin
+// CHECK: acc.deviceptr {{.*}}name("p")
+// CHECK-NOT: acc.copyout
+
+// -----
+
// Test argument mapped with deviceptr but used not via data mapping.
func.func @test_fir_declare_deviceptr_arg_in_parallel(%arg0: !fir.ref<!fir.array<10xf64>>) {
%c10 = arith.constant 10 : index
More information about the flang-commits
mailing list