Skip to content

Commit 3f280e5

Browse files
eugeneepshteyntblah
authored andcommitted
[flang] Consolidate copy-in/copy-out determination in evaluate framework
Backport of llvm#151408 New implementation of `MayNeedCopy()` is used to consolidate copy-in/copy-out checks. `IsAssumedShape()` and `IsAssumedRank()` were simplified and are both now in `Fortran::semantics` workspace. `preparePresentUserCallActualArgument()` in lowering was modified to use `MayNeedCopyInOut()` Fixes llvm#138471 Conflicts in backport were trivial. Remove modifications of new code not present in 21.x branch: flang/lib/Semantics/check-omp-structure.cpp flang/lib/Semantics/resolve-directives.cpp
1 parent 3623fe6 commit 3f280e5

22 files changed

+379
-107
lines changed

flang/include/flang/Evaluate/characteristics.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ class TypeAndShape {
203203
std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes(
204204
FoldingContext &) const;
205205

206+
bool IsExplicitShape() const {
207+
// If it's array and no special attributes are set, then must be
208+
// explicit shape.
209+
return Rank() > 0 && attrs_.none();
210+
}
211+
206212
// called by Fold() to rewrite in place
207213
TypeAndShape &Rewrite(FoldingContext &);
208214

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ std::optional<bool> IsContiguous(const A &, FoldingContext &,
118118
extern template std::optional<bool> IsContiguous(const Expr<SomeType> &,
119119
FoldingContext &, bool namedConstantSectionsAreContiguous,
120120
bool firstDimensionStride1);
121+
extern template std::optional<bool> IsContiguous(const ActualArgument &,
122+
FoldingContext &, bool namedConstantSectionsAreContiguous,
123+
bool firstDimensionStride1);
121124
extern template std::optional<bool> IsContiguous(const ArrayRef &,
122125
FoldingContext &, bool namedConstantSectionsAreContiguous,
123126
bool firstDimensionStride1);
@@ -153,5 +156,8 @@ extern template bool IsErrorExpr(const Expr<SomeType> &);
153156
std::optional<parser::Message> CheckStatementFunction(
154157
const Symbol &, const Expr<SomeType> &, FoldingContext &);
155158

159+
bool MayNeedCopy(const ActualArgument *, const characteristics::DummyArgument *,
160+
FoldingContext &, bool forCopyOut);
161+
156162
} // namespace Fortran::evaluate
157163
#endif

flang/include/flang/Evaluate/tools.h

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,27 +81,6 @@ template <typename A> bool IsVariable(const A &x) {
8181
}
8282
}
8383

