[flang-commits] [flang] [flang] Disable copy-out to INTENT(IN) args (PR #192382)
Eugene Epshteyn via flang-commits
flang-commits at lists.llvm.org
Fri Apr 17 07:18:48 PDT 2026
https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/192382
>From 56bb905526eae5b4fc44a3036e7864a11c5d31a6 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 15 Apr 2026 23:38:36 -0400
Subject: [PATCH 1/6] [flang][DRAFT]
Don't copy out to actual args that themselves happen to be INTENT(IN)
dummy args.
---
flang/lib/Evaluate/check-expression.cpp | 11 +++++++++++
flang/test/Lower/call-copy-in-out.f90 | 14 ++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index be7db0f20821d..25a6dfa799e9e 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1649,6 +1649,17 @@ std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
// Expressions are copy-in, but not copy-out.
return forCopyIn;
}
+ if (forCopyOut) {
+ // If the actual argument's base object has INTENT(IN) in the caller's
+ // context, copy-out would violate the read-only semantics of INTENT(IN).
+ if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) {
+ if (const Symbol *symbol{GetFirstSymbol(*expr)}) {
+ if (semantics::IsIntentIn(*symbol)) {
+ return false;
+ }
+ }
+ }
+ }
auto maybeContigActual{IsContiguous(*actual, fc)};
if (dummyObj) { // Explict interface
CopyInOutExplicitInterface check{fc, *actual, *dummyObj};
diff --git a/flang/test/Lower/call-copy-in-out.f90 b/flang/test/Lower/call-copy-in-out.f90
index fa2182ac11bac..2a988feeccdc2 100644
--- a/flang/test/Lower/call-copy-in-out.f90
+++ b/flang/test/Lower/call-copy-in-out.f90
@@ -84,6 +84,20 @@ subroutine bar_intent_in(x)
call bar_intent_in(x)
end subroutine
+! Test copy-out is skipped when the actual argument has INTENT(IN).
+! CHECK-LABEL: func @_QPtest_actual_arg_intent_in(
+subroutine test_actual_arg_intent_in(x)
+ real, intent(in) :: x(:)
+! CHECK: hlfir.copy_in
+! CHECK: fir.call @_QPbar
+! Note: no-op hlfir.copy_out has comma separated list of args.
+! The actual working hlfir.copy_out has "to" in it.
+! CHECK: hlfir.copy_out
+! CHECK-SAME: ,
+! CHECK: return
+ call bar(x)
+end subroutine
+
! Test copy-in/copy-out is done for intent(inout)
! CHECK-LABEL: func @_QPtest_intent_inout(
subroutine test_intent_inout(x)
>From 31561cda91bf08495f5d826752c4e27c4e92c57d Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 15 Apr 2026 23:40:03 -0400
Subject: [PATCH 2/6] clang-format
---
flang/lib/Evaluate/check-expression.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 25a6dfa799e9e..e6f3350b344be 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1653,7 +1653,7 @@ std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
// If the actual argument's base object has INTENT(IN) in the caller's
// context, copy-out would violate the read-only semantics of INTENT(IN).
if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) {
- if (const Symbol *symbol{GetFirstSymbol(*expr)}) {
+ if (const Symbol * symbol{GetFirstSymbol(*expr)}) {
if (semantics::IsIntentIn(*symbol)) {
return false;
}
>From def94a375315cecf4e351492f5726be44dff577b Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 15 Apr 2026 23:46:54 -0400
Subject: [PATCH 3/6] Revert "clang-format"
This reverts commit 31561cda91bf08495f5d826752c4e27c4e92c57d.
---
flang/lib/Evaluate/check-expression.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index e6f3350b344be..25a6dfa799e9e 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1653,7 +1653,7 @@ std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
// If the actual argument's base object has INTENT(IN) in the caller's
// context, copy-out would violate the read-only semantics of INTENT(IN).
if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) {
- if (const Symbol * symbol{GetFirstSymbol(*expr)}) {
+ if (const Symbol *symbol{GetFirstSymbol(*expr)}) {
if (semantics::IsIntentIn(*symbol)) {
return false;
}
>From cb91cb882979463916cba1e77ed67eac00dd32da Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 16 Apr 2026 00:02:37 -0400
Subject: [PATCH 4/6] tweaked the test
---
flang/test/Lower/call-copy-in-out.f90 | 1 +
1 file changed, 1 insertion(+)
diff --git a/flang/test/Lower/call-copy-in-out.f90 b/flang/test/Lower/call-copy-in-out.f90
index 2a988feeccdc2..e87ae5e294a84 100644
--- a/flang/test/Lower/call-copy-in-out.f90
+++ b/flang/test/Lower/call-copy-in-out.f90
@@ -89,6 +89,7 @@ subroutine bar_intent_in(x)
subroutine test_actual_arg_intent_in(x)
real, intent(in) :: x(:)
! CHECK: hlfir.copy_in
+! CHECK-SAME: to
! CHECK: fir.call @_QPbar
! Note: no-op hlfir.copy_out has comma separated list of args.
! The actual working hlfir.copy_out has "to" in it.
>From 83ac2e2679f6559b61ca63dac40d5b79986e1e53 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 17 Apr 2026 09:52:01 -0400
Subject: [PATCH 5/6] Fixed this case:
type :: t
integer, pointer :: p(:)
end type
type(t), intent(in) :: x
In this case, x%p may still need copy-out.
---
flang/lib/Evaluate/check-expression.cpp | 18 +++++++++++-------
flang/test/Lower/call-copy-in-out.f90 | 23 +++++++++++++++++++----
2 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 25a6dfa799e9e..62c93e5d20737 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1650,13 +1650,17 @@ std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
return forCopyIn;
}
if (forCopyOut) {
- // If the actual argument's base object has INTENT(IN) in the caller's
- // context, copy-out would violate the read-only semantics of INTENT(IN).
- if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) {
- if (const Symbol *symbol{GetFirstSymbol(*expr)}) {
- if (semantics::IsIntentIn(*symbol)) {
- return false;
- }
+ // F2023 8.5.10 C846/p2/p6: a nonpointer INTENT(IN) dummy and its
+ // subobjects may not be defined. Suppress copy-out when the actual
+ // argument is a subobject of a nonpointer INTENT(IN) dummy.
+ // Exception: a data-ref that goes through a pointer component defines the
+ // pointer's target, which is not a subobject of the dummy (F2023 9.4.2
+ // p5), so copy-out is still needed in that case.
+ if (const auto dataRef{ExtractDataRef(*actual)}) {
+ const Symbol &firstSym{dataRef->GetFirstSymbol()};
+ if (semantics::IsIntentIn(firstSym) && !IsPointer(firstSym) &&
+ !GetLastPointerSymbol(*dataRef)) {
+ return false;
}
}
}
diff --git a/flang/test/Lower/call-copy-in-out.f90 b/flang/test/Lower/call-copy-in-out.f90
index e87ae5e294a84..90899d483d039 100644
--- a/flang/test/Lower/call-copy-in-out.f90
+++ b/flang/test/Lower/call-copy-in-out.f90
@@ -85,20 +85,35 @@ subroutine bar_intent_in(x)
end subroutine
! Test copy-out is skipped when the actual argument has INTENT(IN).
+! A copy-in is still needed for the contiguous-requiring callee, but
+! the temp is only deallocated on return, not copied back (no "to" clause).
! CHECK-LABEL: func @_QPtest_actual_arg_intent_in(
subroutine test_actual_arg_intent_in(x)
real, intent(in) :: x(:)
! CHECK: hlfir.copy_in
-! CHECK-SAME: to
! CHECK: fir.call @_QPbar
-! Note: no-op hlfir.copy_out has comma separated list of args.
-! The actual working hlfir.copy_out has "to" in it.
! CHECK: hlfir.copy_out
-! CHECK-SAME: ,
+! CHECK-NOT: to
! CHECK: return
call bar(x)
end subroutine
+! Test copy-out is NOT skipped when passing a section of a pointer component
+! of an INTENT(IN) dummy: the pointer target is not a subobject of the dummy
+! (F2023 9.4.2 p5), so the callee may define it and copy-out is required.
+! CHECK-LABEL: func @_QPtest_actual_arg_intent_in_ptr_component(
+subroutine test_actual_arg_intent_in_ptr_component(x)
+ type :: t
+ integer, pointer :: p(:)
+ end type
+ type(t), intent(in) :: x
+! CHECK: hlfir.copy_in
+! CHECK: fir.call @_QPbar_integer
+! CHECK: hlfir.copy_out
+! CHECK-SAME: to
+ call bar_integer(x%p(1:4:2))
+end subroutine
+
! Test copy-in/copy-out is done for intent(inout)
! CHECK-LABEL: func @_QPtest_intent_inout(
subroutine test_intent_inout(x)
>From 889a156e25eb7bea98b91b91b8b7b9262ad202a4 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 17 Apr 2026 10:07:26 -0400
Subject: [PATCH 6/6] [flang] Add test: copy-out retained for INTENT(IN)
pointer dummy
Add a LIT test for `integer, intent(in), pointer :: pi(:)`: INTENT(IN)
on a pointer restricts pointer association, not the target's contents,
so the callee may define the target and copy-out must still be emitted.
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
flang/test/Lower/call-copy-in-out.f90 | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/flang/test/Lower/call-copy-in-out.f90 b/flang/test/Lower/call-copy-in-out.f90
index 90899d483d039..5d754d8a9ac28 100644
--- a/flang/test/Lower/call-copy-in-out.f90
+++ b/flang/test/Lower/call-copy-in-out.f90
@@ -114,6 +114,20 @@ subroutine test_actual_arg_intent_in_ptr_component(x)
call bar_integer(x%p(1:4:2))
end subroutine
+! Test copy-out is NOT skipped when the actual is a section of an INTENT(IN)
+! pointer dummy: INTENT(IN) on a pointer restricts pointer association, not
+! the target's contents, so the callee may define the target and copy-out is
+! required.
+! CHECK-LABEL: func @_QPtest_actual_intent_in_pointer(
+subroutine test_actual_intent_in_pointer(pi)
+ integer, intent(in), pointer :: pi(:)
+! CHECK: hlfir.copy_in
+! CHECK: fir.call @_QPbar_integer
+! CHECK: hlfir.copy_out
+! CHECK-SAME: to
+ call bar_integer(pi(1:4:2))
+end subroutine
+
! Test copy-in/copy-out is done for intent(inout)
! CHECK-LABEL: func @_QPtest_intent_inout(
subroutine test_intent_inout(x)
More information about the flang-commits
mailing list