[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:55:16 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-openacc
Author: yebinchon
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/222769.diff
2 Files Affected:
- (modified) flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp (+10-1)
- (modified) flang/test/Transforms/OpenACC/acc-implicit-data.fir (+90)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/222769
More information about the flang-commits
mailing list