Skip to content

Commit 1bd1822

Browse files
Merge branch 'copy-inout-dev' into copy-inout-review
2 parents e69fa22 + 6868af8 commit 1bd1822

File tree

15 files changed

+256
-165
lines changed

15 files changed

+256
-165
lines changed

flang/include/flang/Evaluate/call.h

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ using SymbolRef = common::Reference<const Symbol>;
5252

5353
class ActualArgument {
5454
public:
55-
ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef, CopyIn, CopyOut);
55+
ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef);
5656
using Attrs = common::EnumSet<Attr, Attr_enumSize>;
5757

5858
// Dummy arguments that are TYPE(*) can be forwarded as actual arguments.
@@ -131,6 +131,7 @@ class ActualArgument {
131131
return *this;
132132
}
133133

134+
bool Matches(const characteristics::DummyArgument &) const;
134135
common::Intent dummyIntent() const { return dummyIntent_; }
135136
ActualArgument &set_dummyIntent(common::Intent intent) {
136137
dummyIntent_ = intent;
@@ -160,20 +161,6 @@ class ActualArgument {
160161
return *this;
161162
}
162163

163-
// This actual argument may need copy-in before the procedure call
164-
bool mayNeedCopyIn() const { return attrs_.test(Attr::CopyIn); };
165-
ActualArgument &set_mayNeedCopyIn() {
166-
attrs_ = attrs_ + Attr::CopyIn;
167-
return *this;
168-
}
169-
170-
// This actual argument may need copy-out after the procedure call
171-
bool mayNeedCopyOut() const { return attrs_.test(Attr::CopyOut); };
172-
ActualArgument &set_mayNeedCopyOut() {
173-
attrs_ = attrs_ + Attr::CopyOut;
174-
return *this;
175-
}
176-
177164
private:
178165
// Subtlety: There is a distinction that must be maintained here between an
179166
// actual argument expression that is a variable and one that is not,
@@ -285,8 +272,6 @@ class ProcedureRef {
285272
bool operator==(const ProcedureRef &) const;
286273
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
287274

288-
void DetermineCopyInOut();
289-
290275
protected:
291276
ProcedureDesignator proc_;
292277
ActualArguments arguments_;

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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,5 +163,27 @@ extern template bool IsErrorExpr(const Expr<SomeType> &);
163163
std::optional<parser::Message> CheckStatementFunction(
164164
const Symbol &, const Expr<SomeType> &, FoldingContext &);
165165

166+
// Returns a pair of Booleans. The first boolean specifies whether given actual
167+
// argument may need copy-in operation and the second Boolean specifies whether
168+
// copy-out may be necessary. This function works with implicit interface
169+
// procedures.
170+
std::pair<bool, bool> MayNeedCopyInOut(
171+
const ActualArgument &, FoldingContext &);
172+
173+
// Returns a pair of Booleans. The first boolean specifies whether given actual
174+
// and dummy argument pair may need copy-in operation for the actual argument,
175+
// and the second Boolean specifies whether copy-out may be necessary.
176+
// This function works with explicit interface procedures.
177+
std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &,
178+
const characteristics::DummyArgument &, FoldingContext &);
179+
180+
inline std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &actual,
181+
const characteristics::DummyArgument *dummy, FoldingContext &fc) {
182+
if (dummy)
183+
return MayNeedCopyInOut(actual, *dummy, fc);
184+
else
185+
return MayNeedCopyInOut(actual, fc);
186+
}
187+
166188
} // namespace Fortran::evaluate
167189
#endif

flang/include/flang/Evaluate/tools.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,27 @@ template <typename A> bool IsAssumedRank(const A *x) {
103103
return x && IsAssumedRank(*x);
104104
}
105105