84-
// Predicate: true when an expression is assumed-rank
85-
bool IsAssumedRank(const Symbol &);
86-
bool IsAssumedRank(const ActualArgument &);
87-
template <typename A> bool IsAssumedRank(const A &) { return false; }
88-
template <typename A> bool IsAssumedRank(const Designator<A> &designator) {
89-
if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
90-
return IsAssumedRank(symbol->get());
91-
} else {
92-
return false;
93-
}
94-
}
95-
template <typename T> bool IsAssumedRank(const Expr<T> &expr) {
96-
return common::visit([](const auto &x) { return IsAssumedRank(x); }, expr.u);
97-
}
98-
template <typename A> bool IsAssumedRank(const std::optional<A> &x) {
99-
return x && IsAssumedRank(*x);
100-
}
101-
template <typename A> bool IsAssumedRank(const A *x) {
102-
return x && IsAssumedRank(*x);
103-
}
104-
10584
// Finds the corank of an entity, possibly packaged in various ways.
10685
// Unlike rank, only data references have corank > 0.
10786
int GetCorank(const ActualArgument &);
@@ -1122,6 +1101,7 @@ extern template semantics::UnorderedSymbolSet CollectCudaSymbols(
11221101

11231102
// Predicate: does a variable contain a vector-valued subscript (not a triplet)?
11241103
bool HasVectorSubscript(const Expr<SomeType> &);
1104+
bool HasVectorSubscript(const ActualArgument &);
11251105

11261106
// Predicate: does an expression contain constant?
11271107
bool HasConstant(const Expr<SomeType> &);
@@ -1548,7 +1528,19 @@ bool IsAllocatableOrObjectPointer(const Symbol *);
15481528
bool IsAutomatic(const Symbol &);
15491529
bool IsSaved(const Symbol &); // saved implicitly or explicitly
15501530
bool IsDummy(const Symbol &);
1531+
1532+
bool IsAssumedRank(const Symbol &);
1533+
template <typename A> bool IsAssumedRank(const A &x) {
1534+
auto *symbol{UnwrapWholeSymbolDataRef(x)};
1535+
return symbol && IsAssumedRank(*symbol);
1536+
}
1537+
15511538
bool IsAssumedShape(const Symbol &);
1539+
template <typename A> bool IsAssumedShape(const A &x) {
1540+
auto *symbol{UnwrapWholeSymbolDataRef(x)};
1541+
return symbol && IsAssumedShape(*symbol);
1542+
}
1543+
15521544
bool IsDeferredShape(const Symbol &);
15531545
bool IsFunctionResult(const Symbol &);
15541546
bool IsKindTypeParameter(const Symbol &);

flang/lib/Evaluate/check-expression.cpp

Lines changed: 187 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,8 @@ class IsContiguousHelper
917917
} else {
918918
return Base::operator()(ultimate); // use expr
919919
}
920-
} else if (semantics::IsPointer(ultimate) ||
921-
semantics::IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) {
920+
} else if (semantics::IsPointer(ultimate) || IsAssumedShape(ultimate) ||
921+
IsAssumedRank(ultimate)) {
922922
return std::nullopt;
923923
} else if (ultimate.has<semantics::ObjectEntityDetails>()) {
924924
return true;
@@ -1198,9 +1198,21 @@ std::optional<bool> IsContiguous(const A &x, FoldingContext &context,
11981198
}
11991199
}
12001200

