[flang-commits] [flang] [flang][CUDA] Preserve data attrs on assignment RHS (PR #207849)
Andre Kuhlenschmidt via flang-commits
flang-commits at lists.llvm.org
Thu Jul 23 02:49:33 PDT 2026
https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/207849
>From 32b5d37cfc3aacd35cf2fa09711736cac13d0bd3 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 2 Jul 2026 17:49:01 -0700
Subject: [PATCH 1/5] [flang][CUDA] Preserve data attrs on assignment RHS
Treat a parenthesized variable as a source of CUDA data attributes when checking dummy/actual argument compatibility. Defined assignment parenthesizes non-VALUE RHS actuals to preserve value semantics, but the CUDA address space of the referenced data is unchanged by that materialization.
Add CUDA semantics coverage for a device RHS selected through defined assignment.
---
flang/lib/Semantics/check-call.cpp | 35 ++++++++++++++++++-
.../CUDA/cuf-defined-assignment-rhs.cuf | 34 ++++++++++++++++++
2 files changed, 68 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 0680a48bac2be..0bad9e378e2f4 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -27,6 +27,37 @@ namespace characteristics = Fortran::evaluate::characteristics;
namespace Fortran::semantics {
+template <typename A> static bool IsParenthesizedVariable(const A &) {
+ return false;
+}
+
+template <typename T>
+static bool IsParenthesizedVariable(const evaluate::Expr<T> &expr) {
+ if (const auto *parens{
+ evaluate::UnwrapExpr<evaluate::Parentheses<T>>(expr)}) {
+ return evaluate::IsVariable(parens->left());
+ }
+ return false;
+}
+
+template <evaluate::TypeCategory CAT>
+static bool IsParenthesizedVariable(
+ const evaluate::Expr<evaluate::SomeKind<CAT>> &expr) {
+ return common::visit(
+ [](const auto &x) { return IsParenthesizedVariable(x); }, expr.u);
+}
+
+static bool IsParenthesizedVariable(
+ const evaluate::Expr<evaluate::SomeType> &expr) {
+ return common::visit(
+ [](const auto &x) { return IsParenthesizedVariable(x); }, expr.u);
+}
+
+static bool IsVariableOrParenthesizedVariable(
+ const evaluate::Expr<evaluate::SomeType> &expr) {
+ return evaluate::IsVariable(expr) || IsParenthesizedVariable(expr);
+}
+
void CheckImplicitInterfaceArgKeywords(
const evaluate::ActualArgument &arg, parser::ContextualMessages &messages) {
auto restorer{
@@ -1131,7 +1162,9 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
!FindOpenACCConstructContaining(scope)) {
std::optional<common::CUDADataAttr> actualDataAttr, dummyDataAttr;
// For a%b%c, the last symbol with a CUDA data attribute wins
- if (actualIsVariable) {
+ // Parentheses here mean expression/copy semantics, but CUDA address space
+ // attributes remain stable when a variable is materialized as a copy.
+ if (IsVariableOrParenthesizedVariable(actual)) {
for (const Symbol &s : evaluate::GetSymbolVector(actual)) {
if (const auto *object{s.detailsIf<ObjectEntityDetails>()}) {
if (auto cudaAttr{object->cudaDataAttr()}) {
diff --git a/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf b/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
new file mode 100644
index 0000000000000..bd0ab5f7c3068
--- /dev/null
+++ b/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
@@ -0,0 +1,34 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1
+
+module m
+ type box
+ integer :: n
+ end type
+
+ interface assignment(=)
+ module procedure assign_host
+ module procedure assign_device
+ end interface
+
+contains
+ subroutine assign_host(lhs, rhs)
+ type(box), intent(inout) :: lhs
+ real, intent(in) :: rhs(:)
+ end subroutine
+
+ subroutine assign_device(lhs, rhs)
+ type(box), intent(inout) :: lhs
+ real, device, intent(in) :: rhs(:)
+ end subroutine
+end module
+
+subroutine test
+ use m
+ type(box) :: lhs
+ real :: host(4)
+ real, device, allocatable :: dev(:)
+ allocate(dev(4))
+
+ lhs = host
+ lhs = dev
+end subroutine
>From 9df59babaff8137eecce589d3d58c22fbb91844b Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 13 Jul 2026 16:38:17 -0700
Subject: [PATCH 2/5] [flang][cuda] Preserve device RHS refs in defined
assignment
Defined assignment normally parenthesizes the RHS actual to model copy semantics. For host-side CUDA assignments from device data, the assignment body performs the value transfer with a CUDA memcpy, so preserving the device variable reference keeps the CUDA data attribute available while still producing value-like behavior.
Do not parenthesize defined-assignment RHS actuals when the actual is a DEVICE variable or has the VALUE attribute. Keep source-parenthesized actuals as expressions for normal argument checking, and add coverage for the host-side device-to-component assignment reproducer.
---
flang/lib/Semantics/check-call.cpp | 35 +--------
flang/lib/Semantics/expression.cpp | 23 ++++--
.../CUDA/cuf-defined-assignment-rhs.cuf | 72 ++++++++++++++++++-
3 files changed, 90 insertions(+), 40 deletions(-)
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 0bad9e378e2f4..0680a48bac2be 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -27,37 +27,6 @@ namespace characteristics = Fortran::evaluate::characteristics;
namespace Fortran::semantics {
-template <typename A> static bool IsParenthesizedVariable(const A &) {
- return false;
-}
-
-template <typename T>
-static bool IsParenthesizedVariable(const evaluate::Expr<T> &expr) {
- if (const auto *parens{
- evaluate::UnwrapExpr<evaluate::Parentheses<T>>(expr)}) {
- return evaluate::IsVariable(parens->left());
- }
- return false;
-}
-
-template <evaluate::TypeCategory CAT>
-static bool IsParenthesizedVariable(
- const evaluate::Expr<evaluate::SomeKind<CAT>> &expr) {
- return common::visit(
- [](const auto &x) { return IsParenthesizedVariable(x); }, expr.u);
-}
-
-static bool IsParenthesizedVariable(
- const evaluate::Expr<evaluate::SomeType> &expr) {
- return common::visit(
- [](const auto &x) { return IsParenthesizedVariable(x); }, expr.u);
-}
-
-static bool IsVariableOrParenthesizedVariable(
- const evaluate::Expr<evaluate::SomeType> &expr) {
- return evaluate::IsVariable(expr) || IsParenthesizedVariable(expr);
-}
-
void CheckImplicitInterfaceArgKeywords(
const evaluate::ActualArgument &arg, parser::ContextualMessages &messages) {
auto restorer{
@@ -1162,9 +1131,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
!FindOpenACCConstructContaining(scope)) {
std::optional<common::CUDADataAttr> actualDataAttr, dummyDataAttr;
// For a%b%c, the last symbol with a CUDA data attribute wins
- // Parentheses here mean expression/copy semantics, but CUDA address space
- // attributes remain stable when a variable is materialized as a copy.
- if (IsVariableOrParenthesizedVariable(actual)) {
+ if (actualIsVariable) {
for (const Symbol &s : evaluate::GetSymbolVector(actual)) {
if (const auto *object{s.detailsIf<ObjectEntityDetails>()}) {
if (auto cudaAttr{object->cudaDataAttr()}) {
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index fc57cc43e981c..e91c79e20ac94 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -5673,17 +5673,30 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc(
}
ActualArguments actualsCopy{actuals_};
// Ensure that the RHS argument is not passed as a variable unless
- // the dummy argument has the VALUE attribute.
- if (evaluate::IsVariable(actualsCopy.at(1).value().UnwrapExpr())) {
+ // the dummy argument has the VALUE attribute, or the actual's own attributes
+ // already require reference semantics that must be preserved.
+ if (const auto *rhsExpr{actualsCopy.at(1).value().UnwrapExpr()};
+ evaluate::IsVariable(rhsExpr)) {
auto chars{evaluate::characteristics::Procedure::Characterize(
*proc, context_.GetFoldingContext())};
const auto *rhsDummy{chars && chars->dummyArguments.size() == 2
? std::get_if<evaluate::characteristics::DummyDataObject>(
&chars->dummyArguments.at(1).u)
: nullptr};
- if (!rhsDummy ||
- !rhsDummy->attrs.test(
- evaluate::characteristics::DummyDataObject::Attr::Value)) {
+ std::optional<common::CUDADataAttr> rhsDataAttr;
+ for (const Symbol &symbol : evaluate::GetSymbolVector(*rhsExpr)) {
+ if (auto cudaAttr{GetCUDADataAttr(&symbol)}) {
+ rhsDataAttr = *cudaAttr;
+ }
+ }
+ const Symbol *rhsFirstSymbol{evaluate::GetFirstSymbol(*rhsExpr)};
+ const bool preserveActualReference{
+ (rhsDataAttr && *rhsDataAttr == common::CUDADataAttr::Device) ||
+ (rhsFirstSymbol && IsValue(*rhsFirstSymbol))};
+ if (!preserveActualReference &&
+ (!rhsDummy ||
+ !rhsDummy->attrs.test(
+ evaluate::characteristics::DummyDataObject::Attr::Value))) {
actualsCopy.at(1).value().Parenthesize();
}
}
diff --git a/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf b/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
index bd0ab5f7c3068..5836a33aa9dea 100644
--- a/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
+++ b/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
@@ -1,4 +1,6 @@
! RUN: %python %S/../test_errors.py %s %flang_fc1
+! RUN: rm -rf %t && mkdir -p %t && %flang_fc1 -module-dir %t \
+! RUN: -fdebug-unparse %s 2>&1 | FileCheck %s
module m
type box
@@ -8,6 +10,7 @@ module m
interface assignment(=)
module procedure assign_host
module procedure assign_device
+ module procedure assign_scalar
end interface
contains
@@ -20,15 +23,82 @@ contains
type(box), intent(inout) :: lhs
real, device, intent(in) :: rhs(:)
end subroutine
+
+ subroutine assign_scalar(lhs, rhs)
+ type(box), intent(inout) :: lhs
+ real, intent(in) :: rhs
+ end subroutine
end module
-subroutine test
+subroutine test_defined_assignment
use m
type(box) :: lhs
real :: host(4)
real, device, allocatable :: dev(:)
allocate(dev(4))
+ ! CHECK: CALL assign_host(lhs,(host))
lhs = host
+
+ ! CHECK: CALL assign_device(lhs,dev)
lhs = dev
end subroutine
+
+module issue_reproducer
+ type t
+ real :: x(100)
+ end type
+
+ interface assignment(=)
+ module procedure assign_t
+ end interface
+
+contains
+ subroutine assign_t(host_t, dev_x)
+ type(t), intent(inout) :: host_t
+ real, device, intent(in) :: dev_x(:)
+
+ host_t%x = dev_x
+ end subroutine
+end module
+
+subroutine test_issue_reproducer
+ use issue_reproducer
+ real :: y(100)
+ real, device :: x(100)
+ type(t) :: tee
+
+ x = y
+ ! CHECK: CALL assign_t(tee,x)
+ tee = x
+end subroutine
+
+subroutine test_value_actual(rhs)
+ use m
+ type(box) :: lhs
+ real, value :: rhs
+
+ ! CHECK: CALL assign_scalar(lhs,rhs)
+ lhs = rhs
+end subroutine
+
+subroutine test_host_scalar_actual(rhs)
+ use m
+ type(box) :: lhs
+ real :: rhs
+
+ ! CHECK: CALL assign_scalar(lhs,(rhs))
+ lhs = rhs
+end subroutine
+
+subroutine test_explicit_parenthesized_actuals
+ use m
+ type(box) :: lhs
+ real :: host(4)
+ real, device, allocatable :: dev(:)
+ allocate(dev(4))
+
+ call assign_host(lhs, host)
+ call assign_host(lhs, (host))
+ call assign_device(lhs, dev)
+end subroutine
>From 5096133e44643d14a674165087964d337e556ad8 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 14 Jul 2026 11:46:06 -0700
Subject: [PATCH 3/5] [flang][cuda] Note device RHS assignment limits
Document that preserving DEVICE RHS references in defined assignment may need to be limited to device-to-host transfers or unambiguous host-code RHS references.
---
flang/lib/Semantics/expression.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index e91c79e20ac94..e64eda130053e 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -5690,6 +5690,8 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc(
}
}
const Symbol *rhsFirstSymbol{evaluate::GetFirstSymbol(*rhsExpr)};
+ // TODO: This DEVICE exception may need to be limited to device-to-host
+ // transfers or to RHS references that appear in unambiguous host code.
const bool preserveActualReference{
(rhsDataAttr && *rhsDataAttr == common::CUDADataAttr::Device) ||
(rhsFirstSymbol && IsValue(*rhsFirstSymbol))};
>From 11d4771988d2a0c451e97d2774aeb9de09f65d7b Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 14 Jul 2026 12:01:26 -0700
Subject: [PATCH 4/5] [flang][cuda] Test parenthesized device RHS assignment
Add coverage showing that a source-parenthesized DEVICE variable on the RHS is treated as an expression and does not take the defined-assignment DEVICE-variable reference exception.
---
flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf b/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
index 5836a33aa9dea..a1d11256b1321 100644
--- a/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
+++ b/flang/test/Semantics/CUDA/cuf-defined-assignment-rhs.cuf
@@ -42,6 +42,11 @@ subroutine test_defined_assignment
! CHECK: CALL assign_device(lhs,dev)
lhs = dev
+
+ ! A source-parenthesized DEVICE variable is an expression, so it does not
+ ! trigger the DEVICE-variable RHS exception.
+ ! CHECK: CALL assign_host(lhs,(dev))
+ lhs = (dev)
end subroutine
module issue_reproducer
>From 7fc92be3dfb07861cf85490f82e17f2eb7f7c4fb Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 23 Jul 2026 02:47:40 -0700
Subject: [PATCH 5/5] [flang][cuda] Avoid HLFIR copies for device RHS data
When lowering hlfir.region_assign for user-defined assignment, avoid materializing a host-side RHS value copy when the yielded RHS is CUDA device data. Preserve the address instead so the downstream CUDA transfer path can handle the device reference.
Add FIR coverage for host RHS copies, device RHS variables and sections, and parenthesized device RHS expressions.
---
.../LowerHLFIROrderedAssignments.cpp | 73 ++++++++++++
.../order_assignments/cuf-device-rhs.fir | 105 ++++++++++++++++++
2 files changed, 178 insertions(+)
create mode 100644 flang/test/HLFIR/order_assignments/cuf-device-rhs.fir
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp
index dcc40c7b25c5c..76268d5af425f 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp
@@ -23,13 +23,17 @@
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/TemporaryStorage.h"
#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
+#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
#include "flang/Optimizer/HLFIR/Passes.h"
#include "mlir/Analysis/Liveness.h"
#include "mlir/IR/Dominance.h"
#include "mlir/IR/IRMapping.h"
+#include "mlir/Interfaces/FunctionInterfaces.h"
#include "mlir/Transforms/DialectConversion.h"
#include "mlir/Transforms/RegionUtils.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/Debug.h"
#include <unordered_set>
@@ -687,6 +691,59 @@ static hlfir::YieldOp getYield(mlir::Region ®ion) {
return yield;
}
+static bool isCUDADeviceDataAttr(mlir::Attribute attr) {
+ if (auto dataAttr = mlir::dyn_cast_if_present<cuf::DataAttributeAttr>(attr))
+ return dataAttr.getValue() == cuf::DataAttribute::Device;
+ return false;
+}
+
+static bool hasCUDADeviceDataAttr(mlir::BlockArgument blockArg) {
+ mlir::Block *owner = blockArg.getOwner();
+ if (!owner)
+ return false;
+ mlir::Operation *parentOp = owner->getParentOp();
+ if (!parentOp)
+ return false;
+ auto funcLike = mlir::dyn_cast<mlir::FunctionOpInterface>(parentOp);
+ if (!funcLike || blockArg.getArgNumber() >= funcLike.getNumArguments())
+ return false;
+ unsigned argNumber = blockArg.getArgNumber();
+ return isCUDADeviceDataAttr(
+ funcLike.getArgAttr(argNumber, cuf::getDataAttrName())) ||
+ isCUDADeviceDataAttr(
+ funcLike.getArgAttr(argNumber, cuf::dataAttrName));
+}
+
+static bool isCUDADeviceData(mlir::Value value) {
+ llvm::SmallPtrSet<void *, 8> visited;
+ mlir::Value current = value;
+ while (current) {
+ if (!visited.insert(current.getAsOpaquePointer()).second)
+ return false;
+
+ if (auto blockArg = mlir::dyn_cast<mlir::BlockArgument>(current))
+ return hasCUDADeviceDataAttr(blockArg);
+
+ mlir::Operation *defOp = current.getDefiningOp();
+ if (!defOp)
+ return false;
+
+ if (cuf::hasDataAttr(defOp, cuf::DataAttribute::Device))
+ return true;
+
+ if (auto result = mlir::dyn_cast<mlir::OpResult>(current))
+ if (auto viewOp =
+ mlir::dyn_cast<fir::FortranObjectViewOpInterface>(defOp))
+ if (mlir::Value source = viewOp.getViewSource(result)) {
+ current = source;
+ continue;
+ }
+
+ return false;
+ }
+ return false;
+}
+
OrderedAssignmentRewriter::ValueAndCleanUp
OrderedAssignmentRewriter::generateYieldedEntity(
mlir::Region ®ion, std::optional<mlir::Type> castToType) {
@@ -1066,6 +1123,14 @@ getAssignIfLeftHandSideRegion(mlir::Region ®ion) {
return nullptr;
}
+static hlfir::RegionAssignOp
+getAssignIfRightHandSideRegion(mlir::Region ®ion) {
+ auto assign = mlir::dyn_cast<hlfir::RegionAssignOp>(region.getParentOp());
+ if (assign && (&assign.getRhsRegion() == ®ion))
+ return assign;
+ return nullptr;
+}
+
static bool isPointerAssignmentRHS(mlir::Region ®ion) {
auto assign = mlir::dyn_cast<hlfir::RegionAssignOp>(region.getParentOp());
return assign && assign.isPointerAssignment() &&
@@ -1199,6 +1264,14 @@ void OrderedAssignmentRewriter::generateSaveEntity(
"rhs cannot be used in the loop nest where it is saved");
return saveNonVectorSubscriptedAddress(savedEntity);
}
+ if (hlfir::RegionAssignOp regionAssignOp =
+ getAssignIfRightHandSideRegion(region);
+ regionAssignOp && !regionAssignOp.getUserDefinedAssignment().empty() &&
+ isCUDADeviceData(getYield(region).getEntity())) {
+ // Avoid materializing a host-side value copy of CUDA device data. The
+ // address is preserved instead, matching CUDA data-transfer semantics.
+ return saveNonVectorSubscriptedAddress(savedEntity);
+ }
mlir::Location loc = region.getParentOp()->getLoc();
// Evaluate the region inside the loop nest (if any).
diff --git a/flang/test/HLFIR/order_assignments/cuf-device-rhs.fir b/flang/test/HLFIR/order_assignments/cuf-device-rhs.fir
new file mode 100644
index 0000000000000..db75c48baaa8e
--- /dev/null
+++ b/flang/test/HLFIR/order_assignments/cuf-device-rhs.fir
@@ -0,0 +1,105 @@
+// Test code generation of hlfir.region_assign with CUDA device RHS variables.
+// RUN: fir-opt %s --lower-hlfir-ordered-assignments | FileCheck %s
+
+func.func @host_rhs_still_saved(%lhs_ref: !fir.ref<!fir.array<10xf32>>,
+ %rhs_ref: !fir.ref<!fir.array<10xf32>>) {
+ %c10 = arith.constant 10 : index
+ %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+ %lhs:2 = hlfir.declare %lhs_ref(%shape) {uniq_name = "lhs"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+ %rhs:2 = hlfir.declare %rhs_ref(%shape) {uniq_name = "rhs"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+ hlfir.region_assign {
+ hlfir.yield %rhs#0 : !fir.ref<!fir.array<10xf32>>
+ } to {
+ hlfir.yield %lhs#0 : !fir.ref<!fir.array<10xf32>>
+ } user_defined_assign (%arg0: !fir.ref<!fir.array<10xf32>>) to (%arg1: !fir.ref<!fir.array<10xf32>>) {
+ fir.call @assign_array(%arg1, %arg0) : (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>) -> ()
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @host_rhs_still_saved(
+// CHECK: %[[RHS:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "rhs"}
+// CHECK: %[[EXPR:.*]] = hlfir.as_expr %[[RHS]]#0 : (!fir.ref<!fir.array<10xf32>>) -> !hlfir.expr<10xf32>
+// CHECK: %[[TMP:.*]]:3 = hlfir.associate %[[EXPR]](%{{.*}}) {uniq_name = ".tmp.assign"}
+// CHECK: fir.call @assign_array(%{{.*}}, %[[TMP]]#0)
+
+func.func @device_rhs_not_copied(%lhs_ref: !fir.ref<!fir.array<10xf32>>,
+ %rhs_ref: !fir.ref<!fir.array<10xf32>> {cuf.data_attr = #cuf.cuda<device>}) {
+ %c10 = arith.constant 10 : index
+ %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+ %lhs:2 = hlfir.declare %lhs_ref(%shape) {uniq_name = "lhs"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+ %rhs:2 = hlfir.declare %rhs_ref(%shape) {data_attr = #cuf.cuda<device>, uniq_name = "rhs"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+ hlfir.region_assign {
+ hlfir.yield %rhs#0 : !fir.ref<!fir.array<10xf32>>
+ } to {
+ hlfir.yield %lhs#0 : !fir.ref<!fir.array<10xf32>>
+ } user_defined_assign (%arg0: !fir.ref<!fir.array<10xf32>>) to (%arg1: !fir.ref<!fir.array<10xf32>>) {
+ fir.call @assign_array(%arg1, %arg0) : (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>) -> ()
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @device_rhs_not_copied(
+// CHECK: %[[LHS:.*]]:2 = hlfir.declare
+// CHECK: %[[RHS:.*]]:2 = hlfir.declare {{.*}} {data_attr = #cuf.cuda<device>
+// CHECK-NOT: hlfir.as_expr
+// CHECK-NOT: uniq_name = ".tmp.assign"
+// CHECK: fir.call @assign_array(%[[LHS]]#0, %[[RHS]]#0)
+
+func.func @device_rhs_section_not_copied(%lhs_ref: !fir.ref<!fir.array<9xf32>>,
+ %rhs_ref: !fir.ref<!fir.array<10xf32>> {cuf.data_attr = #cuf.cuda<device>}) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %c9 = arith.constant 9 : index
+ %c10 = arith.constant 10 : index
+ %lhs_shape = fir.shape %c9 : (index) -> !fir.shape<1>
+ %rhs_shape = fir.shape %c10 : (index) -> !fir.shape<1>
+ %lhs:2 = hlfir.declare %lhs_ref(%lhs_shape) {uniq_name = "lhs"} : (!fir.ref<!fir.array<9xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<9xf32>>, !fir.ref<!fir.array<9xf32>>)
+ %rhs:2 = hlfir.declare %rhs_ref(%rhs_shape) {data_attr = #cuf.cuda<device>, uniq_name = "rhs"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+ hlfir.region_assign {
+ %section = hlfir.designate %rhs#0 (%c2:%c10:%c1) shape %lhs_shape : (!fir.ref<!fir.array<10xf32>>, index, index, index, !fir.shape<1>) -> !fir.ref<!fir.array<9xf32>>
+ hlfir.yield %section : !fir.ref<!fir.array<9xf32>>
+ } to {
+ hlfir.yield %lhs#0 : !fir.ref<!fir.array<9xf32>>
+ } user_defined_assign (%arg0: !fir.ref<!fir.array<9xf32>>) to (%arg1: !fir.ref<!fir.array<9xf32>>) {
+ fir.call @assign_array9(%arg1, %arg0) : (!fir.ref<!fir.array<9xf32>>, !fir.ref<!fir.array<9xf32>>) -> ()
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @device_rhs_section_not_copied(
+// CHECK: %[[LHS:.*]]:2 = hlfir.declare
+// CHECK: %[[RHS:.*]]:2 = hlfir.declare {{.*}} {data_attr = #cuf.cuda<device>
+// CHECK: %[[SECTION:.*]] = hlfir.designate %[[RHS]]#0
+// CHECK-NOT: hlfir.as_expr
+// CHECK-NOT: uniq_name = ".tmp.assign"
+// CHECK: fir.call @assign_array9(%[[LHS]]#0, %[[SECTION]])
+
+func.func @parenthesized_device_rhs_still_saved(%lhs_ref: !fir.ref<!fir.array<10xf32>>,
+ %rhs_ref: !fir.ref<!fir.array<10xf32>> {cuf.data_attr = #cuf.cuda<device>}) {
+ %c10 = arith.constant 10 : index
+ %shape = fir.shape %c10 : (index) -> !fir.shape<1>
+ %lhs:2 = hlfir.declare %lhs_ref(%shape) {uniq_name = "lhs"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+ %rhs:2 = hlfir.declare %rhs_ref(%shape) {data_attr = #cuf.cuda<device>, uniq_name = "rhs"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
+ hlfir.region_assign {
+ %expr = hlfir.as_expr %rhs#0 : (!fir.ref<!fir.array<10xf32>>) -> !hlfir.expr<10xf32>
+ hlfir.yield %expr : !hlfir.expr<10xf32>
+ } to {
+ hlfir.yield %lhs#0 : !fir.ref<!fir.array<10xf32>>
+ } user_defined_assign (%arg0: !hlfir.expr<10xf32>) to (%arg1: !fir.ref<!fir.array<10xf32>>) {
+ %copy:3 = hlfir.associate %arg0(%shape) {uniq_name = "adapt.valuebyref"} : (!hlfir.expr<10xf32>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>, i1)
+ fir.call @assign_array(%arg1, %copy#0) : (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>) -> ()
+ hlfir.end_associate %copy#1, %copy#2 : !fir.ref<!fir.array<10xf32>>, i1
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @parenthesized_device_rhs_still_saved(
+// CHECK: %[[RHS:.*]]:2 = hlfir.declare {{.*}} {data_attr = #cuf.cuda<device>
+// CHECK: %[[EXPR:.*]] = hlfir.as_expr %[[RHS]]#0 : (!fir.ref<!fir.array<10xf32>>) -> !hlfir.expr<10xf32>
+// CHECK: %[[TMP:.*]]:3 = hlfir.associate %[[EXPR]](%{{.*}}) {uniq_name = ".tmp.assign"}
+// CHECK: %[[COPY_EXPR:.*]] = hlfir.as_expr %[[TMP]]#0 : (!fir.ref<!fir.array<10xf32>>) -> !hlfir.expr<10xf32>
+// CHECK: hlfir.associate %[[COPY_EXPR]](%{{.*}}) {uniq_name = "adapt.valuebyref"}
+
+func.func private @assign_array(!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>) -> ()
+func.func private @assign_array9(!fir.ref<!fir.array<9xf32>>, !fir.ref<!fir.array<9xf32>>) -> ()
More information about the flang-commits
mailing list