106+
// Predicate: true when an expression is assumed-shape
107+
bool IsAssumedShape(const Symbol &);
108+
bool IsAssumedShape(const ActualArgument &);
109+
template <typename A> bool IsAssumedShape(const A &) { return false; }
110+
template <typename A> bool IsAssumedShape(const Designator<A> &designator) {
111+
if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
112+
return evaluate::IsAssumedShape(symbol->get());
113+
} else {
114+
return false;
115+
}
116+
}
117+
template <typename T> bool IsAssumedShape(const Expr<T> &expr) {
118+
return common::visit([](const auto &x) { return IsAssumedShape(x); }, expr.u);
119+
}
120+
template <typename A> bool IsAssumedShape(const std::optional<A> &x) {
121+
return x && IsAssumedShape(*x);
122+
}
123+
template <typename A> bool IsAssumedShape(const A *x) {
124+
return x && IsAssumedShape(*x);
125+
}
126+
106127
// Finds the corank of an entity, possibly packaged in various ways.
107128
// Unlike rank, only data references have corank > 0.
108129
int GetCorank(const ActualArgument &);
@@ -1556,7 +1577,6 @@ bool IsAllocatableOrObjectPointer(const Symbol *);
15561577
bool IsAutomatic(const Symbol &);
15571578
bool IsSaved(const Symbol &); // saved implicitly or explicitly
15581579
bool IsDummy(const Symbol &);
1559-
bool IsAssumedShape(const Symbol &);
15601580
bool IsDeferredShape(const Symbol &);
15611581
bool IsFunctionResult(const Symbol &);
15621582
bool IsKindTypeParameter(const Symbol &);

flang/lib/Evaluate/call.cpp

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "flang/Evaluate/check-expression.h"
1313
#include "flang/Evaluate/expression.h"
1414
#include "flang/Evaluate/tools.h"
15-
#include "flang/Semantics/semantics.h"
1615
#include "flang/Semantics/symbol.h"
1716
#include "flang/Support/Fortran.h"
1817

@@ -248,108 +247,4 @@ ProcedureRef::~ProcedureRef() {}
248247

249248
void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
250249

251-
// We don't know the dummy argument info (e.g., procedure with implicit
252-
// interface
253-
static void DetermineCopyInOutArgument(
254-
const characteristics::Procedure &procInfo, ActualArgument &actual,
255-
semantics::SemanticsContext &sc) {
256-
if (actual.isAlternateReturn()) {
257-
return;
258-
}
259-
if (!evaluate::IsVariable(actual)) {
260-
// Actual argument expressions that aren’t variables are copy-in, but
261-
// not copy-out.
262-
actual.set_mayNeedCopyIn();
263-
} else if (!IsSimplyContiguous(actual, sc.foldingContext())) {
264-
// Actual arguments that are variables are copy-in when non-contiguous.
265-
// They are copy-out when don't have vector subscripts
266-
actual.set_mayNeedCopyIn();
267-
if (!HasVectorSubscript(actual)) {
268-
actual.set_mayNeedCopyOut();
269-
}
270-
} else if (ExtractCoarrayRef(actual)) {
271-
// Coindexed actual args need copy-in and copy-out
272-
actual.set_mayNeedCopyIn();
273-
actual.set_mayNeedCopyOut();
274-
}
275-
}
276-
277-
static void DetermineCopyInOutArgument(
278-
const characteristics::Procedure &procInfo, ActualArgument &actual,
279-
characteristics::DummyArgument &dummy, semantics::SemanticsContext &sc) {
280-
assert(procInfo.HasExplicitInterface() && "expect explicit interface proc");
281-
if (actual.isAlternateReturn()) {
282-
return;
283-
}
284-
// TODO
285-
}
286-
287-
void ProcedureRef::DetermineCopyInOut() {
288-
if (!proc_.GetSymbol()) {
289-
return;
290-
}
291-
// Get folding context of the call site owner
292-
semantics::SemanticsContext &sc{proc_.GetSymbol()->owner().context()};
293-
FoldingContext &fc{sc.foldingContext()};
294-
auto procInfo{
295-
characteristics::Procedure::Characterize(proc_, fc, /*emitError=*/true)};
296-
if (!procInfo) {
297-
return;
298-
}
299-
if (!procInfo->HasExplicitInterface()) {
300-
for (auto &actual : arguments_) {
301-
if (!actual) {
302-
continue;
303-
}
304-
DetermineCopyInOutArgument(*procInfo, *actual, sc);
305-
}
306-
return;
307-
}
308-
// Don't change anything about actual or dummy arguments, except for
309-
// computing copy-in/copy-out information. If detect something wrong with
310-
// the arguments, stop processing and let semantic analysis generate the
311-
// error messages.
312-
size_t index{0};
313-
std::set<std::string> processedKeywords;
314-
bool seenKeyword{false};
315-
for (auto &actual : arguments_) {
316-
if (!actual) {
317-
continue;
318-
}
319-
if (index >= procInfo->dummyArguments.size()) {
320-
// More actual arguments than dummy arguments. Semantic analysis will
321-
// deal with the error.
322-
return;
323-
}
324-
if (actual->keyword()) {
325-
seenKeyword = true;
326-
auto actualName = actual->keyword()->ToString();
327-
if (processedKeywords.find(actualName) != processedKeywords.end()) {
328-
// Actual arguments with duplicate keywords. Semantic analysis will
329-
// deal with the error.
330-
return;
331-
} else {
332-
processedKeywords.insert(actualName);
333-
if (auto it = std::find_if(procInfo->dummyArguments.begin(),
334-
procInfo->dummyArguments.end(),
335-
[&](const characteristics::DummyArgument &dummy) {
336-
return dummy.name == actualName;
337-
});
338-
it != procInfo->dummyArguments.end()) {
339-
DetermineCopyInOutArgument(*procInfo, *actual, *it, sc);
340-
}
341-
}
342-
} else if (seenKeyword) {
343-
// Non-keyword actual argument after have seen at least one keyword
344-
// actual argument. Semantic analysis will deal with the error.
345-
return;
346-
} else {
347-
// Positional argument processing
348-
DetermineCopyInOutArgument(
349-
*procInfo, *actual, procInfo->dummyArguments[index], sc);
350-
}
351-
++index;
352-
}
353-
}
354-
355250
} // namespace Fortran::evaluate

