13
13
#include " check-omp-structure.h"
14
14
15
15
#include " flang/Common/indirection.h"
16
- #include " flang/Common/template.h"
17
16
#include " flang/Evaluate/expression.h"
18
- #include " flang/Evaluate/match.h"
19
17
#include " flang/Evaluate/rewrite.h"
20
18
#include " flang/Evaluate/tools.h"
21
19
#include " flang/Parser/char-block.h"
@@ -52,127 +50,6 @@ static bool operator!=(const evaluate::Expr<T> &e, const evaluate::Expr<U> &f) {
52
50
return !(e == f);
53
51
}
54
52
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
-
176
53
struct AnalyzedCondStmt {
177
54
SomeExpr cond{evaluate::NullPointer{}}; // Default ctor is deleted
178
55
parser::CharBlock source;
@@ -322,26 +199,6 @@ static std::pair<parser::CharBlock, parser::CharBlock> SplitAssignmentSource(
322
199
llvm_unreachable (" Could not find assignment operator" );
323
200
}
324
201
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
-
345
202
static bool IsCheckForAssociated (const SomeExpr &cond) {
346
203
return GetTopLevelOperationIgnoreResizing (cond).first ==
347
204
operation::Operator::Associated;
@@ -768,8 +625,7 @@ void OmpStructureChecker::CheckAtomicWriteAssignment(
768
625
}
769
626
}
770
627
771
- std::optional<evaluate::Assignment>
772
- OmpStructureChecker::CheckAtomicUpdateAssignment (
628
+ void OmpStructureChecker::CheckAtomicUpdateAssignment (
773
629
const evaluate::Assignment &update, parser::CharBlock source) {
774
630
// [6.0:191:1-7]
775
631
// An update structured block is update-statement, an update statement
@@ -785,46 +641,14 @@ OmpStructureChecker::CheckAtomicUpdateAssignment(
785
641
if (!IsVarOrFunctionRef (atom)) {
786
642
ErrorShouldBeVariable (atom, rsrc);
787
643
// Skip other checks.
788
- return std::nullopt ;
644
+ return ;
789
645
}
790
646
791
647
CheckAtomicVariable (atom, lsrc);
792
648
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
-
825
649
std::pair<operation::Operator, std::vector<SomeExpr>> top{
826
650
operation::Operator::Unknown, {}};
827
- if (auto &&maybeInput{GetConvertInput (rhs)}) {
651
+ if (auto &&maybeInput{GetConvertInput (update. rhs )}) {
828
652
top = GetTopLevelOperationIgnoreResizing (*maybeInput);
829
653
}
830
654
switch (top.first ) {
@@ -841,39 +665,29 @@ std::pair<bool, bool> OmpStructureChecker::CheckAtomicUpdateAssignmentRhs(
841
665
case operation::Operator::Identity:
842
666
break ;
843
667
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 ;
849
671
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 ;
855
675
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 ;
861
679
case operation::Operator::Constant:
862
680
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 ;
868
684
default :
869
685
assert (
870
686
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 ;
877
691
}
878
692
// Check how many times `atom` occurs as an argument, if it's a subexpression
879
693
// of an argument, and collect the non-atom arguments.
@@ -894,48 +708,39 @@ std::pair<bool, bool> OmpStructureChecker::CheckAtomicUpdateAssignmentRhs(
894
708
return count;
895
709
}()};
896
710
897
- bool hasError{false }, tryReassoc{ false } ;
711
+ bool hasError{false };
898
712
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 ());
904
716
hasError = true ;
905
717
}
906
718
if (top.first == operation::Operator::Identity) {
907
719
// This is "x = y".
908
720
assert ((atomCount == 0 || atomCount == 1 ) && " Unexpected count" );
909
721
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 ());
915
725
hasError = true ;
916
726
}
917
727
} else {
918
728
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 ));
927
732
hasError = true ;
928
733
} 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 ));
934
737
hasError = true ;
935
738
}
936
739
}
937
740
938
- return std::make_pair (hasError, tryReassoc);
741
+ if (!hasError) {
742
+ CheckStorageOverlap (atom, nonAtom, source);
743
+ }
939
744
}
940
745
941
746
void OmpStructureChecker::CheckAtomicConditionalUpdateAssignment (
@@ -1038,13 +843,11 @@ void OmpStructureChecker::CheckAtomicUpdateOnly(
1038
843
SourcedActionStmt action{GetActionStmt (&body.front ())};
1039
844
if (auto maybeUpdate{GetEvaluateAssignment (action.stmt )}) {
1040
845
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 );
1044
847
1045
848
using Analysis = parser::OpenMPAtomicConstruct::Analysis;
1046
849
x.analysis = AtomicAnalysis (atom)
1047
- .addOp0 (Analysis::Update, updateAssign )
850
+ .addOp0 (Analysis::Update, maybeUpdate )
1048
851
.addOp1 (Analysis::None);
1049
852
} else if (!IsAssignment (action.stmt )) {
1050
853
context_.Say (
@@ -1160,32 +963,29 @@ void OmpStructureChecker::CheckAtomicUpdateCapture(
1160
963
using Analysis = parser::OpenMPAtomicConstruct::Analysis;
1161
964
int action;
1162
965
1163
- std::optional<evaluate::Assignment> updateAssign{update};
1164
966
if (IsMaybeAtomicWrite (update)) {
1165
967
action = Analysis::Write;
1166
968
CheckAtomicWriteAssignment (update, uact.source );
1167
969
} else {
1168
970
action = Analysis::Update;
1169
- if (auto &&maybe{CheckAtomicUpdateAssignment (update, uact.source )}) {
1170
- updateAssign = maybe;
1171
- }
971
+ CheckAtomicUpdateAssignment (update, uact.source );
1172
972
}
1173
973
CheckAtomicCaptureAssignment (capture, atom, cact.source );
1174
974
1175
- if (IsPointerAssignment (*updateAssign ) != IsPointerAssignment (capture)) {
975
+ if (IsPointerAssignment (update ) != IsPointerAssignment (capture)) {
1176
976
context_.Say (cact.source ,
1177
977
" The update and capture assignments should both be pointer-assignments or both be non-pointer-assignments" _err_en_US);
1178
978
return ;
1179
979
}
1180
980
1181
981
if (GetActionStmt (&body.front ()).stmt == uact.stmt ) {
1182
982
x.analysis = AtomicAnalysis (atom)
1183
- .addOp0 (action, updateAssign )
983
+ .addOp0 (action, update )
1184
984
.addOp1 (Analysis::Read, capture);
1185
985
} else {
1186
986
x.analysis = AtomicAnalysis (atom)
1187
987
.addOp0 (Analysis::Read, capture)
1188
- .addOp1 (action, updateAssign );
988
+ .addOp1 (action, update );
1189
989
}
1190
990
}
1191
991
0 commit comments