Skip to content

Commit e7bccc7

Browse files
[flang] Fixed regression in copy-in/copy-out (llvm#161259)
Fixed the polymprphic check for copy-in/copy-out, added regression tests. Changed MayNeedCopy() to return std::optional<bool> and renamed it to ActualArgNeedsCopy(). This function now returns true/false when it's known that actual arguments needs copy in/out, or std::nullopt to signify that it's now known, whether copy in/out is needed. Fixes llvm#159149
1 parent 242a6cb commit e7bccc7

File tree

5 files changed

+110
-61
lines changed

5 files changed

+110
-61
lines changed

flang/include/flang/Evaluate/check-expression.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ extern template bool IsErrorExpr(const Expr<SomeType> &);
163163
std::optional<parser::Message> CheckStatementFunction(
164164
const Symbol &, const Expr<SomeType> &, FoldingContext &);
165165

166-
bool MayNeedCopy(const ActualArgument *, const characteristics::DummyArgument *,
167-
FoldingContext &, bool forCopyOut);
166+
std::optional<bool> ActualArgNeedsCopy(const ActualArgument *,
167+
const characteristics::DummyArgument *, FoldingContext &, bool forCopyOut);
168168

169169
} // namespace Fortran::evaluate
170170
#endif

flang/lib/Evaluate/check-expression.cpp

Lines changed: 37 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,13 +1478,12 @@ class CopyInOutExplicitInterface {
14781478
const characteristics::DummyDataObject &dummyObj)
14791479
: fc_{fc}, actual_{actual}, dummyObj_{dummyObj} {}
14801480