1201+
std::optional<bool> IsContiguous(const ActualArgument &actual,
1202+
FoldingContext &fc, bool namedConstantSectionsAreContiguous,
1203+
bool firstDimensionStride1) {
1204+
auto *expr{actual.UnwrapExpr()};
1205+
return expr &&
1206+
IsContiguous(
1207+
*expr, fc, namedConstantSectionsAreContiguous, firstDimensionStride1);
1208+
}
1209+
12011210
template std::optional<bool> IsContiguous(const Expr<SomeType> &,
12021211
FoldingContext &, bool namedConstantSectionsAreContiguous,
12031212
bool firstDimensionStride1);
1213+
template std::optional<bool> IsContiguous(const ActualArgument &,
1214+
FoldingContext &, bool namedConstantSectionsAreContiguous,
1215+
bool firstDimensionStride1);
12041216
template std::optional<bool> IsContiguous(const ArrayRef &, FoldingContext &,
12051217
bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
12061218
template std::optional<bool> IsContiguous(const Substring &, FoldingContext &,
@@ -1350,4 +1362,177 @@ std::optional<parser::Message> CheckStatementFunction(
13501362
return StmtFunctionChecker{sf, context}(expr);
13511363
}
13521364

1365+
// Helper class for checking differences between actual and dummy arguments
1366+
class CopyInOutExplicitInterface {
1367+
public:
1368+
explicit CopyInOutExplicitInterface(FoldingContext &fc,
1369+
const ActualArgument &actual,
1370+
const characteristics::DummyDataObject &dummyObj)
1371+
: fc_{fc}, actual_{actual}, dummyObj_{dummyObj} {}
1372+
1373+
// Returns true, if actual and dummy have different contiguity requirements
1374+
bool HaveContiguityDifferences() const {
1375+
// Check actual contiguity, unless dummy doesn't care
1376+
bool dummyTreatAsArray{dummyObj_.ignoreTKR.test(common::IgnoreTKR::Rank)};
1377+
bool actualTreatAsContiguous{
1378+
dummyObj_.ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
1379+
IsSimplyContiguous(actual_, fc_)};
1380+
bool dummyIsExplicitShape{dummyObj_.type.IsExplicitShape()};
1381+
bool dummyIsAssumedSize{dummyObj_.type.attrs().test(
1382+
characteristics::TypeAndShape::Attr::AssumedSize)};
1383+
bool dummyIsPolymorphic{dummyObj_.type.type().IsPolymorphic()};
1384+
// type(*) with IGNORE_TKR(tkr) is often used to interface with C "void*".
1385+
// Since the other languages don't know about Fortran's discontiguity
1386+
// handling, such cases should require contiguity.
1387+
bool dummyIsVoidStar{dummyObj_.type.type().IsAssumedType() &&
1388+
dummyObj_.ignoreTKR.test(common::IgnoreTKR::Type) &&
1389+
dummyObj_.ignoreTKR.test(common::IgnoreTKR::Rank) &&
1390+
dummyObj_.ignoreTKR.test(common::IgnoreTKR::Kind)};
1391+
// Explicit shape and assumed size arrays must be contiguous
1392+
bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
1393+
(dummyTreatAsArray && !dummyIsPolymorphic) || dummyIsVoidStar ||
1394+
dummyObj_.attrs.test(
1395+
characteristics::DummyDataObject::Attr::Contiguous)};
1396+
return !actualTreatAsContiguous && dummyNeedsContiguity;
1397+
}
1398+
1399+
// Returns true, if actual and dummy have polymorphic differences
1400+
bool HavePolymorphicDifferences() const {
1401+
bool dummyIsAssumedRank{dummyObj_.type.attrs().test(
1402+
characteristics::TypeAndShape::Attr::AssumedRank)};
1403+
bool actualIsAssumedRank{semantics::IsAssumedRank(actual_)};
1404+
bool dummyIsAssumedShape{dummyObj_.type.attrs().test(
1405+
characteristics::TypeAndShape::Attr::AssumedShape)};
1406+
bool actualIsAssumedShape{semantics::IsAssumedShape(actual_)};
1407+
if ((actualIsAssumedRank && dummyIsAssumedRank) ||
1408+
(actualIsAssumedShape && dummyIsAssumedShape)) {
1409+
// Assumed-rank and assumed-shape arrays are represented by descriptors,
1410+
// so don't need to do polymorphic check.
1411+
} else if (!dummyObj_.ignoreTKR.test(common::IgnoreTKR::Type)) {
1412+
// flang supports limited cases of passing polymorphic to non-polimorphic.
1413+
// These cases require temporary of non-polymorphic type. (For example,
1414+
// the actual argument could be polymorphic array of child type,
1415+
// while the dummy argument could be non-polymorphic array of parent
1416+
// type.)
1417+
bool dummyIsPolymorphic{dummyObj_.type.type().IsPolymorphic()};
1418+
auto actualType{
1419+
characteristics::TypeAndShape::Characterize(actual_, fc_)};
1420+
bool actualIsPolymorphic{
1421+
actualType && actualType->type().IsPolymorphic()};
1422+
if (actualIsPolymorphic && !dummyIsPolymorphic) {
1423+
return true;
1424+
}
1425+
}
1426+
return false;
1427+
}
1428+
1429+
bool HaveArrayOrAssumedRankArgs() const {
1430+
bool dummyTreatAsArray{dummyObj_.ignoreTKR.test(common::IgnoreTKR::Rank)};
1431+
return IsArrayOrAssumedRank(actual_) &&
1432+
(IsArrayOrAssumedRank(dummyObj_) || dummyTreatAsArray);
1433+
}
1434+
1435+
bool PassByValue() const {
1436+
return dummyObj_.attrs.test(characteristics::DummyDataObject::Attr::Value);
1437+
}
1438+
1439+
bool HaveCoarrayDifferences() const {
1440+
return ExtractCoarrayRef(actual_) && dummyObj_.type.corank() == 0;
1441+
}
1442+
1443+
bool HasIntentOut() const { return dummyObj_.intent == common::Intent::Out; }
1444+
1445+
bool HasIntentIn() const { return dummyObj_.intent == common::Intent::In; }
1446+
1447+
static bool IsArrayOrAssumedRank(const ActualArgument &actual) {
1448+
return semantics::IsAssumedRank(actual) || actual.Rank() > 0;
1449+
}
1450+
1451+
static bool IsArrayOrAssumedRank(
1452+
const characteristics::DummyDataObject &dummy) {
1453+
return dummy.type.attrs().test(
1454+
characteristics::TypeAndShape::Attr::AssumedRank) ||
1455+
dummy.type.Rank() > 0;
1456+
}
1457+
1458+
private:
1459+
FoldingContext &fc_;
1460+
const ActualArgument &actual_;
1461+
const characteristics::DummyDataObject &dummyObj_;
1462+
};
1463+
1464+
// If forCopyOut is false, returns if a particular actual/dummy argument
1465+
// combination may need a temporary creation with copy-in operation. If
1466+
// forCopyOut is true, returns the same for copy-out operation. For
1467+
// procedures with explicit interface, it's expected that "dummy" is not null.
1468+
// For procedures with implicit interface dummy may be null.
1469+
//
1470+
// Note that these copy-in and copy-out checks are done from the caller's
1471+
// perspective, meaning that for copy-in the caller need to do the copy
1472+
// before calling the callee. Similarly, for copy-out the caller is expected
1473+
// to do the copy after the callee returns.
1474+
bool MayNeedCopy(const ActualArgument *actual,
1475+
const characteristics::DummyArgument *dummy, FoldingContext &fc,
1476+
bool forCopyOut) {
1477+
if (!actual) {
1478+
return false;
1479+
}
1480+
if (actual->isAlternateReturn()) {
1481+
return false;
1482+
}
1483+
const auto *dummyObj{dummy
1484+
? std::get_if<characteristics::DummyDataObject>(&dummy->u)
1485+
: nullptr};
1486+
const bool forCopyIn = !forCopyOut;
1487+
if (!evaluate::IsVariable(*actual)) {
1488+
// Actual argument expressions that aren’t variables are copy-in, but
1489+
// not copy-out.
1490+
return forCopyIn;
1491+
}
1492+
if (dummyObj) { // Explict interface
1493+
CopyInOutExplicitInterface check{fc, *actual, *dummyObj};
1494+
if (forCopyOut && check.HasIntentIn()) {
1495+
// INTENT(IN) dummy args never need copy-out
1496+
return false;
1497+
}
1498+
if (forCopyIn && check.HasIntentOut()) {
1499+
// INTENT(OUT) dummy args never need copy-in
1500+
return false;
1501+
}
1502+
if (check.PassByValue()) {
1503+
// Pass by value, always copy-in, never copy-out
1504+
return forCopyIn;
1505+
}
1506+
if (check.HaveCoarrayDifferences()) {
1507+
return true;
1508+
}
1509+
// Note: contiguity and polymorphic checks deal with array or assumed rank
1510+
// arguments
1511+
if (!check.HaveArrayOrAssumedRankArgs()) {
1512+
return false;
1513+
}
1514+
if (check.HaveContiguityDifferences()) {
1515+
return true;
1516+
}
1517+
if (check.HavePolymorphicDifferences()) {
1518+
return true;
1519+
}
1520+
} else { // Implicit interface
1521+
if (ExtractCoarrayRef(*actual)) {
1522+
// Coindexed actual args may need copy-in and copy-out with implicit
1523+
// interface
1524+
return true;
1525+
}
1526+
if (!IsSimplyContiguous(*actual, fc)) {
1527+
// Copy-in: actual arguments that are variables are copy-in when
1528+
// non-contiguous.
1529+
// Copy-out: vector subscripts could refer to duplicate elements, can't
1530+
// copy out.
1531+
return !(forCopyOut && HasVectorSubscript(*actual));
1532+
}
1533+
}
1534+
// For everything else, no copy-in or copy-out
1535+
return false;
1536+
}
1537+
13531538
} // namespace Fortran::evaluate

