1313#include " check-omp-structure.h"
1414
1515#include " flang/Common/indirection.h"
16- #include " flang/Common/template.h"
1716#include " flang/Evaluate/expression.h"
18- #include " flang/Evaluate/match.h"
1917#include " flang/Evaluate/rewrite.h"
2018#include " flang/Evaluate/tools.h"
2119#include " flang/Parser/char-block.h"
@@ -52,127 +50,6 @@ static bool operator!=(const evaluate::Expr<T> &e, const evaluate::Expr<U> &f) {
5250 return !(e == f);
5351}
5452
55- namespace {
56- template <typename ...> struct IsIntegral {
57- static constexpr bool value{false };
58- };
59-
60- template <common::TypeCategory C, int K>
61- struct IsIntegral <evaluate::Type<C, K>> {
62- static constexpr bool value{//
63- C == common::TypeCategory::Integer ||
64- C == common::TypeCategory::Unsigned ||
65- C == common::TypeCategory::Logical};
66- };
67-
68- template <typename T> constexpr bool is_integral_v{IsIntegral<T>::value};
69-
70- template <typename T, typename Op0, typename Op1>
71- using ReassocOpBase = evaluate::match::AnyOfPattern< //
72- evaluate::match::Add<T, Op0, Op1>, //
73- evaluate::match::Mul<T, Op0, Op1>>;
74-
75- template <typename T, typename Op0, typename Op1>
76- struct ReassocOp : public ReassocOpBase <T, Op0, Op1> {
77- using Base = ReassocOpBase<T, Op0, Op1>;
78- using Base::Base;
79- };
80-
81- template <typename T, typename Op0, typename Op1>
82- ReassocOp<T, Op0, Op1> reassocOp (const Op0 &op0, const Op1 &op1) {
83- return ReassocOp<T, Op0, Op1>(op0, op1);
84- }
85- } // namespace
86-
87- struct ReassocRewriter : public evaluate ::rewrite::Identity {
88- using Id = evaluate::rewrite::Identity;
89- using Id::operator ();
90- struct NonIntegralTag {};
91-
92- ReassocRewriter (const SomeExpr &atom) : atom_(atom) {}
93-
94- // Try to find cases where the input expression is of the form
95- // (1) (a . b) . c, or
96- // (2) a . (b . c),
97- // where . denotes an associative operation (currently + or *), and a, b, c
98- // are some subexpresions.
99- // If one of the operands in the nested operation is the atomic variable
100- // (with some possible type conversions applied to it), bring it to the
101- // top-level operation, and move the top-level operand into the nested
102- // operation.
103- // For example, assuming x is the atomic variable:
104- // (a + x) + b -> (a + b) + x, i.e. (conceptually) swap x and b.
105- template <typename T, typename U,
106- typename = std::enable_if_t <is_integral_v<T>>>
107- evaluate::Expr<T> operator ()(evaluate::Expr<T> &&x, const U &u) {
108- // As per the above comment, there are 3 subexpressions involved in this
109- // transformation. A match::Expr<T> will match evaluate::Expr<U> when T is
110- // same as U, plus it will store a pointer (ref) to the matched expression.
111- // When the match is successful, the sub[i].ref will point to a, b, x (in
112- // some order) from the example above.
113- evaluate::match::Expr<T> sub[3 ];
114- auto inner{reassocOp<T>(sub[0 ], sub[1 ])};
115- auto outer1{reassocOp<T>(inner, sub[2 ])}; // inner + something
116- auto outer2{reassocOp<T>(sub[2 ], inner)}; // something + inner
117- // There is no way to ensure that the outer operation is the same as
118- // the inner one. They are matched independently, so we need to compare
119- // the index in the member variant that represents the matched type.
120- if ((match (outer1, x) && outer1.ref .index () == inner.ref .index ()) ||
121- (match (outer2, x) && outer2.ref .index () == inner.ref .index ())) {
122- size_t atomIdx{[&]() { // sub[atomIdx] will be the atom.
123- size_t idx;
124- for (idx = 0 ; idx != 3 ; ++idx) {
125- if (IsAtom (*sub[idx].ref )) {
126- break ;
127- }
128- }
129- return idx;
130- }()};
131-
132- if (atomIdx > 2 ) {
133- return Id::operator ()(std::move (x), u);
134- }
135- return common::visit (
136- [&](auto &&s) {
137- using Expr = evaluate::Expr<T>;
138- using TypeS = llvm::remove_cvref_t <decltype (s)>;
139- // This visitor has to be semantically correct for all possible
140- // types of s even though at runtime s will only be one of the
141- // matched types.
142- // Limit the construction to the operation types that we tried
143- // to match (otherwise TypeS(op1, op2) would fail for non-binary
144- // operations).
145- if constexpr (common::HasMember<TypeS,
146- typename decltype (outer1)::MatchTypes>) {
147- Expr atom{*sub[atomIdx].ref };
148- Expr op1{*sub[(atomIdx + 1 ) % 3 ].ref };
149- Expr op2{*sub[(atomIdx + 2 ) % 3 ].ref };
150- return Expr (
151- TypeS (atom, Expr (TypeS (std::move (op1), std::move (op2)))));
152- } else {
153- return Expr (TypeS (s));
154- }
155- },
156- evaluate::match::deparen (x).u );
157- }
158- return Id::operator ()(std::move (x), u);
159- }
160-
161- template <typename T, typename U,
162- typename = std::enable_if_t <!is_integral_v<T>>>
163- evaluate::Expr<T> operator ()(
164- evaluate::Expr<T> &&x, const U &u, NonIntegralTag = {}) {
165- return Id::operator ()(std::move (x), u);
166- }
167-
168- private:
169- template <typename T> bool IsAtom (const evaluate::Expr<T> &x) const {
170- return IsSameOrConvertOf (evaluate::AsGenericExpr (AsRvalue (x)), atom_);
171- }
172-
173- const SomeExpr &atom_;
174- };
175-
17653struct AnalyzedCondStmt {
17754 SomeExpr cond{evaluate::NullPointer{}}; // Default ctor is deleted
17855 parser::CharBlock source;
@@ -322,26 +199,6 @@ static std::pair<parser::CharBlock, parser::CharBlock> SplitAssignmentSource(
322199 llvm_unreachable (" Could not find assignment operator" );
323200}
324201
325- static std::vector<SomeExpr> GetNonAtomExpressions (
326- const SomeExpr &atom, const std::vector<SomeExpr> &exprs) {
327- std::vector<SomeExpr> nonAtom;
328- for (const SomeExpr &e : exprs) {
329- if (!IsSameOrConvertOf (e, atom)) {
330- nonAtom.push_back (e);
331- }
332- }
333- return nonAtom;
334- }
335-
336- static std::vector<SomeExpr> GetNonAtomArguments (
337- const SomeExpr &atom, const SomeExpr &expr) {
338- if (auto &&maybe{GetConvertInput (expr)}) {
339- return GetNonAtomExpressions (
340- atom, GetTopLevelOperationIgnoreResizing (*maybe).second );
341- }
342- return {};
343- }
344-
345202static bool IsCheckForAssociated (const SomeExpr &cond) {
346203 return GetTopLevelOperationIgnoreResizing (cond).first ==
347204 operation::Operator::Associated;
@@ -768,8 +625,7 @@ void OmpStructureChecker::CheckAtomicWriteAssignment(
768625 }
769626}
770627
771- std::optional<evaluate::Assignment>
772- OmpStructureChecker::CheckAtomicUpdateAssignment (
628+ void OmpStructureChecker::CheckAtomicUpdateAssignment (
773629 const evaluate::Assignment &update, parser::CharBlock source) {
774630 // [6.0:191:1-7]
775631 // An update structured block is update-statement, an update statement
@@ -785,46 +641,14 @@ OmpStructureChecker::CheckAtomicUpdateAssignment(
785641 if (!IsVarOrFunctionRef (atom)) {
786642 ErrorShouldBeVariable (atom, rsrc);
787643 // Skip other checks.
788- return std:: nullopt ;
644+ return ;
789645 }
790646
791647 CheckAtomicVariable (atom, lsrc);
792648
793- auto [hasErrors, tryReassoc]{CheckAtomicUpdateAssignmentRhs (
794- atom, update.rhs , source, /* suppressDiagnostics=*/ true )};
795-
796- if (!hasErrors) {
797- CheckStorageOverlap (atom, GetNonAtomArguments (atom, update.rhs ), source);
798- return std::nullopt ;
799- } else if (tryReassoc) {
800- ReassocRewriter ra (atom);
801- SomeExpr raRhs{evaluate::rewrite::Mutator (ra)(update.rhs )};
802-
803- std::tie (hasErrors, tryReassoc) = CheckAtomicUpdateAssignmentRhs (
804- atom, raRhs, source, /* suppressDiagnostics=*/ true );
805- if (!hasErrors) {
806- CheckStorageOverlap (atom, GetNonAtomArguments (atom, raRhs), source);
807-
808- evaluate::Assignment raAssign (update);
809- raAssign.rhs = raRhs;
810- return raAssign;
811- }
812- }
813-
814- // This is guaranteed to report errors.
815- CheckAtomicUpdateAssignmentRhs (
816- atom, update.rhs , source, /* suppressDiagnostics=*/ false );
817- return std::nullopt ;
818- }
819-
820- std::pair<bool , bool > OmpStructureChecker::CheckAtomicUpdateAssignmentRhs (
821- const SomeExpr &atom, const SomeExpr &rhs, parser::CharBlock source,
822- bool suppressDiagnostics) {
823- auto [lsrc, rsrc]{SplitAssignmentSource (source)};
824-
825649 std::pair<operation::Operator, std::vector<SomeExpr>> top{
826650 operation::Operator::Unknown, {}};
827- if (auto &&maybeInput{GetConvertInput (rhs)}) {
651+ if (auto &&maybeInput{GetConvertInput (update. rhs )}) {
828652 top = GetTopLevelOperationIgnoreResizing (*maybeInput);
829653 }
830654 switch (top.first ) {
@@ -841,39 +665,29 @@ std::pair<bool, bool> OmpStructureChecker::CheckAtomicUpdateAssignmentRhs(
841665 case operation::Operator::Identity:
842666 break ;
843667 case operation::Operator::Call:
844- if (!suppressDiagnostics) {
845- context_.Say (source,
846- " A call to this function is not a valid ATOMIC UPDATE operation" _err_en_US);
847- }
848- return std::make_pair (true , false );
668+ context_.Say (source,
669+ " A call to this function is not a valid ATOMIC UPDATE operation" _err_en_US);
670+ return ;
849671 case operation::Operator::Convert:
850- if (!suppressDiagnostics) {
851- context_.Say (source,
852- " An implicit or explicit type conversion is not a valid ATOMIC UPDATE operation" _err_en_US);
853- }
854- return std::make_pair (true , false );
672+ context_.Say (source,
673+ " An implicit or explicit type conversion is not a valid ATOMIC UPDATE operation" _err_en_US);
674+ return ;
855675 case operation::Operator::Intrinsic:
856- if (!suppressDiagnostics) {
857- context_.Say (source,
858- " This intrinsic function is not a valid ATOMIC UPDATE operation" _err_en_US);
859- }
860- return std::make_pair (true , false );
676+ context_.Say (source,
677+ " This intrinsic function is not a valid ATOMIC UPDATE operation" _err_en_US);
678+ return ;
861679 case operation::Operator::Constant:
862680 case operation::Operator::Unknown:
863- if (!suppressDiagnostics) {
864- context_.Say (
865- source, " This is not a valid ATOMIC UPDATE operation" _err_en_US);
866- }
867- return std::make_pair (true , false );
681+ context_.Say (
682+ source, " This is not a valid ATOMIC UPDATE operation" _err_en_US);
683+ return ;
868684 default :
869685 assert (
870686 top.first != operation::Operator::Identity && " Handle this separately" );
871- if (!suppressDiagnostics) {
872- context_.Say (source,
873- " The %s operator is not a valid ATOMIC UPDATE operation" _err_en_US,
874- operation::ToString (top.first ));
875- }
876- return std::make_pair (true , false );
687+ context_.Say (source,
688+ " The %s operator is not a valid ATOMIC UPDATE operation" _err_en_US,
689+ operation::ToString (top.first ));
690+ return ;
877691 }
878692 // Check how many times `atom` occurs as an argument, if it's a subexpression
879693 // of an argument, and collect the non-atom arguments.
@@ -894,48 +708,39 @@ std::pair<bool, bool> OmpStructureChecker::CheckAtomicUpdateAssignmentRhs(
894708 return count;
895709 }()};
896710
897- bool hasError{false }, tryReassoc{ false } ;
711+ bool hasError{false };
898712 if (subExpr) {
899- if (!suppressDiagnostics) {
900- context_.Say (rsrc,
901- " The atomic variable %s cannot be a proper subexpression of an argument (here: %s) in the update operation" _err_en_US,
902- atom.AsFortran (), subExpr->AsFortran ());
903- }
713+ context_.Say (rsrc,
714+ " The atomic variable %s cannot be a proper subexpression of an argument (here: %s) in the update operation" _err_en_US,
715+ atom.AsFortran (), subExpr->AsFortran ());
904716 hasError = true ;
905717 }
906718 if (top.first == operation::Operator::Identity) {
907719 // This is "x = y".
908720 assert ((atomCount == 0 || atomCount == 1 ) && " Unexpected count" );
909721 if (atomCount == 0 ) {
910- if (!suppressDiagnostics) {
911- context_.Say (rsrc,
912- " The atomic variable %s should appear as an argument in the update operation" _err_en_US,
913- atom.AsFortran ());
914- }
722+ context_.Say (rsrc,
723+ " The atomic variable %s should appear as an argument in the update operation" _err_en_US,
724+ atom.AsFortran ());
915725 hasError = true ;
916726 }
917727 } else {
918728 if (atomCount == 0 ) {
919- if (!suppressDiagnostics) {
920- context_.Say (rsrc,
921- " The atomic variable %s should appear as an argument of the top-level %s operator" _err_en_US,
922- atom.AsFortran (), operation::ToString (top.first ));
923- }
924- // If `atom` is a proper subexpression, and it not present as an
925- // argument on its own, reassociation may be able to help.
926- tryReassoc = subExpr.has_value ();
729+ context_.Say (rsrc,
730+ " The atomic variable %s should appear as an argument of the top-level %s operator" _err_en_US,
731+ atom.AsFortran (), operation::ToString (top.first ));
927732 hasError = true ;
928733 } else if (atomCount > 1 ) {
929- if (!suppressDiagnostics) {
930- context_.Say (rsrc,
931- " The atomic variable %s should be exactly one of the arguments of the top-level %s operator" _err_en_US,
932- atom.AsFortran (), operation::ToString (top.first ));
933- }
734+ context_.Say (rsrc,
735+ " The atomic variable %s should be exactly one of the arguments of the top-level %s operator" _err_en_US,
736+ atom.AsFortran (), operation::ToString (top.first ));
934737 hasError = true ;
935738 }
936739 }
937740
938- return std::make_pair (hasError, tryReassoc);
741+ if (!hasError) {
742+ CheckStorageOverlap (atom, nonAtom, source);
743+ }
939744}
940745
941746void OmpStructureChecker::CheckAtomicConditionalUpdateAssignment (
@@ -1038,13 +843,11 @@ void OmpStructureChecker::CheckAtomicUpdateOnly(
1038843 SourcedActionStmt action{GetActionStmt (&body.front ())};
1039844 if (auto maybeUpdate{GetEvaluateAssignment (action.stmt )}) {
1040845 const SomeExpr &atom{maybeUpdate->lhs };
1041- auto maybeAssign{
1042- CheckAtomicUpdateAssignment (*maybeUpdate, action.source )};
1043- auto &updateAssign{maybeAssign.has_value () ? maybeAssign : maybeUpdate};
846+ CheckAtomicUpdateAssignment (*maybeUpdate, action.source );
1044847
1045848 using Analysis = parser::OpenMPAtomicConstruct::Analysis;
1046849 x.analysis = AtomicAnalysis (atom)
1047- .addOp0 (Analysis::Update, updateAssign )
850+ .addOp0 (Analysis::Update, maybeUpdate )
1048851 .addOp1 (Analysis::None);
1049852 } else if (!IsAssignment (action.stmt )) {
1050853 context_.Say (
@@ -1160,32 +963,29 @@ void OmpStructureChecker::CheckAtomicUpdateCapture(
1160963 using Analysis = parser::OpenMPAtomicConstruct::Analysis;
1161964 int action;
1162965
1163- std::optional<evaluate::Assignment> updateAssign{update};
1164966 if (IsMaybeAtomicWrite (update)) {
1165967 action = Analysis::Write;
1166968 CheckAtomicWriteAssignment (update, uact.source );
1167969 } else {
1168970 action = Analysis::Update;
1169- if (auto &&maybe{CheckAtomicUpdateAssignment (update, uact.source )}) {
1170- updateAssign = maybe;
1171- }
971+ CheckAtomicUpdateAssignment (update, uact.source );
1172972 }
1173973 CheckAtomicCaptureAssignment (capture, atom, cact.source );
1174974
1175- if (IsPointerAssignment (*updateAssign ) != IsPointerAssignment (capture)) {
975+ if (IsPointerAssignment (update ) != IsPointerAssignment (capture)) {
1176976 context_.Say (cact.source ,
1177977 " The update and capture assignments should both be pointer-assignments or both be non-pointer-assignments" _err_en_US);
1178978 return ;
1179979 }
1180980
1181981 if (GetActionStmt (&body.front ()).stmt == uact.stmt ) {
1182982 x.analysis = AtomicAnalysis (atom)
1183- .addOp0 (action, updateAssign )
983+ .addOp0 (action, update )
1184984 .addOp1 (Analysis::Read, capture);
1185985 } else {
1186986 x.analysis = AtomicAnalysis (atom)
1187987 .addOp0 (Analysis::Read, capture)
1188- .addOp1 (action, updateAssign );
988+ .addOp1 (action, update );
1189989 }
1190990}
1191991
0 commit comments