flang/lib/Evaluate/check-expression.cpp

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,8 +1001,8 @@ class IsContiguousHelper
10011001
} else {
10021002
return Base::operator()(ultimate); // use expr
10031003
}
1004-
} else if (semantics::IsPointer(ultimate) ||
1005-
semantics::IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) {
1004+
} else if (semantics::IsPointer(ultimate) || IsAssumedShape(ultimate) ||
1005+
IsAssumedRank(ultimate)) {
10061006
return std::nullopt;
10071007
} else if (ultimate.has<semantics::ObjectEntityDetails>()) {
10081008
return true;
@@ -1446,4 +1446,133 @@ std::optional<parser::Message> CheckStatementFunction(
14461446
return StmtFunctionChecker{sf, context}(expr);
14471447
}
14481448

1449+
std::pair<bool, bool> MayNeedCopyInOut(
1450+
const ActualArgument &actual, FoldingContext &fc) {
1451+
bool mayNeedCopyIn{false};
1452+
bool mayNeedCopyOut{false};
1453+
if (actual.isAlternateReturn()) {
1454+
return {mayNeedCopyIn, mayNeedCopyOut};
1455+
}
1456+
if (!evaluate::IsVariable(actual)) {
1457+
// Actual argument expressions that aren’t variables are copy-in, but
1458+
// not copy-out.
1459+
mayNeedCopyIn = true;
1460+
} else if (bool actualIsArray{actual.Rank() > 0};
1461+
actualIsArray && !IsSimplyContiguous(actual, fc)) {
1462+
// Actual arguments that are variables are copy-in when non-contiguous.
1463+
// They are copy-out when don't have vector subscripts
1464+
mayNeedCopyIn = true;
1465+
if (!HasVectorSubscript(actual)) {
1466+
mayNeedCopyOut = true;
1467+
}
1468+
} else if (ExtractCoarrayRef(actual)) {
1469+
// Coindexed actual args need copy-in and copy-out
1470+
mayNeedCopyIn = true;
1471+
mayNeedCopyOut = true;
1472+
}
1473+
1474+
return {mayNeedCopyIn, mayNeedCopyOut};
1475+
}
1476+
1477+
std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &actual,
1478+
const characteristics::DummyArgument &dummy, FoldingContext &fc) {
1479+
bool mayNeedCopyIn{false};
1480+
bool mayNeedCopyOut{false};
1481+
if (actual.isAlternateReturn()) {
1482+
return {mayNeedCopyIn, mayNeedCopyOut};
1483+
}
1484+
if (!evaluate::IsVariable(actual)) {
1485+
// Actual argument expressions that aren’t variables are copy-in, but
1486+
// not copy-out.
1487+
mayNeedCopyIn = true;
1488+
return {mayNeedCopyIn, mayNeedCopyOut};
1489+
}
1490+
const auto *dummyObj{std::get_if<characteristics::DummyDataObject>(&dummy.u)};
1491+
if (!dummyObj) {
1492+
// Only DummyDataObject has the information we need
1493+
return {mayNeedCopyIn, mayNeedCopyOut};
1494+
}
1495+
// Pass by value, always copy-in, never copy-out
1496+
bool dummyIsValue{
1497+
dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Value)};
1498+
if (dummyIsValue) {
1499+
mayNeedCopyIn = true;
1500+
return {mayNeedCopyIn, mayNeedCopyOut};
1501+
}
1502+
// All the checks below are for arrays
1503+
1504+
bool actualIsAssumedRank{evaluate::IsAssumedRank(actual)};
1505+
bool actualIsArray{actualIsAssumedRank || actual.Rank() > 0};
1506+
bool dummyIsAssumedRank{dummyObj->type.attrs().test(
1507+
characteristics::TypeAndShape::Attr::AssumedRank)};
1508+
bool dummyIsArray{dummyIsAssumedRank || dummyObj->type.Rank() > 0};
1509+
bool treatDummyScalarAsArray{dummyObj->type.Rank() == 0 &&
1510+
dummyObj->ignoreTKR.test(common::IgnoreTKR::Rank)};
1511+
if (!actualIsArray || !(dummyIsArray || treatDummyScalarAsArray)) {
1512+
return {mayNeedCopyIn, mayNeedCopyOut};
1513+
}
1514+
1515+
bool dummyIntentIn{dummyObj->intent == common::Intent::In};
1516+
bool dummyIntentOut{dummyObj->intent == common::Intent::Out};
1517+
auto setCopyIn = [&]() {
1518+
if (!dummyIntentOut) {
1519+
// INTENT(OUT) dummy args never need copy-in
1520+
mayNeedCopyIn = true;
1521+
}
1522+
};
1523+
auto setCopyOut = [&]() {
1524+
if (!dummyIntentIn) {
1525+
// INTENT(IN) dummy args never need copy-out
1526+
mayNeedCopyOut = true;
1527+
}
1528+
};
1529+
1530+
// Check actual contiguity, unless dummy doesn't care
1531+
bool actualTreatAsContiguous{
1532+
dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
1533+
IsSimplyContiguous(actual, fc)};
1534+
bool actualHasVectorSubscript{HasVectorSubscript(actual)};
1535+
bool dummyIsExplicitShape{dummyObj->type.IsExplicitShape()};
1536+
bool dummyIsAssumedSize{dummyObj->type.attrs().test(
1537+
characteristics::TypeAndShape::Attr::AssumedSize)};
1538+
bool dummyIsPolymorphic{dummyObj->type.type().IsPolymorphic()};
1539+
// Explicit shape and assumed size arrays must be contiguous
1540+
bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
1541+
// Polymorphic dummy is descriptor based, so should be able to handle
1542+
// discontigunity.
1543+
(treatDummyScalarAsArray && !dummyIsPolymorphic) ||
1544+
dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
1545+
if (!actualTreatAsContiguous && dummyNeedsContiguity) {
1546+
setCopyIn();
1547+
// Cannot do copy-out for vector subscripts: there could be repeated
1548+
// indices, for example
1549+
if (!actualHasVectorSubscript) {
1550+
setCopyOut();
1551+
}
1552+
return {mayNeedCopyIn, mayNeedCopyOut};
1553+
}
1554+
1555+
bool dummyIsAssumedShape{dummyObj->type.attrs().test(
1556+
characteristics::TypeAndShape::Attr::AssumedShape)};
1557+
bool actualIsAssumedShape{IsAssumedShape(actual)};
1558+
if ((actualIsAssumedRank && dummyIsAssumedRank) ||
1559+
(actualIsAssumedShape && dummyIsAssumedShape)) {
1560+
// Assumed-rank and assumed-shape arrays are represented by descriptors,
1561+
// so don't need to do polymorphic check.
1562+
} else if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
1563+
// flang supports limited cases of passing polymorphic to non-polimorphic.
1564+
// These cases require temporary of non-polymorphic type. (For example,
1565+
// the actual argument could be polymorphic array of child type,
1566+
// while the dummy argument could be non-polymorphic array of parent type.)
1567+
auto actualType{characteristics::TypeAndShape::Characterize(actual, fc)};
1568+
bool actualIsPolymorphic{actualType->type().IsPolymorphic()};
1569+
if (actualIsPolymorphic && !dummyIsPolymorphic) {
1570+
setCopyIn();
1571+
setCopyOut();
1572+
}
1573+
}
1574+
1575+
return {mayNeedCopyIn, mayNeedCopyOut};
1576+
}
1577+
14491578
} // namespace Fortran::evaluate

0 commit comments

Comments
 (0)