[flang-commits] [flang] [flang][cuda] Restrict managed data transfers to whole-array assignments (PR #212593)
Zhen Wang via flang-commits
flang-commits at lists.llvm.org
Tue Jul 28 15:46:01 PDT 2026
https://github.com/wangzpgi updated https://github.com/llvm/llvm-project/pull/212593
>From c27ce86054cc6014a3f6e03f1eda8f082a7b4711 Mon Sep 17 00:00:00 2001
From: Zhen Wang <zhenw at nvidia.com>
Date: Tue, 28 Jul 2026 10:31:18 -0700
Subject: [PATCH 1/2] Restrict managed data transfers to whole-array
assignments
---
flang/include/flang/Evaluate/tools.h | 24 ++-
flang/test/Lower/CUDA/cuda-managed-assign.cuf | 194 +++++++++++++++++-
2 files changed, 205 insertions(+), 13 deletions(-)
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index a60848bea714e..28cf9b5cc3c82 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1392,6 +1392,14 @@ template <typename A> inline bool HasCUDADeviceAttrs(const A &expr) {
return GetNbOfCUDADeviceSymbols(expr) > 0;
}
+// Check if the expression designates an entire array variable or component, as
+// opposed to an array section, an array element, or a computed value. Unlike
+// !IsArraySection(), a whole array component of a scalar base (a%b) qualifies.
+template <typename A> inline bool IsWholeArrayDesignator(const A &expr) {
+ return expr.Rank() > 0 &&
+ UnwrapWholeSymbolOrComponentDataRef(expr) != nullptr;
+}
+
// Check if any of the symbols part of the lhs or rhs expression has a CUDA
// device attribute.
template <typename A, typename B>
@@ -1408,25 +1416,29 @@ inline bool IsCUDADataTransfer(const A &lhs, const B &rhs) {
return true; // Managed arrays initialization is performed on the device.
}
+ // A whole-array assignment whose right-hand side is a managed/unified
+ // variable is a synchronous data transfer that waits for previously launched
+ // kernels. Subsections are excluded: a loop assigning one section at a time
+ // would otherwise become a sequence of blocking copies.
+ bool wholeArrayTransfer{
+ IsWholeArrayDesignator(lhs) && IsWholeArrayDesignator(rhs)};
+
// Managed/unified data is host-addressable, so several assignments are
// performed on the host and need no explicit data transfer:
// - A whole-allocatable left-hand side involving managed/unified data: the
// assignment has reallocation semantics and is performed on the host.
- // - Element-wise (scalar) access to managed/unified data.
+ // - Element-wise (scalar) accesses and array-section assignments.
// - A right-hand side expression involving managed/unified data assigned into
// a host-addressable (managed/unified or host) left-hand side: evaluating
// it on the host avoids materializing a temporary.
// - A managed/unified left-hand side assigned from host-only data.
- // A whole-array assignment whose right-hand side is a managed/unified
- // variable is a synchronous data transfer that waits for previously launched
- // kernels.
if ((IsAllocatableDesignator(lhs) &&
(lhsNbManagedSymbols >= 1 || rhsNbManagedSymbols >= 1)) ||
(lhsNbManagedSymbols >= 1 && rhsNbManagedSymbols == rhsNbSymbols &&
- lhs.Rank() == 0) ||
+ !wholeArrayTransfer) ||
(lhsNbManagedSymbols == 0 && !HasCUDADeviceAttrs(lhs) &&
rhsNbManagedSymbols >= 1 && rhsNbManagedSymbols == rhsNbSymbols &&
- lhs.Rank() == 0) ||
+ !wholeArrayTransfer) ||
(rhsNbManagedSymbols >= 1 && !IsVariable(rhs) &&
(lhsNbManagedSymbols >= 1 || !HasCUDADeviceAttrs(lhs))) ||
(lhsNbManagedSymbols >= 1 && rhsNbSymbols == 0)) {
diff --git a/flang/test/Lower/CUDA/cuda-managed-assign.cuf b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
index 126169d3d40b3..ee8c0f5a2aef0 100644
--- a/flang/test/Lower/CUDA/cuda-managed-assign.cuf
+++ b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
@@ -1,10 +1,11 @@
! RUN: bbc -emit-hlfir -fcuda %s -o - | FileCheck %s
-! A whole-array assignment whose right-hand side is a managed variable or a
-! managed function result is a synchronous data transfer (matching CUDA Fortran
-! assignment-statement semantics). Element-wise (scalar) accesses and right-hand
-! side expressions involving managed data are performed on the host and need no
-! transfer.
+! An assignment between whole managed arrays, or from a managed function result,
+! is a synchronous data transfer (matching CUDA Fortran assignment-statement
+! semantics). Managed data is host-addressable, so element-wise accesses,
+! array-section assignments, and right-hand side expressions involving managed
+! data are performed on the host: a loop assigning one section at a time would
+! otherwise become a sequence of blocking copies.
module mfr
contains
@@ -16,6 +17,8 @@ contains
end function
end module
+! Whole-array assignments are data transfers.
+
subroutine managed_array_assign()
integer(4), managed :: ma(16), mb(16)
integer(4) :: ha(16)
@@ -29,6 +32,34 @@ end subroutine
! CHECK: cuf.data_transfer %{{.*}} to %{{.*}} {hasManagedOrUnifedSymbols, transfer_kind = #cuf.cuda_transfer<host_device>}
! CHECK: cuf.data_transfer %{{.*}} to %{{.*}} {hasManagedOrUnifedSymbols, transfer_kind = #cuf.cuda_transfer<device_host>}
+subroutine managed_array_assign_2d()
+ integer(4), managed :: ma(4,16), mb(4,16)
+ integer(4) :: ha(4,16)
+ ma = mb
+ ha = ma
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_array_assign_2d()
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+! A whole array component of a scalar base is a whole-array designator.
+subroutine managed_component_assign()
+ type :: t
+ integer(4) :: m(16)
+ end type
+ type(t), managed :: ma, mb
+ type(t) :: ha
+ ma%m = mb%m
+ ha%m = mb%m
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_component_assign()
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+! Assignments performed on the host.
+
! A right-hand side expression involving managed data is evaluated on the host
! to avoid materializing a temporary, so no data transfer is generated.
subroutine managed_expr_assign()
@@ -53,13 +84,162 @@ end subroutine
! CHECK-LABEL: func.func @_QPmanaged_scalar_access
! CHECK-NOT: cuf.data_transfer
+subroutine managed_element_assign(n)
+ integer :: n, i
+ integer(4), managed :: ma(16), mb(16)
+ integer(4) :: ha(16)
+ do i = 1, n
+ ma(i) = mb(i) ! managed element = managed element
+ ha(i) = mb(i) ! host element = managed element
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_element_assign
+! CHECK-NOT: cuf.data_transfer
+
+subroutine managed_section_assign(n)
+ integer :: n, i
+ integer(4), managed :: ma(4,16), mb(4,16)
+ integer(4) :: ha(4,16)
+ do i = 1, n
+ ma(:,i) = mb(:,i) ! managed section = managed section
+ ha(:,i) = mb(:,i) ! host section = managed section
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_section_assign
+! CHECK-NOT: cuf.data_transfer
+
+subroutine managed_section_assign_1d(n)
+ integer :: n
+ integer(4), managed :: ma(16), mb(16)
+ integer(4) :: ha(16)
+ ma(1:8) = mb(1:8) ! contiguous section
+ ma(1:16:2) = mb(1:16:2) ! strided section
+ ha(1:8) = mb(1:8)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_section_assign_1d
+! CHECK-NOT: cuf.data_transfer
+
+! The whole-array test is syntactic: a section that happens to cover the whole
+! array is still a section.
+subroutine managed_full_slice_assign()
+ integer(4), managed :: ma(4,16), mb(4,16)
+ integer(4) :: ha(4,16)
+ ma(:,:) = mb(:,:)
+ ha(:,:) = mb(:,:)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_full_slice_assign()
+! CHECK-NOT: cuf.data_transfer
+
+subroutine managed_component_section_assign()
+ type :: t
+ integer(4) :: m(16)
+ end type
+ type(t), managed :: ma, mb
+ ma%m(1:8) = mb%m(1:8)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_component_section_assign()
+! CHECK-NOT: cuf.data_transfer
+
+! Both sides must be whole arrays for the assignment to be a transfer.
+subroutine managed_mixed_whole_section()
+ integer(4), managed :: ma(4), mb(4,16)
+ integer(4) :: ha(4)
+ ma = mb(:,1) ! whole = section
+ mb(:,1) = ma ! section = whole
+ ha = mb(:,1)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_mixed_whole_section()
+! CHECK-NOT: cuf.data_transfer
+
+! A whole-allocatable left-hand side has reallocation semantics and is performed
+! on the host.
+subroutine managed_alloc_assign(n)
+ integer :: n
+ integer(4), allocatable, managed :: ma(:), mb(:)
+ allocate(ma(n), mb(n))
+ ma = mb
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_alloc_assign
+! CHECK-NOT: cuf.data_transfer
+
+! The unified attribute follows the same rules as managed.
+subroutine unified_section_assign(n)
+ integer :: n, i
+ integer(4), unified :: ua(4,16), ub(4,16)
+ integer(4) :: ha(4,16)
+ ua = ub ! whole array: data transfer
+ do i = 1, n
+ ua(:,i) = ub(:,i) ! sections: performed on the host
+ ha(:,i) = ub(:,i)
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPunified_section_assign
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK-NOT: cuf.data_transfer
+
+! Cases that remain data transfers.
+
+! Device memory is not host-addressable, so sections and elements involving it
+! are still transferred.
+subroutine managed_device_section(n)
+ integer :: n, i
+ integer(4), managed :: ma(4,16)
+ integer(4), device :: da(4,16)
+ integer(4) :: ha(4,16)
+ do i = 1, n
+ ma(:,i) = da(:,i) ! managed section = device section
+ ha(:,i) = da(:,i) ! host section = device section
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_device_section
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+! Initialization of managed data from a host-only right-hand side is performed
+! on the device, whether the left-hand side is a whole array or a section.
+subroutine managed_init(n)
+ integer :: n, i
+ integer(4), managed :: ma(4,16)
+ integer(4) :: ha(4,16)
+ ma = 0
+ do i = 1, n
+ ma(:,i) = ha(:,i)
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_init
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+
! A managed function result may be produced by an asynchronous kernel, so
-! consuming it in an assignment is a synchronizing data transfer.
+! consuming it in an assignment is a synchronizing data transfer, independently
+! of the shape of the left-hand side.
subroutine managed_func_result()
use mfr
integer(4), managed :: b(4)
+ integer(4) :: hb(4)
b = fr(4)
+ hb = fr(4)
end subroutine
! CHECK-LABEL: func.func @_QPmanaged_func_result()
-! CHECK: cuf.data_transfer
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+subroutine managed_func_result_section()
+ use mfr
+ integer(4), managed :: b(4)
+ b(1:2) = fr(2)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_func_result_section()
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
>From a5032185c4faca28b5f5c49a5b8bf78b147d76f8 Mon Sep 17 00:00:00 2001
From: Zhen Wang <zhenw at nvidia.com>
Date: Tue, 28 Jul 2026 13:50:26 -0700
Subject: [PATCH 2/2] Align managed data transfers with CUDA Fortran Guide
3.4.1
---
flang/include/flang/Evaluate/tools.h | 28 +--
flang/test/Lower/CUDA/cuda-data-transfer.cuf | 4 +-
flang/test/Lower/CUDA/cuda-managed-assign.cuf | 199 +++++++++++-------
3 files changed, 141 insertions(+), 90 deletions(-)
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 28cf9b5cc3c82..9c9abfc5d3e94 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1411,23 +1411,25 @@ inline bool IsCUDADataTransfer(const A &lhs, const B &rhs) {
if (HasNonAllocatableModuleCUDAManagedSymbols(lhs))
return false;
- if (lhsNbManagedSymbols >= 1 && lhs.Rank() > 0 && rhsNbSymbols == 0 &&
- rhsNbManagedSymbols == 0 && (IsVariable(rhs) || IsConstantExpr(rhs))) {
+ if (lhsNbManagedSymbols >= 1 && IsWholeArrayDesignator(lhs) &&
+ rhsNbSymbols == 0 && rhsNbManagedSymbols == 0 &&
+ (IsVariable(rhs) || IsConstantExpr(rhs))) {
return true; // Managed arrays initialization is performed on the device.
}
- // A whole-array assignment whose right-hand side is a managed/unified
- // variable is a synchronous data transfer that waits for previously launched
- // kernels. Subsections are excluded: a loop assigning one section at a time
- // would otherwise become a sequence of blocking copies.
- bool wholeArrayTransfer{
- IsWholeArrayDesignator(lhs) && IsWholeArrayDesignator(rhs)};
+ // Per CUDA Fortran Programming Guide 3.4.1, an assignment involving
+ // managed/unified data is a synchronous copy when the managed operand is a
+ // whole variable or array, and is performed by host code when it is an array
+ // section: managed data is host-addressable, and a loop assigning one section
+ // at a time would otherwise become a sequence of blocking copies. Sections
+ // are still copied when the other side is device data.
+ bool wholeLhs{IsWholeArrayDesignator(lhs)};
+ bool wholeRhs{IsWholeArrayDesignator(rhs)};
- // Managed/unified data is host-addressable, so several assignments are
- // performed on the host and need no explicit data transfer:
+ // Assignments that are performed on the host and need no data transfer:
// - A whole-allocatable left-hand side involving managed/unified data: the
// assignment has reallocation semantics and is performed on the host.
- // - Element-wise (scalar) accesses and array-section assignments.
+ // - A managed/unified operand that is an array section or an element.
// - A right-hand side expression involving managed/unified data assigned into
// a host-addressable (managed/unified or host) left-hand side: evaluating
// it on the host avoids materializing a temporary.
@@ -1435,10 +1437,10 @@ inline bool IsCUDADataTransfer(const A &lhs, const B &rhs) {
if ((IsAllocatableDesignator(lhs) &&
(lhsNbManagedSymbols >= 1 || rhsNbManagedSymbols >= 1)) ||
(lhsNbManagedSymbols >= 1 && rhsNbManagedSymbols == rhsNbSymbols &&
- !wholeArrayTransfer) ||
+ !(wholeLhs && wholeRhs)) ||
(lhsNbManagedSymbols == 0 && !HasCUDADeviceAttrs(lhs) &&
rhsNbManagedSymbols >= 1 && rhsNbManagedSymbols == rhsNbSymbols &&
- !wholeArrayTransfer) ||
+ !wholeRhs) ||
(rhsNbManagedSymbols >= 1 && !IsVariable(rhs) &&
(lhsNbManagedSymbols >= 1 || !HasCUDADeviceAttrs(lhs))) ||
(lhsNbManagedSymbols >= 1 && rhsNbSymbols == 0)) {
diff --git a/flang/test/Lower/CUDA/cuda-data-transfer.cuf b/flang/test/Lower/CUDA/cuda-data-transfer.cuf
index 54cf439e66d9c..c6a47f227e531 100644
--- a/flang/test/Lower/CUDA/cuda-data-transfer.cuf
+++ b/flang/test/Lower/CUDA/cuda-data-transfer.cuf
@@ -634,6 +634,8 @@ end subroutine
! CHECK-LABEL: func.func @_QPsub33
! CHECK-NOT: cuf.data_transfer
+! A managed array section is assigned on the host: only a whole managed array is
+! initialized on the device (CUDA Fortran Programming Guide 3.4.1).
subroutine sub34(n)
integer :: n
real(2), managed, allocatable :: dx(:)
@@ -642,7 +644,7 @@ subroutine sub34(n)
end subroutine
! CHECK-LABEL: func.func @_QPsub34
-! CHECK: cuf.data_transfer %{{.*}} to %{{.*}} {hasManagedOrUnifedSymbols, transfer_kind = #cuf.cuda_transfer<host_device>} : f16, !fir.box<!fir.array<?xf16>>
+! CHECK-NOT: cuf.data_transfer
module managed_mod
integer, managed :: marray(10)
diff --git a/flang/test/Lower/CUDA/cuda-managed-assign.cuf b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
index ee8c0f5a2aef0..227a8e1445bd5 100644
--- a/flang/test/Lower/CUDA/cuda-managed-assign.cuf
+++ b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
@@ -1,11 +1,11 @@
! RUN: bbc -emit-hlfir -fcuda %s -o - | FileCheck %s
-! An assignment between whole managed arrays, or from a managed function result,
-! is a synchronous data transfer (matching CUDA Fortran assignment-statement
-! semantics). Managed data is host-addressable, so element-wise accesses,
-! array-section assignments, and right-hand side expressions involving managed
-! data are performed on the host: a loop assigning one section at a time would
-! otherwise become a sequence of blocking copies.
+! CUDA Fortran Programming Guide 3.4.1: an assignment involving managed data is a
+! synchronous copy when the managed operand is a whole variable or array, and is
+! performed by host code when it is an array section. Sections are still copied
+! when the other side is device data, which is not host-addressable. A managed
+! function result may be produced by an asynchronous kernel, so consuming it is
+! also a synchronizing copy.
module mfr
contains
@@ -17,13 +17,13 @@ contains
end function
end module
-! Whole-array assignments are data transfers.
+! Assignments that are data transfers.
subroutine managed_array_assign()
integer(4), managed :: ma(16), mb(16)
integer(4) :: ha(16)
ma = mb ! managed = managed
- ma = ha ! managed = host
+ ma = ha ! managed = host array
ha = ma ! host = managed
end subroutine
@@ -58,10 +58,86 @@ end subroutine
! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+! A whole managed array is copied from a conforming constant, host scalar, host
+! array or host array section.
+subroutine managed_whole_from_host(hs)
+ integer(4) :: hs
+ integer(4), managed :: ma(4)
+ integer(4) :: hb(4,16)
+ ma = 0
+ ma = hs
+ ma = hb(:,1)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_whole_from_host
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
+
+! Device memory is not host-addressable, so an assignment between managed and
+! device data is a copy in both directions, including for sections.
+subroutine managed_device_transfer(n)
+ integer :: n, i
+ integer(4), managed :: ma(4,16)
+ integer(4), device :: da(4,16)
+ integer(4) :: ha(4,16)
+ ma = da ! whole managed = whole device
+ da = ma ! whole device = whole managed
+ do i = 1, n
+ ma(:,i) = da(:,i) ! managed section = device section
+ da(:,i) = ma(:,i) ! device section = managed section
+ ha(:,i) = da(:,i) ! host section = device section
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_device_transfer
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+! The rule keys on the managed operand: a host section may be the target of a
+! whole managed array copy.
+subroutine host_section_from_managed(n)
+ integer :: n, i
+ integer(4), managed :: mb(4)
+ integer(4) :: ha(4,16)
+ do i = 1, n
+ ha(:,i) = mb
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPhost_section_from_managed
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+! A managed function result is a synchronizing copy, independently of the shape of
+! the left-hand side.
+subroutine managed_func_result()
+ use mfr
+ integer(4), managed :: b(4)
+ integer(4) :: hb(4)
+ b = fr(4)
+ hb = fr(4)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_func_result()
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
+
+subroutine managed_func_result_section()
+ use mfr
+ integer(4), managed :: b(4)
+ b(1:2) = fr(2)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_func_result_section()
+! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
+
! Assignments performed on the host.
-! A right-hand side expression involving managed data is evaluated on the host
-! to avoid materializing a temporary, so no data transfer is generated.
+! A right-hand side expression involving managed data is evaluated on the host to
+! avoid materializing a temporary.
subroutine managed_expr_assign()
integer(4), managed :: ma(16), mb(16)
integer(4) :: ha(16)
@@ -72,12 +148,24 @@ end subroutine
! CHECK-LABEL: func.func @_QPmanaged_expr_assign()
! CHECK-NOT: cuf.data_transfer
+! Scalar and element-wise accesses of managed data are performed on the host.
+subroutine managed_scalar_assign()
+ integer(4), managed :: ms, mt
+ integer(4) :: hs
+ ms = mt
+ hs = ms
+ ms = hs
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_scalar_assign()
+! CHECK-NOT: cuf.data_transfer
+
subroutine managed_scalar_access(n)
integer :: n, i
integer(4), managed :: ma(16)
integer(4) :: ha(16)
do i = 1, n
- ha(i) = ma(i) + 1 ! element-wise access is performed on the host
+ ha(i) = ma(i) + 1
end do
end subroutine
@@ -97,6 +185,26 @@ end subroutine
! CHECK-LABEL: func.func @_QPmanaged_element_assign
! CHECK-NOT: cuf.data_transfer
+! A managed array section is assigned on the host, whatever the host right-hand
+! side is.
+subroutine managed_section_from_host(n, hs)
+ integer :: n, i
+ integer(4) :: hs
+ integer(4), managed :: mb(4,16)
+ integer(4) :: ha(4), hb(4,16)
+ do i = 1, n
+ mb(:,i) = 0
+ mb(:,i) = hs
+ mb(:,i) = ha
+ mb(:,i) = hb(:,i)
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_section_from_host
+! CHECK-NOT: cuf.data_transfer
+
+! A managed array section on either side is assigned on the host: a loop assigning
+! one section at a time would otherwise become a sequence of blocking copies.
subroutine managed_section_assign(n)
integer :: n, i
integer(4), managed :: ma(4,16), mb(4,16)
@@ -110,8 +218,7 @@ end subroutine
! CHECK-LABEL: func.func @_QPmanaged_section_assign
! CHECK-NOT: cuf.data_transfer
-subroutine managed_section_assign_1d(n)
- integer :: n
+subroutine managed_section_assign_1d()
integer(4), managed :: ma(16), mb(16)
integer(4) :: ha(16)
ma(1:8) = mb(1:8) ! contiguous section
@@ -145,13 +252,12 @@ end subroutine
! CHECK-LABEL: func.func @_QPmanaged_component_section_assign()
! CHECK-NOT: cuf.data_transfer
-! Both sides must be whole arrays for the assignment to be a transfer.
subroutine managed_mixed_whole_section()
integer(4), managed :: ma(4), mb(4,16)
integer(4) :: ha(4)
- ma = mb(:,1) ! whole = section
- mb(:,1) = ma ! section = whole
- ha = mb(:,1)
+ ma = mb(:,1) ! whole managed = managed section
+ mb(:,1) = ma ! managed section = whole managed
+ ha = mb(:,1) ! whole host = managed section
end subroutine
! CHECK-LABEL: func.func @_QPmanaged_mixed_whole_section()
@@ -184,62 +290,3 @@ end subroutine
! CHECK-LABEL: func.func @_QPunified_section_assign
! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
! CHECK-NOT: cuf.data_transfer
-
-! Cases that remain data transfers.
-
-! Device memory is not host-addressable, so sections and elements involving it
-! are still transferred.
-subroutine managed_device_section(n)
- integer :: n, i
- integer(4), managed :: ma(4,16)
- integer(4), device :: da(4,16)
- integer(4) :: ha(4,16)
- do i = 1, n
- ma(:,i) = da(:,i) ! managed section = device section
- ha(:,i) = da(:,i) ! host section = device section
- end do
-end subroutine
-
-! CHECK-LABEL: func.func @_QPmanaged_device_section
-! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
-! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
-
-! Initialization of managed data from a host-only right-hand side is performed
-! on the device, whether the left-hand side is a whole array or a section.
-subroutine managed_init(n)
- integer :: n, i
- integer(4), managed :: ma(4,16)
- integer(4) :: ha(4,16)
- ma = 0
- do i = 1, n
- ma(:,i) = ha(:,i)
- end do
-end subroutine
-
-! CHECK-LABEL: func.func @_QPmanaged_init
-! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
-! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<host_device>
-
-! A managed function result may be produced by an asynchronous kernel, so
-! consuming it in an assignment is a synchronizing data transfer, independently
-! of the shape of the left-hand side.
-subroutine managed_func_result()
- use mfr
- integer(4), managed :: b(4)
- integer(4) :: hb(4)
- b = fr(4)
- hb = fr(4)
-end subroutine
-
-! CHECK-LABEL: func.func @_QPmanaged_func_result()
-! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
-! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_host>
-
-subroutine managed_func_result_section()
- use mfr
- integer(4), managed :: b(4)
- b(1:2) = fr(2)
-end subroutine
-
-! CHECK-LABEL: func.func @_QPmanaged_func_result_section()
-! CHECK: cuf.data_transfer {{.*}}#cuf.cuda_transfer<device_device>
More information about the flang-commits
mailing list