1481-
// Returns true, if actual and dummy have different contiguity requirements
1482-
bool HaveContiguityDifferences() const {
1483-
// Check actual contiguity, unless dummy doesn't care
1481+
// Returns true if dummy arg needs to be contiguous
1482+
bool DummyNeedsContiguity() const {
1483+
if (dummyObj_.ignoreTKR.test(common::IgnoreTKR::Contiguous)) {
1484+
return false;
1485+
}
14841486
bool dummyTreatAsArray{dummyObj_.ignoreTKR.test(common::IgnoreTKR::Rank)};
1485-
bool actualTreatAsContiguous{
1486-
dummyObj_.ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
1487-
IsSimplyContiguous(actual_, fc_)};
14881487
bool dummyIsExplicitShape{dummyObj_.type.IsExplicitShape()};
14891488
bool dummyIsAssumedSize{dummyObj_.type.attrs().test(
14901489
characteristics::TypeAndShape::Attr::AssumedSize)};
@@ -1501,32 +1500,17 @@ class CopyInOutExplicitInterface {
15011500
(dummyTreatAsArray && !dummyIsPolymorphic) || dummyIsVoidStar ||
15021501
dummyObj_.attrs.test(
15031502
characteristics::DummyDataObject::Attr::Contiguous)};
1504-
return !actualTreatAsContiguous && dummyNeedsContiguity;
1503+
return dummyNeedsContiguity;
15051504
}
15061505

1507-
// Returns true, if actual and dummy have polymorphic differences
15081506
bool HavePolymorphicDifferences() const {
1509-
bool dummyIsAssumedRank{dummyObj_.type.attrs().test(
1510-
characteristics::TypeAndShape::Attr::AssumedRank)};
1511-
bool actualIsAssumedRank{semantics::IsAssumedRank(actual_)};
1512-
bool dummyIsAssumedShape{dummyObj_.type.attrs().test(
1513-
characteristics::TypeAndShape::Attr::AssumedShape)};
1514-
bool actualIsAssumedShape{semantics::IsAssumedShape(actual_)};
1515-
if ((actualIsAssumedRank && dummyIsAssumedRank) ||
1516-
(actualIsAssumedShape && dummyIsAssumedShape)) {
1517-
// Assumed-rank and assumed-shape arrays are represented by descriptors,
1518-
// so don't need to do polymorphic check.
1519-
} else if (!dummyObj_.ignoreTKR.test(common::IgnoreTKR::Type)) {
1520-
// flang supports limited cases of passing polymorphic to non-polimorphic.
1521-
// These cases require temporary of non-polymorphic type. (For example,
1522-
// the actual argument could be polymorphic array of child type,
1523-
// while the dummy argument could be non-polymorphic array of parent
1524-
// type.)
1507+
if (dummyObj_.ignoreTKR.test(common::IgnoreTKR::Type)) {
1508+
return false;
1509+
}
1510+
if (auto actualType{
1511+
characteristics::TypeAndShape::Characterize(actual_, fc_)}) {
1512+
bool actualIsPolymorphic{actualType->type().IsPolymorphic()};
15251513
bool dummyIsPolymorphic{dummyObj_.type.type().IsPolymorphic()};
1526-
auto actualType{
1527-
characteristics::TypeAndShape::Characterize(actual_, fc_)};
1528-
bool actualIsPolymorphic{
1529-
actualType && actualType->type().IsPolymorphic()};
15301514
if (actualIsPolymorphic && !dummyIsPolymorphic) {
15311515
return true;
15321516
}
@@ -1575,28 +1559,32 @@ class CopyInOutExplicitInterface {
15751559
// procedures with explicit interface, it's expected that "dummy" is not null.
15761560
// For procedures with implicit interface dummy may be null.
15771561
//
1562+
// Returns std::optional<bool> indicating whether the copy is known to be
1563+
// needed (true) or not needed (false); returns std::nullopt if the necessity
1564+
// of the copy is undetermined.
1565+
//
15781566
// Note that these copy-in and copy-out checks are done from the caller's
15791567
// perspective, meaning that for copy-in the caller need to do the copy
15801568
// before calling the callee. Similarly, for copy-out the caller is expected
15811569
// to do the copy after the callee returns.
1582-
bool MayNeedCopy(const ActualArgument *actual,
1570+
std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
15831571
const characteristics::DummyArgument *dummy, FoldingContext &fc,
15841572
bool forCopyOut) {
15851573
if (!actual) {
1586-
return false;
1574+
return std::nullopt;
15871575
}
15881576
if (actual->isAlternateReturn()) {
1589-
return false;
1577+
return std::nullopt;
15901578
}
15911579
const auto *dummyObj{dummy
15921580
? std::get_if<characteristics::DummyDataObject>(&dummy->u)
15931581
: nullptr};
1594-
const bool forCopyIn = !forCopyOut;
1582+
const bool forCopyIn{!forCopyOut};
15951583
if (!evaluate::IsVariable(*actual)) {
1596-
// Actual argument expressions that aren’t variables are copy-in, but
1597-
// not copy-out.
1584+
// Expressions are copy-in, but not copy-out.
15981585
return forCopyIn;
15991586
}
1587+
auto maybeContigActual{IsContiguous(*actual, fc)};
16001588
if (dummyObj) { // Explict interface
16011589
CopyInOutExplicitInterface check{fc, *actual, *dummyObj};
16021590
if (forCopyOut && check.HasIntentIn()) {
@@ -1619,28 +1607,25 @@ bool MayNeedCopy(const ActualArgument *actual,
16191607
if (!check.HaveArrayOrAssumedRankArgs()) {
16201608
return false;
16211609
}
1622-
if (check.HaveContiguityDifferences()) {
1623-
return true;
1624-
}
1625-
if (check.HavePolymorphicDifferences()) {
1626-
return true;
1610+
if (maybeContigActual.has_value()) {
1611+
// We know whether actual arg is contiguous or not
1612+
bool isContiguousActual{maybeContigActual.value()};
1613+
bool actualArgNeedsCopy{
1614+
(!isContiguousActual || check.HavePolymorphicDifferences()) &&
1615+
check.DummyNeedsContiguity()};
1616+
return actualArgNeedsCopy;
1617+
} else {
1618+
// We don't know whether actual arg is contiguous or not
1619+
return check.DummyNeedsContiguity();
16271620
}
16281621
} else { // Implicit interface
1629-
if (ExtractCoarrayRef(*actual)) {
1630-
// Coindexed actual args may need copy-in and copy-out with implicit
1631-
// interface
1632-
return true;
1633-
}
1634-
if (!IsSimplyContiguous(*actual, fc)) {
1635-
// Copy-in: actual arguments that are variables are copy-in when
1636-
// non-contiguous.
1637-
// Copy-out: vector subscripts could refer to duplicate elements, can't
1638-
// copy out.
1639-
return !(forCopyOut && HasVectorSubscript(*actual));
1622+
if (maybeContigActual.has_value()) {
1623+
// If known contiguous, don't copy in/out.
1624+
// If known non-contiguous, copy in/out.
1625+
return !*maybeContigActual;
16401626
}
16411627
}
1642-
// For everything else, no copy-in or copy-out
1643-
return false;
1628+
return std::nullopt;
16441629
}
16451630

16461631
} // namespace Fortran::evaluate

flang/lib/Lower/ConvertCall.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,10 +1296,14 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
12961296
Fortran::evaluate::FoldingContext &foldingContext{
12971297
callContext.converter.getFoldingContext()};
12981298

1299-
bool suggestCopyIn = Fortran::evaluate::MayNeedCopy(
1300-
arg.entity, arg.characteristics, foldingContext, /*forCopyOut=*/false);
1301-
bool suggestCopyOut = Fortran::evaluate::MayNeedCopy(
1302-
arg.entity, arg.characteristics, foldingContext, /*forCopyOut=*/true);
1299+
bool suggestCopyIn = Fortran::evaluate::ActualArgNeedsCopy(
1300+
arg.entity, arg.characteristics, foldingContext,
1301+
/*forCopyOut=*/false)
1302+
.value_or(true);
1303+
bool suggestCopyOut = Fortran::evaluate::ActualArgNeedsCopy(
1304+
arg.entity, arg.characteristics, foldingContext,
1305+
/*forCopyOut=*/true)
1306+
.value_or(true);
13031307
mustDoCopyIn = actual.isArray() && suggestCopyIn;
13041308
mustDoCopyOut = actual.isArray() && suggestCopyOut;
13051309
}

flang/lib/Semantics/check-call.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,9 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
800800
bool dummyIsAssumedShape{dummy.type.attrs().test(
801801
characteristics::TypeAndShape::Attr::AssumedShape)};
802802
bool copyOutNeeded{
803-
evaluate::MayNeedCopy(&arg, &dummyArg, foldingContext, true)};
803+
evaluate::ActualArgNeedsCopy(&arg, &dummyArg, foldingContext,
804+
/*forCopyOut=*/true)
805+
.value_or(false)};
804806
if (copyOutNeeded && !dummyIsValue &&
805807
(dummyIsAsynchronous || dummyIsVolatile)) {
806808
if (actualIsAsynchronous || actualIsVolatile) {
@@ -837,8 +839,8 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
837839
// a unread value in the actual argument.
838840
// Occurences of `volatileOrAsyncNeedsTempDiagnosticIssued = true` indicate a
839841
// more specific error message has already been issued. We might be able to
840-
// clean this up by switching the coding style of MayNeedCopy to be more like
841-
// WhyNotDefinable.
842+
// clean this up by switching the coding style of ActualArgNeedsCopy to be
843+
// more like WhyNotDefinable.
842844
if (copyOutNeeded && !volatileOrAsyncNeedsTempDiagnosticIssued) {
843845
if ((actualIsVolatile || actualIsAsynchronous) &&
844846
(dummyIsVolatile || dummyIsAsynchronous)) {

flang/test/Lower/force-temp.f90

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ subroutine pass_intent_out(buf)
2727
integer, intent(out) :: buf(5)
2828
end subroutine
2929
end interface
30+
31+
! Used by call_s6() and others below
32+
type base
33+
integer :: i = -1
34+
end type
35+
type, extends (base) :: child
36+
real :: r = -2.0
37+
end type
3038
contains
3139
subroutine s1(buf)
3240
!CHECK-LABEL: func.func @_QMtestPs1
@@ -79,4 +87,54 @@ subroutine s5()
7987
p => x(::2) ! pointer to non-contiguous array section
8088
call pass_intent_out(p)
8189
end subroutine
90+
subroutine call_s6()
91+
interface
92+
subroutine s6(b)
93+
import :: base
94+
type(base), intent(inout) :: b(:)
95+
end subroutine s6
96+
end interface
97+
class(base), pointer :: pb(:)
98+
type(child), target :: c(2)
99+
!CHECK-LABEL: func.func @_QMtestPcall_s6
100+
!CHECK-NOT: hlfir.copy_in
101+
!CHECK: fir.call @_QPs6
102+
!CHECK-NOT: hlfir.copy_out
103+
pb => c
104+
call s6(pb)
105+
end subroutine call_s6
106+
subroutine call_s7()
107+
interface
108+
subroutine s7(b1, b2, n)
109+
import :: base
110+
integer :: n
111+
type(base), intent(inout) :: b1(n)
112+
type(base), intent(inout) :: b2(*)
113+
end subroutine
114+
end interface
115+
integer, parameter :: n = 7
116+
class(base), allocatable :: c1(:), c2(:)
117+
!CHECK-LABEL: func.func @_QMtestPcall_s7
118+
!CHECK: hlfir.copy_in
119+
!CHECK: hlfir.copy_in
120+
!CHECK: fir.call @_QPs7
121+
!CHECK: hlfir.copy_out
122+
!CHECK: hlfir.copy_out
123+
call s7(c1, c2, n)
124+
end subroutine call_s7
125+
subroutine call_s8()
126+
interface
127+
subroutine s8(buf)
128+
! IGNORE_TKR(C) takes precendence over CONTIGUOUS
129+
!DIR$ IGNORE_TKR(C) buf
130+
real, contiguous :: buf(:)
131+
end subroutine
132+
end interface
133+
real a(10)
134+
!CHECK-LABEL: func.func @_QMtestPcall_s8
135+
!CHECK-NOT: hlfir.copy_in
136+
!CHECK: fir.call @_QPs8
137+
!CHECK-NOT: hlfir.copy_out
138+
call s8(a(1:5:2))
139+
end subroutine call_s8
82140
end module

0 commit comments

Comments
 (0)