[flang-commits] [flang] [mlir] [flang][acc] Don't treat a managed pointer descriptor as device data (PR #222769)
via flang-commits
flang-commits at lists.llvm.org
Fri Sep 18 13:54:45 PDT 2026
https://github.com/yebinchon updated https://github.com/llvm/llvm-project/pull/222769
>From d39b5810a17c6e43affad23180c763015ddb207f 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 1/5] [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 fbaf1010b6b79..d9a9e3a45116e 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -1788,8 +1788,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
>From fabd8f504cb9db35b8cf5273afabe4e56ba020c9 Mon Sep 17 00:00:00 2001
From: Yebin Chon <ychon at nvidia.com>
Date: Wed, 16 Sep 2026 14:33:20 -0700
Subject: [PATCH 2/5] remove pointer specific check; also return false for
unified
---
.../Support/FIROpenACCTypeInterfaces.cpp | 25 +++---
.../OpenACC/acc-implicit-data-device-data.fir | 77 ++++++++++++++++
.../Transforms/OpenACC/acc-implicit-data.fir | 90 -------------------
3 files changed, 92 insertions(+), 100 deletions(-)
create mode 100644 flang/test/Transforms/OpenACC/acc-implicit-data-device-data.fir
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index d9a9e3a45116e..7c4ae44d67e30 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -1788,16 +1788,21 @@ 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)) {
- // 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;
+ if (auto dataAttr = cuf::getDataAttr(defOp)) {
+ cuf::DataAttribute attr = dataAttr.getValue();
+ if (cuf::isDeviceDataAttribute(attr)) {
+ // Managed/Unified establish device *accessibility*, not device
+ // *residency*: the data is reachable from the device but is not an
+ // already-resident device object. Treating it as deviceptr skips the
+ // mapping/attach the runtime needs, so classify it as non-device data
+ // and let it be mapped instead. Device/Constant/Shared are genuinely
+ // device-resident and stay device data. Applies regardless of whether
+ // the entity is a pointer or allocatable.
+ if (attr == cuf::DataAttribute::Managed ||
+ attr == cuf::DataAttribute::Unified)
+ return false;
+ return true;
+ }
}
// Handle operations that access a partial entity - check if the base entity
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-data-device-data.fir b/flang/test/Transforms/OpenACC/acc-implicit-data-device-data.fir
new file mode 100644
index 0000000000000..d371d50c82137
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/acc-implicit-data-device-data.fir
@@ -0,0 +1,77 @@
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data)" -split-input-file | FileCheck %s
+
+// Implicit data classification of CUDA data attributes for compute constructs.
+//
+// A `managed`/`unified` attribute establishes device *accessibility*, not device
+// *residency*: the storage is reachable from the device but is not an already
+// resident device object, so it must be mapped (so the runtime can attach and,
+// where needed, privatize it) rather than treated as `deviceptr`. This holds for
+// both POINTER (!fir.box<!fir.ptr>) and ALLOCATABLE (!fir.box<!fir.heap>)
+// descriptors -- the classification keys on the attribute, not the type.
+//
+// `device` (and `constant`/`shared`) are genuinely device resident and stay
+// device data, i.e. they are given `deviceptr`.
+
+// A managed POINTER descriptor must be mapped, not deviceptr.
+func.func @managed_ptr_desc() {
+ %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>>>>
+ acc.parallel {
+ %1 = fir.load %0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @managed_ptr_desc
+// CHECK-NOT: acc.deviceptr
+// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
+// CHECK: acc.copyout accPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) to varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
+
+// -----
+
+// A managed ALLOCATABLE descriptor must be mapped, not deviceptr (same rule).
+func.func @managed_alloc_desc() {
+ %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>>>>
+ acc.parallel {
+ %1 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @managed_alloc_desc
+// CHECK-NOT: acc.deviceptr
+// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
+// CHECK: acc.copyout accPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) to varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
+
+// -----
+
+// A unified ALLOCATABLE descriptor must be mapped, not deviceptr.
+func.func @unified_alloc_desc() {
+ %0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf64>>> {bindc_name = "u", data_attr = #cuf.cuda<unified>, uniq_name = "_QFEu"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ acc.parallel {
+ %1 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @unified_alloc_desc
+// CHECK-NOT: acc.deviceptr
+// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
+
+// -----
+
+// A device descriptor is genuinely device resident and stays deviceptr.
+func.func @device_alloc_desc() {
+ %0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf64>>> {bindc_name = "d", data_attr = #cuf.cuda<device>, uniq_name = "_QFEd"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ acc.parallel {
+ %1 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @device_alloc_desc
+// CHECK-NOT: acc.copyin
+// CHECK: acc.deviceptr varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) implicit(true)
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-data.fir b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
index 078e9f16889a6..736c6f1e9bf2a 100644
--- a/flang/test/Transforms/OpenACC/acc-implicit-data.fir
+++ b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
@@ -397,96 +397,6 @@ 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
>From ac158cc52b614db501b057568be51235864b8fdb Mon Sep 17 00:00:00 2001
From: Yebin Chon <ychon at nvidia.com>
Date: Wed, 16 Sep 2026 14:35:43 -0700
Subject: [PATCH 3/5] comment cleanup
---
.../OpenACC/Support/FIROpenACCTypeInterfaces.cpp | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 7c4ae44d67e30..cb6d551173496 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -1791,13 +1791,12 @@ static bool isDeviceDataImpl(mlir::Value var) {
if (auto dataAttr = cuf::getDataAttr(defOp)) {
cuf::DataAttribute attr = dataAttr.getValue();
if (cuf::isDeviceDataAttribute(attr)) {
- // Managed/Unified establish device *accessibility*, not device
- // *residency*: the data is reachable from the device but is not an
+ // Managed/Unified establish device accessibility, not device
+ // residency: the data is reachable from the device but is not an
// already-resident device object. Treating it as deviceptr skips the
- // mapping/attach the runtime needs, so classify it as non-device data
- // and let it be mapped instead. Device/Constant/Shared are genuinely
- // device-resident and stay device data. Applies regardless of whether
- // the entity is a pointer or allocatable.
+ // mapping/attach, so classify it as non-device data and let it be mapped
+ // instead. Device/Constant/Shared are genuinely device-resident and stay
+ // device data.
if (attr == cuf::DataAttribute::Managed ||
attr == cuf::DataAttribute::Unified)
return false;
>From ee588fd2e1c846e4ef5e64e8a8cad07b3b1114fc Mon Sep 17 00:00:00 2001
From: Yebin Chon <ychon at nvidia.com>
Date: Wed, 16 Sep 2026 14:37:02 -0700
Subject: [PATCH 4/5] comment cleanup
---
.../Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index cb6d551173496..0ef09b0fcb044 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -1792,8 +1792,8 @@ static bool isDeviceDataImpl(mlir::Value var) {
cuf::DataAttribute attr = dataAttr.getValue();
if (cuf::isDeviceDataAttribute(attr)) {
// Managed/Unified establish device accessibility, not device
- // residency: the data is reachable from the device but is not an
- // already-resident device object. Treating it as deviceptr skips the
+ // residency: the data is reachable from the device but may not already
+ // reside on the device. Treating it as deviceptr skips the
// mapping/attach, so classify it as non-device data and let it be mapped
// instead. Device/Constant/Shared are genuinely device-resident and stay
// device data.
>From d9056eaf5369544cc7a4d099818dea3fda2388ac Mon Sep 17 00:00:00 2001
From: Yebin Chon <ychon at nvidia.com>
Date: Fri, 18 Sep 2026 12:44:14 -0700
Subject: [PATCH 5/5] managed/unified check only for deviceptr
---
.../Support/FIROpenACCTypeInterfaces.cpp | 17 +------
.../OpenACC/Transforms/ACCImplicitData.cpp | 46 ++++++++++++++++---
2 files changed, 42 insertions(+), 21 deletions(-)
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 0ef09b0fcb044..fbaf1010b6b79 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -1788,21 +1788,8 @@ 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 (auto dataAttr = cuf::getDataAttr(defOp)) {
- cuf::DataAttribute attr = dataAttr.getValue();
- if (cuf::isDeviceDataAttribute(attr)) {
- // Managed/Unified establish device accessibility, not device
- // residency: the data is reachable from the device but may not already
- // reside on the device. Treating it as deviceptr skips the
- // mapping/attach, so classify it as non-device data and let it be mapped
- // instead. Device/Constant/Shared are genuinely device-resident and stay
- // device data.
- if (attr == cuf::DataAttribute::Managed ||
- attr == cuf::DataAttribute::Unified)
- return false;
- return true;
- }
- }
+ if (cuf::hasDeviceDataAttr(defOp))
+ return true;
// Handle operations that access a partial entity - check if the base entity
// is device data.
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
index 5ee3a21fab901..f6ea7ba5445ba 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
@@ -216,6 +216,8 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
#include <type_traits>
namespace mlir {
@@ -458,12 +460,44 @@ Operation *ACCImplicitData::generateDataClauseOpForCandidate(
Location loc = computeConstructOp->getLoc();
if (acc::isDeviceValue(var)) {
- // If the variable is device data, use deviceptr clause.
- LLVM_DEBUG(llvm::dbgs() << "Using deviceptr clause because variable is "
- "device data\n");
- return acc::DevicePtrOp::create(builder, loc, var,
- /*structured=*/true, /*implicit=*/true,
- accSupport.getVariableName(var));
+ // `deviceptr` asserts the value is already device-*resident*: no runtime
+ // mapping or attach is performed. CUDA managed/unified storage is device-
+ // *accessible* but not resident, so it must be mapped (so the runtime can
+ // attach and, where needed, privatize it) rather than treated as
+ // deviceptr. isDeviceValue answers accessibility, so refine it to
+ // residence here by excluding managed/unified.
+ //
+ // FIRST-PASS: this pass lives in mlir core and cannot link the CUF dialect,
+ // so the data attribute is matched by name+mnemonic instead of via
+ // cuf::getDataAttr. Replace with an acc::isDeviceResident interface query.
+ auto isManagedOrUnified = [](mlir::Value v) {
+ mlir::Operation *defOp = v.getDefiningOp();
+ if (!defOp)
+ return false;
+ for (llvm::StringRef name : {"data_attr", "cuf.data_attr"}) {
+ if (mlir::Attribute a = defOp->getAttr(name)) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ a.print(os);
+ llvm::StringRef sv(s);
+ if (sv.contains("managed") || sv.contains("unified"))
+ return true;
+ }
+ }
+ return false;
+ };
+
+ if (!isManagedOrUnified(var)) {
+ // If the variable is device-resident data, use deviceptr clause.
+ LLVM_DEBUG(llvm::dbgs() << "Using deviceptr clause because variable is "
+ "device data\n");
+ return acc::DevicePtrOp::create(builder, loc, var,
+ /*structured=*/true, /*implicit=*/true,
+ accSupport.getVariableName(var));
+ }
+ LLVM_DEBUG(llvm::dbgs()
+ << "Not using deviceptr for managed/unified variable "
+ "(device-accessible, not device-resident)\n");
}
Operation *op = nullptr;
More information about the flang-commits
mailing list