flang/lib/Evaluate/fold-integer.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ static bool CheckDimArg(const std::optional<ActualArgument> &dimArg,
3838
const Expr<SomeType> &array, parser::ContextualMessages &messages,
3939
bool isLBound, std::optional<int> &dimVal) {
4040
dimVal.reset();
41-
if (int rank{array.Rank()}; rank > 0 || IsAssumedRank(array)) {
41+
if (int rank{array.Rank()}; rank > 0 || semantics::IsAssumedRank(array)) {
4242
auto named{ExtractNamedEntity(array)};
4343
if (auto dim64{ToInt64(dimArg)}) {
4444
if (*dim64 < 1) {
4545
messages.Say("DIM=%jd dimension must be positive"_err_en_US, *dim64);
4646
return false;
47-
} else if (!IsAssumedRank(array) && *dim64 > rank) {
47+
} else if (!semantics::IsAssumedRank(array) && *dim64 > rank) {
4848
messages.Say(
4949
"DIM=%jd dimension is out of range for rank-%d array"_err_en_US,
5050
*dim64, rank);
@@ -56,7 +56,7 @@ static bool CheckDimArg(const std::optional<ActualArgument> &dimArg,
5656
"DIM=%jd dimension is out of range for rank-%d assumed-size array"_err_en_US,
5757
*dim64, rank);
5858
return false;
59-
} else if (IsAssumedRank(array)) {
59+
} else if (semantics::IsAssumedRank(array)) {
6060
if (*dim64 > common::maxRank) {
6161
messages.Say(
6262
"DIM=%jd dimension is too large for any array (maximum rank %d)"_err_en_US,
@@ -189,7 +189,7 @@ Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context,
189189
return Expr<T>{std::move(funcRef)};
190190
}
191191
}
192-
if (IsAssumedRank(*array)) {
192+
if (semantics::IsAssumedRank(*array)) {
193193
// Would like to return 1 if DIM=.. is present, but that would be
194194
// hiding a runtime error if the DIM= were too large (including
195195
// the case of an assumed-rank argument that's scalar).
@@ -240,7 +240,7 @@ Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
240240
return Expr<T>{std::move(funcRef)};
241241
}
242242
}
243-
if (IsAssumedRank(*array)) {
243+
if (semantics::IsAssumedRank(*array)) {
244244
} else if (int rank{array->Rank()}; rank > 0) {
245245
bool takeBoundsFromShape{true};
246246
if (auto named{ExtractNamedEntity(*array)}) {

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2251,7 +2251,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
22512251
for (std::size_t j{0}; j < dummies; ++j) {
22522252
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
22532253
if (const ActualArgument *arg{actualForDummy[j]}) {
2254-
bool isAssumedRank{IsAssumedRank(*arg)};
2254+
bool isAssumedRank{semantics::IsAssumedRank(*arg)};
22552255
if (isAssumedRank && d.rank != Rank::anyOrAssumedRank &&
22562256
d.rank != Rank::arrayOrAssumedRank) {
22572257
messages.Say(arg->sourceLocation(),
@@ -2997,7 +2997,7 @@ SpecificCall IntrinsicProcTable::Implementation::HandleNull(
29972997
mold = nullptr;
29982998
}
29992999
if (mold) {
3000-
if (IsAssumedRank(*arguments[0])) {
3000+
if (semantics::IsAssumedRank(*arguments[0])) {
30013001
context.messages().Say(arguments[0]->sourceLocation(),
30023002
"MOLD= argument to NULL() must not be assumed-rank"_err_en_US);
30033003
}

flang/lib/Evaluate/shape.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
947947
intrinsic->name == "ubound") {
948948
// For LBOUND/UBOUND, these are the array-valued cases (no DIM=)
949949
if (!call.arguments().empty() && call.arguments().front()) {
950-
if (IsAssumedRank(*call.arguments().front())) {
950+
if (semantics::IsAssumedRank(*call.arguments().front())) {
951951
return Shape{MaybeExtentExpr{}};
952952
} else {
953953
return Shape{

0 commit comments

Comments
 (0)