-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[flang] Consolidate copy-in/copy-out determination in evaluate framework #151408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 114 commits
54c0158
808fb20
28bc5bd
f44b945
ffd6563
fb3a93c
6e052f4
b914f79
527b2d7
9c1755b
3198e90
0378a5f
5d5418a
2cce4bb
f6f64ca
4a6d402
698b865
de06d25
cf42b12
9373b90
889f7c1
905c315
d9452c3
fab0c75
9e47176
12ca5a8
3126a8c
c2fc2e9
902894c
c3ef243
143f7bd
6d0935c
15db3f8
58764a6
eb27031
3ec01ad
889d514
040572f
04daa83
5136de2
7e448f0
546af8f
068216f
3f36c9f
070ffd1
5062266
312cf25
bc2e17a
d026f49
55b8e0e
a781512
384bc2e
b3fd99a
258fc64
a197190
30d36b0
47f2656
aa65a3e
d285ded
413eafd
4590955
d026e0d
0289de2
2464dd3
f76c9e3
b12d2c8
24c2040
a760320
6868af8
e69fa22
1bd1822
f752112
7aa458c
d1a8d7c
57fd73e
b7dab28
0239c1d
95f0aac
02ee2b7
4938d03
0faca16
6f828b3
450a5d7
0be16a4
68722d6
dd9560a
a8c4dcf
be85c20
36a3049
69d7ca0
f6bb3df
fce3da4
e36a534
6c5a1e2
11c27fb
73e9db7
4ab82fd
3baa7da
0050af6
3e281e4
bd61c2a
335a463
b8e78b1
9be6a3e
36993cd
629b67d
6138cc8
02eec4c
f5be38b
5ddac27
b5ff581
6284e57
def4089
09e507a
e54a8d2
d22d27c
84ce8ad
45eaddc
fc28315
c7a740c
0d9302e
3bb10d5
395e4ca
b7c35c9
d22d76f
991df0e
99aaa2b
3c64f13
0c5dc79
d79c588
8f768ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -203,6 +203,12 @@ class TypeAndShape { | |
| std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes( | ||
| FoldingContext &) const; | ||
|
|
||
| bool IsExplicitShape() const { | ||
| // If it's array and no special attributes are set, then must be | ||
| // explicit shape. | ||
| return Rank() > 0 && attrs_.none(); | ||
| } | ||
|
|
||
| // called by Fold() to rewrite in place | ||
| TypeAndShape &Rewrite(FoldingContext &); | ||
|
|
||
|
|
@@ -248,6 +254,7 @@ struct DummyDataObject { | |
| bool CanBePassedViaImplicitInterface(std::string *whyNot = nullptr) const; | ||
| bool IsPassedByDescriptor(bool isBindC) const; | ||
| llvm::raw_ostream &Dump(llvm::raw_ostream &) const; | ||
| bool IsArray() const; | ||
|
||
|
|
||
| TypeAndShape type; | ||
| std::vector<Expr<SubscriptInteger>> coshape; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1001,8 +1001,8 @@ class IsContiguousHelper | |
| } else { | ||
| return Base::operator()(ultimate); // use expr | ||
| } | ||
| } else if (semantics::IsPointer(ultimate) || | ||
| semantics::IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) { | ||
| } else if (semantics::IsPointer(ultimate) || IsAssumedShape(ultimate) || | ||
| IsAssumedRank(ultimate)) { | ||
| return std::nullopt; | ||
| } else if (ultimate.has<semantics::ObjectEntityDetails>()) { | ||
| return true; | ||
|
|
@@ -1282,9 +1282,21 @@ std::optional<bool> IsContiguous(const A &x, FoldingContext &context, | |
| } | ||
| } | ||
|
|
||
| std::optional<bool> IsContiguous(const ActualArgument &actual, | ||
| FoldingContext &fc, bool namedConstantSectionsAreContiguous, | ||
| bool firstDimensionStride1) { | ||
| auto *expr{actual.UnwrapExpr()}; | ||
| return expr && | ||
| IsContiguous( | ||
| *expr, fc, namedConstantSectionsAreContiguous, firstDimensionStride1); | ||
| } | ||
|
|
||
| template std::optional<bool> IsContiguous(const Expr<SomeType> &, | ||
| FoldingContext &, bool namedConstantSectionsAreContiguous, | ||
| bool firstDimensionStride1); | ||
| template std::optional<bool> IsContiguous(const ActualArgument &, | ||
| FoldingContext &, bool namedConstantSectionsAreContiguous, | ||
| bool firstDimensionStride1); | ||
| template std::optional<bool> IsContiguous(const ArrayRef &, FoldingContext &, | ||
| bool namedConstantSectionsAreContiguous, bool firstDimensionStride1); | ||
| template std::optional<bool> IsContiguous(const Substring &, FoldingContext &, | ||
|
|
@@ -1434,4 +1446,212 @@ std::optional<parser::Message> CheckStatementFunction( | |
| return StmtFunctionChecker{sf, context}(expr); | ||
| } | ||
|
|
||
| // Helper class for cheching differences between actual and dummy arguments | ||
eugeneepshteyn marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| class CopyInOutExplicitInterface { | ||
| public: | ||
| explicit CopyInOutExplicitInterface(FoldingContext &fc, | ||
| const ActualArgument &actual, | ||
| const characteristics::DummyDataObject &dummyObj) | ||
| : fc_{fc}, actual_{actual}, dummyObj_{dummyObj} {} | ||
|
|
||
| // Returns true, if actual and dummy have different contiguity requirements | ||
| bool HaveContiguityDifferences() const { | ||
| // Check actual contiguity, unless dummy doesn't care | ||
| bool dummyTreatAsArray{dummyObj_.ignoreTKR.test(common::IgnoreTKR::Rank)}; | ||
| bool actualTreatAsContiguous{ | ||
| dummyObj_.ignoreTKR.test(common::IgnoreTKR::Contiguous) || | ||
| IsSimplyContiguous(actual_, fc_)}; | ||
| bool dummyIsExplicitShape{dummyObj_.type.IsExplicitShape()}; | ||
| bool dummyIsAssumedSize{dummyObj_.type.attrs().test( | ||
| characteristics::TypeAndShape::Attr::AssumedSize)}; | ||
| bool dummyIsPolymorphic{dummyObj_.type.type().IsPolymorphic()}; | ||
| // type(*) with IGNORE_TKR(tkr) is often used to interface with C "void*". | ||
| // Since the other languages don't know about Fortran's discontiguity | ||
| // handling, such cases should require contiguity. | ||
| bool dummyIsVoidStar{dummyObj_.type.type().IsAssumedType() && | ||
| dummyObj_.ignoreTKR.test(common::IgnoreTKR::Type) && | ||
| dummyObj_.ignoreTKR.test(common::IgnoreTKR::Rank) && | ||
| dummyObj_.ignoreTKR.test(common::IgnoreTKR::Kind)}; | ||
| // Explicit shape and assumed size arrays must be contiguous | ||
| bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize || | ||
| (dummyTreatAsArray && !dummyIsPolymorphic) || dummyIsVoidStar || | ||
| dummyObj_.attrs.test( | ||
| characteristics::DummyDataObject::Attr::Contiguous)}; | ||
| if (!actualTreatAsContiguous && dummyNeedsContiguity) { | ||
|
||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| // Returns true, if actual and dummy have polymorphic differences | ||
| bool HavePolymorphicDifferences() const { | ||
| bool dummyIsAssumedRank{dummyObj_.type.attrs().test( | ||
| characteristics::TypeAndShape::Attr::AssumedRank)}; | ||
| bool actualIsAssumedRank{semantics::IsAssumedRank(actual_)}; | ||
| bool dummyIsAssumedShape{dummyObj_.type.attrs().test( | ||
| characteristics::TypeAndShape::Attr::AssumedShape)}; | ||
| bool actualIsAssumedShape{semantics::IsAssumedShape(actual_)}; | ||
| if ((actualIsAssumedRank && dummyIsAssumedRank) || | ||
| (actualIsAssumedShape && dummyIsAssumedShape)) { | ||
| // Assumed-rank and assumed-shape arrays are represented by descriptors, | ||
| // so don't need to do polymorphic check. | ||
| } else if (!dummyObj_.ignoreTKR.test(common::IgnoreTKR::Type)) { | ||
| // flang supports limited cases of passing polymorphic to non-polimorphic. | ||
| // These cases require temporary of non-polymorphic type. (For example, | ||
| // the actual argument could be polymorphic array of child type, | ||
| // while the dummy argument could be non-polymorphic array of parent | ||
| // type.) | ||
| bool dummyIsPolymorphic{dummyObj_.type.type().IsPolymorphic()}; | ||
| auto actualType{ | ||
| characteristics::TypeAndShape::Characterize(actual_, fc_)}; | ||
| bool actualIsPolymorphic{ | ||
| actualType && actualType->type().IsPolymorphic()}; | ||
| if (actualIsPolymorphic && !dummyIsPolymorphic) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| bool HaveArrayArgs() const { | ||
| bool dummyTreatAsArray{dummyObj_.ignoreTKR.test(common::IgnoreTKR::Rank)}; | ||
| return actual_.IsArray() && (dummyObj_.IsArray() || dummyTreatAsArray); | ||
| } | ||
|
|
||
| bool PassByValue() const { | ||
| return dummyObj_.attrs.test(characteristics::DummyDataObject::Attr::Value); | ||
| } | ||
|
|
||
| bool HaveCoarrayDifferences() const { | ||
| return ExtractCoarrayRef(actual_) && dummyObj_.type.corank() == 0; | ||
| } | ||
|
|
||
| bool HasIntentOut() const { return dummyObj_.intent == common::Intent::Out; } | ||
|
|
||
| bool HasIntentIn() const { return dummyObj_.intent == common::Intent::In; } | ||
|
|
||
| private: | ||
| FoldingContext &fc_; | ||
| const ActualArgument &actual_; | ||
| const characteristics::DummyDataObject &dummyObj_; | ||
| }; | ||
|
|
||
| static bool MayNeedCopyIn(FoldingContext &fc, const ActualArgument &actual, | ||
|
||
| const characteristics::DummyDataObject *dummyObj) { | ||
| if (!evaluate::IsVariable(actual)) { | ||
| // Actual argument expressions that aren’t variables are copy-in, but | ||
| // not copy-out. | ||
| return true; | ||
| } | ||
| if (dummyObj) { // Explicit interface | ||
| CopyInOutExplicitInterface check{fc, actual, *dummyObj}; | ||
| if (check.HasIntentOut()) { | ||
| // INTENT(OUT) dummy args never need copy-in | ||
| return false; | ||
| } | ||
| if (check.PassByValue()) { | ||
| // Pass by value, always copy-in, never copy-out | ||
| return true; | ||
| } | ||
| if (check.HaveCoarrayDifferences()) { | ||
| return true; | ||
| } | ||
| // Note: contiguity and polymorphic checks deal with array arguments | ||
| if (!check.HaveArrayArgs()) { | ||
| return false; | ||
| } | ||
| if (check.HaveContiguityDifferences()) { | ||
| return true; | ||
| } | ||
| if (check.HavePolymorphicDifferences()) { | ||
| return true; | ||
| } | ||
| } else { // Implicit interface | ||
| if (ExtractCoarrayRef(actual)) { | ||
| // Coindexed actual args may need copy-in and copy-out with implicit | ||
| // interface | ||
| return true; | ||
| } | ||
| if (!IsSimplyContiguous(actual, fc)) { | ||
| // Actual arguments that are variables are copy-in when non-contiguous. | ||
| return true; | ||
| } | ||
| } | ||
| // For everything else assume no copy-in | ||
| return false; | ||
| } | ||
|
|
||
| static bool MayNeedCopyOut(FoldingContext &fc, const ActualArgument &actual, | ||
| const characteristics::DummyDataObject *dummyObj) { | ||
| if (!evaluate::IsVariable(actual)) { | ||
| // Expressions are never copy-out | ||
| return false; | ||
| } | ||
| if (dummyObj) { // Explict interface | ||
| CopyInOutExplicitInterface check{fc, actual, *dummyObj}; | ||
| if (check.HasIntentIn()) { | ||
| // INTENT(IN) dummy args never need copy-out | ||
| return false; | ||
| } | ||
| if (check.PassByValue()) { | ||
| // Pass by value is never copy-out | ||
| return false; | ||
| } | ||
| if (check.HaveCoarrayDifferences()) { | ||
| return true; | ||
| } | ||
| // Note: contiguity and polymorphic checks deal with array arguments | ||
| if (!check.HaveArrayArgs()) { | ||
| return false; | ||
| } | ||
| if (check.HaveContiguityDifferences()) { | ||
| return true; | ||
| } | ||
| if (check.HavePolymorphicDifferences()) { | ||
| return true; | ||
| } | ||
| } else { // Implicit interface | ||
| if (ExtractCoarrayRef(actual)) { | ||
| // Coindexed actual args may need copy-in and copy-out with implicit | ||
| // interface | ||
| return true; | ||
| } | ||
| if (!IsSimplyContiguous(actual, fc)) { | ||
| // Vector subscripts could refer to duplicate elements, can't copy out | ||
| return !HasVectorSubscript(actual); | ||
| } | ||
| } | ||
| // For everything else assume no copy-out | ||
| return false; | ||
| } | ||
|
|
||
| // If forCopyOut is false, returns if a particular actual/dummy argument | ||
| // combination may need a temporary creation with copy-in operation. If | ||
| // forCopyOut is true, returns the same for copy-out operation. For | ||
| // procedures with explicit interface, it's expected that "dummy" is not null. | ||
| // For procedures with implicit interface dummy may be null. | ||
| // | ||
| // Note that these copy-in and copy-out checks are done from the caller's | ||
| // perspective, meaning that for copy-in the caller need to do the copy | ||
| // before calling the callee. Similarly, for copy-out the caller is expected | ||
| // to do the copy after the callee returns. | ||
| bool MayNeedCopy(const ActualArgument *actual, | ||
| const characteristics::DummyArgument *dummy, FoldingContext &fc, | ||
| bool forCopyOut) { | ||
| if (!actual) { | ||
| return false; | ||
| } | ||
| if (actual->isAlternateReturn()) { | ||
| return false; | ||
| } | ||
| const auto *dummyObj{dummy | ||
| ? std::get_if<characteristics::DummyDataObject>(&dummy->u) | ||
| : nullptr}; | ||
| if (forCopyOut) { | ||
| return MayNeedCopyOut(fc, *actual, dummyObj); | ||
| } else { | ||
| return MayNeedCopyIn(fc, *actual, dummyObj); | ||
| } | ||
| } | ||
|
|
||
| } // namespace Fortran::evaluate | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
IsArray()redundant withRank() > 0? If not, how do they differ -- assumed-rank? (In the terminology of the Fortran standard, "assumed-rank" dummy arguments are actually not arrays.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid confusion, I'll move the checks elsewhere.