1 | //===-- lib/Semantics/check-omp-structure.h ---------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // OpenMP structure validity check list |
10 | // 1. invalid clauses on directive |
11 | // 2. invalid repeated clauses on directive |
12 | // 3. TODO: invalid nesting of regions |
13 | |
14 | #ifndef FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ |
15 | #define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ |
16 | |
17 | #include "check-directive-structure.h" |
18 | #include "flang/Common/enum-set.h" |
19 | #include "flang/Parser/parse-tree.h" |
20 | #include "flang/Semantics/openmp-directive-sets.h" |
21 | #include "flang/Semantics/semantics.h" |
22 | #include "llvm/Frontend/OpenMP/OMPConstants.h" |
23 | |
24 | using OmpClauseSet = |
25 | Fortran::common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>; |
26 | |
27 | #define GEN_FLANG_DIRECTIVE_CLAUSE_SETS |
28 | #include "llvm/Frontend/OpenMP/OMP.inc" |
29 | |
30 | namespace llvm { |
31 | namespace omp { |
32 | static OmpClauseSet privateSet{ |
33 | Clause::OMPC_private, Clause::OMPC_firstprivate, Clause::OMPC_lastprivate}; |
34 | static OmpClauseSet privateReductionSet{ |
35 | OmpClauseSet{Clause::OMPC_reduction} | privateSet}; |
36 | // omp.td cannot differentiate allowed/not allowed clause list for few |
37 | // directives for fortran. nowait is not allowed on begin directive clause list |
38 | // for below list of directives. Directives with conflicting list of clauses are |
39 | // included in below list. |
40 | static const OmpDirectiveSet noWaitClauseNotAllowedSet{ |
41 | Directive::OMPD_do, |
42 | Directive::OMPD_do_simd, |
43 | Directive::OMPD_sections, |
44 | Directive::OMPD_single, |
45 | Directive::OMPD_workshare, |
46 | }; |
47 | } // namespace omp |
48 | } // namespace llvm |
49 | |
50 | namespace Fortran::semantics { |
51 | struct AnalyzedCondStmt; |
52 | |
53 | // Mapping from 'Symbol' to 'Source' to keep track of the variables |
54 | // used in multiple clauses |
55 | using SymbolSourceMap = std::multimap<const Symbol *, parser::CharBlock>; |
56 | // Multimap to check the triple <current_dir, enclosing_dir, enclosing_clause> |
57 | using DirectivesClauseTriple = std::multimap<llvm::omp::Directive, |
58 | std::pair<llvm::omp::Directive, const OmpClauseSet>>; |
59 | |
60 | class OmpStructureChecker |
61 | : public DirectiveStructureChecker<llvm::omp::Directive, llvm::omp::Clause, |
62 | parser::OmpClause, llvm::omp::Clause_enumSize> { |
63 | public: |
64 | using Base = DirectiveStructureChecker<llvm::omp::Directive, |
65 | llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>; |
66 | |
67 | OmpStructureChecker(SemanticsContext &context) |
68 | : DirectiveStructureChecker(context, |
69 | #define GEN_FLANG_DIRECTIVE_CLAUSE_MAP |
70 | #include "llvm/Frontend/OpenMP/OMP.inc" |
71 | ) { |
72 | } |
73 | using llvmOmpClause = const llvm::omp::Clause; |
74 | |
75 | void Enter(const parser::OpenMPConstruct &); |
76 | void Leave(const parser::OpenMPConstruct &); |
77 | void Enter(const parser::OpenMPInteropConstruct &); |
78 | void Leave(const parser::OpenMPInteropConstruct &); |
79 | void Enter(const parser::OpenMPDeclarativeConstruct &); |
80 | void Leave(const parser::OpenMPDeclarativeConstruct &); |
81 | |
82 | void Enter(const parser::OpenMPLoopConstruct &); |
83 | void Leave(const parser::OpenMPLoopConstruct &); |
84 | void Enter(const parser::OmpEndLoopDirective &); |
85 | void Leave(const parser::OmpEndLoopDirective &); |
86 | |
87 | void Enter(const parser::OpenMPAssumeConstruct &); |
88 | void Leave(const parser::OpenMPAssumeConstruct &); |
89 | void Enter(const parser::OpenMPDeclarativeAssumes &); |
90 | void Leave(const parser::OpenMPDeclarativeAssumes &); |
91 | void Enter(const parser::OpenMPBlockConstruct &); |
92 | void Leave(const parser::OpenMPBlockConstruct &); |
93 | void Leave(const parser::OmpBeginBlockDirective &); |
94 | void Enter(const parser::OmpEndBlockDirective &); |
95 | void Leave(const parser::OmpEndBlockDirective &); |
96 | |
97 | void Enter(const parser::OpenMPSectionsConstruct &); |
98 | void Leave(const parser::OpenMPSectionsConstruct &); |
99 | void Enter(const parser::OmpEndSectionsDirective &); |
100 | void Leave(const parser::OmpEndSectionsDirective &); |
101 | |
102 | void Enter(const parser::OmpDeclareVariantDirective &); |
103 | void Leave(const parser::OmpDeclareVariantDirective &); |
104 | void Enter(const parser::OpenMPDeclareSimdConstruct &); |
105 | void Leave(const parser::OpenMPDeclareSimdConstruct &); |
106 | void Enter(const parser::OpenMPDeclarativeAllocate &); |
107 | void Leave(const parser::OpenMPDeclarativeAllocate &); |
108 | void Enter(const parser::OpenMPDeclareMapperConstruct &); |
109 | void Leave(const parser::OpenMPDeclareMapperConstruct &); |
110 | void Enter(const parser::OpenMPDeclareReductionConstruct &); |
111 | void Leave(const parser::OpenMPDeclareReductionConstruct &); |
112 | void Enter(const parser::OpenMPDeclareTargetConstruct &); |
113 | void Leave(const parser::OpenMPDeclareTargetConstruct &); |
114 | void Enter(const parser::OpenMPDepobjConstruct &); |
115 | void Leave(const parser::OpenMPDepobjConstruct &); |
116 | void Enter(const parser::OmpDeclareTargetWithList &); |
117 | void Enter(const parser::OmpDeclareTargetWithClause &); |
118 | void Leave(const parser::OmpDeclareTargetWithClause &); |
119 | void Enter(const parser::OpenMPDispatchConstruct &); |
120 | void Leave(const parser::OpenMPDispatchConstruct &); |
121 | void Enter(const parser::OmpErrorDirective &); |
122 | void Leave(const parser::OmpErrorDirective &); |
123 | void Enter(const parser::OpenMPExecutableAllocate &); |
124 | void Leave(const parser::OpenMPExecutableAllocate &); |
125 | void Enter(const parser::OpenMPAllocatorsConstruct &); |
126 | void Leave(const parser::OpenMPAllocatorsConstruct &); |
127 | void Enter(const parser::OpenMPRequiresConstruct &); |
128 | void Leave(const parser::OpenMPRequiresConstruct &); |
129 | void Enter(const parser::OpenMPThreadprivate &); |
130 | void Leave(const parser::OpenMPThreadprivate &); |
131 | |
132 | void Enter(const parser::OpenMPSimpleStandaloneConstruct &); |
133 | void Leave(const parser::OpenMPSimpleStandaloneConstruct &); |
134 | void Enter(const parser::OpenMPFlushConstruct &); |
135 | void Leave(const parser::OpenMPFlushConstruct &); |
136 | void Enter(const parser::OpenMPCancelConstruct &); |
137 | void Leave(const parser::OpenMPCancelConstruct &); |
138 | void Enter(const parser::OpenMPCancellationPointConstruct &); |
139 | void Leave(const parser::OpenMPCancellationPointConstruct &); |
140 | void Enter(const parser::OpenMPCriticalConstruct &); |
141 | void Leave(const parser::OpenMPCriticalConstruct &); |
142 | void Enter(const parser::OpenMPAtomicConstruct &); |
143 | void Leave(const parser::OpenMPAtomicConstruct &); |
144 | |
145 | void Leave(const parser::OmpClauseList &); |
146 | void Enter(const parser::OmpClause &); |
147 | |
148 | void Enter(const parser::DoConstruct &); |
149 | void Leave(const parser::DoConstruct &); |
150 | |
151 | void Enter(const parser::OmpDirectiveSpecification &); |
152 | void Leave(const parser::OmpDirectiveSpecification &); |
153 | |
154 | void Enter(const parser::OmpMetadirectiveDirective &); |
155 | void Leave(const parser::OmpMetadirectiveDirective &); |
156 | |
157 | void Enter(const parser::OmpContextSelector &); |
158 | void Leave(const parser::OmpContextSelector &); |
159 | |
160 | #define GEN_FLANG_CLAUSE_CHECK_ENTER |
161 | #include "llvm/Frontend/OpenMP/OMP.inc" |
162 | |
163 | private: |
164 | bool CheckAllowedClause(llvmOmpClause clause); |
165 | bool IsVariableListItem(const Symbol &sym); |
166 | bool IsExtendedListItem(const Symbol &sym); |
167 | bool IsCommonBlock(const Symbol &sym); |
168 | std::optional<bool> IsContiguous(const parser::OmpObject &object); |
169 | void CheckVariableListItem(const SymbolSourceMap &symbols); |
170 | void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars, |
171 | const std::list<parser::Name> &nameList, const parser::CharBlock &item, |
172 | const std::string &clauseName); |
173 | void CheckMultListItems(); |
174 | void CheckStructureComponent( |
175 | const parser::OmpObjectList &objects, llvm::omp::Clause clauseId); |
176 | bool HasInvalidWorksharingNesting( |
177 | const parser::CharBlock &, const OmpDirectiveSet &); |
178 | bool IsCloselyNestedRegion(const OmpDirectiveSet &set); |
179 | void HasInvalidTeamsNesting( |
180 | const llvm::omp::Directive &dir, const parser::CharBlock &source); |
181 | void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x); |
182 | void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x); |
183 | // specific clause related |
184 | void CheckAllowedMapTypes(const parser::OmpMapType::Value &, |
185 | const std::list<parser::OmpMapType::Value> &); |
186 | |
187 | const std::list<parser::OmpTraitProperty> &GetTraitPropertyList( |
188 | const parser::OmpTraitSelector &); |
189 | std::optional<llvm::omp::Clause> GetClauseFromProperty( |
190 | const parser::OmpTraitProperty &); |
191 | |
192 | void CheckTraitSelectorList(const std::list<parser::OmpTraitSelector> &); |
193 | void CheckTraitSetSelector(const parser::OmpTraitSetSelector &); |
194 | void CheckTraitScore(const parser::OmpTraitScore &); |
195 | bool VerifyTraitPropertyLists( |
196 | const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &); |
197 | void CheckTraitSelector( |
198 | const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &); |
199 | void CheckTraitADMO( |
200 | const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &); |
201 | void CheckTraitCondition( |
202 | const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &); |
203 | void CheckTraitDeviceNum( |
204 | const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &); |
205 | void CheckTraitRequires( |
206 | const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &); |
207 | void CheckTraitSimd( |
208 | const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &); |
209 | |
210 | llvm::StringRef getClauseName(llvm::omp::Clause clause) override; |
211 | llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override; |
212 | |
213 | template < // |
214 | typename LessTy, typename RangeTy, |
215 | typename IterTy = decltype(std::declval<RangeTy>().begin())> |
216 | std::optional<IterTy> FindDuplicate(RangeTy &&); |
217 | |
218 | const Symbol *GetObjectSymbol(const parser::OmpObject &object); |
219 | const Symbol *GetArgumentSymbol(const parser::OmpArgument &argument); |
220 | std::optional<parser::CharBlock> GetObjectSource( |
221 | const parser::OmpObject &object); |
222 | void CheckDependList(const parser::DataRef &); |
223 | void CheckDependArraySection( |
224 | const common::Indirection<parser::ArrayElement> &, const parser::Name &); |
225 | void CheckDoacross(const parser::OmpDoacross &doa); |
226 | bool IsDataRefTypeParamInquiry(const parser::DataRef *dataRef); |
227 | void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source, |
228 | const parser::OmpObject &obj, llvm::StringRef clause = "" ); |
229 | void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source, |
230 | const parser::OmpObjectList &objList, llvm::StringRef clause = "" ); |
231 | void CheckThreadprivateOrDeclareTargetVar( |
232 | const parser::OmpObjectList &objList); |
233 | void CheckSymbolNames( |
234 | const parser::CharBlock &source, const parser::OmpObjectList &objList); |
235 | void CheckIntentInPointer(SymbolSourceMap &, const llvm::omp::Clause); |
236 | void CheckProcedurePointer(SymbolSourceMap &, const llvm::omp::Clause); |
237 | void CheckCrayPointee(const parser::OmpObjectList &objectList, |
238 | llvm::StringRef clause, bool suggestToUseCrayPointer = true); |
239 | void GetSymbolsInObjectList(const parser::OmpObjectList &, SymbolSourceMap &); |
240 | void CheckDefinableObjects(SymbolSourceMap &, const llvm::omp::Clause); |
241 | void CheckCopyingPolymorphicAllocatable( |
242 | SymbolSourceMap &, const llvm::omp::Clause); |
243 | void CheckPrivateSymbolsInOuterCxt( |
244 | SymbolSourceMap &, DirectivesClauseTriple &, const llvm::omp::Clause); |
245 | const parser::Name GetLoopIndex(const parser::DoConstruct *x); |
246 | void SetLoopInfo(const parser::OpenMPLoopConstruct &x); |
247 | void CheckIsLoopIvPartOfClause( |
248 | llvmOmpClause clause, const parser::OmpObjectList &ompObjectList); |
249 | bool CheckTargetBlockOnlyTeams(const parser::Block &); |
250 | void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock); |
251 | |
252 | void CheckIteratorRange(const parser::OmpIteratorSpecifier &x); |
253 | void CheckIteratorModifier(const parser::OmpIterator &x); |
254 | void CheckLoopItrVariableIsInt(const parser::OpenMPLoopConstruct &x); |
255 | void CheckDoWhile(const parser::OpenMPLoopConstruct &x); |
256 | void CheckAssociatedLoopConstraints(const parser::OpenMPLoopConstruct &x); |
257 | template <typename T, typename D> bool IsOperatorValid(const T &, const D &); |
258 | |
259 | void CheckStorageOverlap(const evaluate::Expr<evaluate::SomeType> &, |
260 | llvm::ArrayRef<evaluate::Expr<evaluate::SomeType>>, parser::CharBlock); |
261 | void ErrorShouldBeVariable(const MaybeExpr &expr, parser::CharBlock source); |
262 | void CheckAtomicType( |
263 | SymbolRef sym, parser::CharBlock source, std::string_view name); |
264 | void CheckAtomicVariable( |
265 | const evaluate::Expr<evaluate::SomeType> &, parser::CharBlock); |
266 | std::pair<const parser::ExecutionPartConstruct *, |
267 | const parser::ExecutionPartConstruct *> |
268 | CheckUpdateCapture(const parser::ExecutionPartConstruct *ec1, |
269 | const parser::ExecutionPartConstruct *ec2, parser::CharBlock source); |
270 | void CheckAtomicCaptureAssignment(const evaluate::Assignment &capture, |
271 | const SomeExpr &atom, parser::CharBlock source); |
272 | void CheckAtomicReadAssignment( |
273 | const evaluate::Assignment &read, parser::CharBlock source); |
274 | void CheckAtomicWriteAssignment( |
275 | const evaluate::Assignment &write, parser::CharBlock source); |
276 | void CheckAtomicUpdateAssignment( |
277 | const evaluate::Assignment &update, parser::CharBlock source); |
278 | void CheckAtomicConditionalUpdateAssignment(const SomeExpr &cond, |
279 | parser::CharBlock condSource, const evaluate::Assignment &assign, |
280 | parser::CharBlock assignSource); |
281 | void CheckAtomicConditionalUpdateStmt( |
282 | const AnalyzedCondStmt &update, parser::CharBlock source); |
283 | void CheckAtomicUpdateOnly(const parser::OpenMPAtomicConstruct &x, |
284 | const parser::Block &body, parser::CharBlock source); |
285 | void CheckAtomicConditionalUpdate(const parser::OpenMPAtomicConstruct &x, |
286 | const parser::Block &body, parser::CharBlock source); |
287 | void CheckAtomicUpdateCapture(const parser::OpenMPAtomicConstruct &x, |
288 | const parser::Block &body, parser::CharBlock source); |
289 | void CheckAtomicConditionalUpdateCapture( |
290 | const parser::OpenMPAtomicConstruct &x, const parser::Block &body, |
291 | parser::CharBlock source); |
292 | void CheckAtomicRead(const parser::OpenMPAtomicConstruct &x); |
293 | void CheckAtomicWrite(const parser::OpenMPAtomicConstruct &x); |
294 | void CheckAtomicUpdate(const parser::OpenMPAtomicConstruct &x); |
295 | |
296 | void CheckDistLinear(const parser::OpenMPLoopConstruct &x); |
297 | void CheckSIMDNest(const parser::OpenMPConstruct &x); |
298 | void CheckTargetNest(const parser::OpenMPConstruct &x); |
299 | void CheckTargetUpdate(); |
300 | void CheckDependenceType(const parser::OmpDependenceType::Value &x); |
301 | void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x); |
302 | std::optional<llvm::omp::Directive> GetCancelType( |
303 | llvm::omp::Directive cancelDir, const parser::CharBlock &cancelSource, |
304 | const std::optional<parser::OmpClauseList> &maybeClauses); |
305 | void CheckCancellationNest( |
306 | const parser::CharBlock &source, llvm::omp::Directive type); |
307 | std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x); |
308 | void CheckReductionObjects( |
309 | const parser::OmpObjectList &objects, llvm::omp::Clause clauseId); |
310 | bool CheckReductionOperator(const parser::OmpReductionIdentifier &ident, |
311 | parser::CharBlock source, llvm::omp::Clause clauseId); |
312 | void CheckReductionObjectTypes(const parser::OmpObjectList &objects, |
313 | const parser::OmpReductionIdentifier &ident); |
314 | void CheckReductionModifier(const parser::OmpReductionModifier &); |
315 | void CheckLastprivateModifier(const parser::OmpLastprivateModifier &); |
316 | void CheckMasterNesting(const parser::OpenMPBlockConstruct &x); |
317 | void ChecksOnOrderedAsBlock(); |
318 | void CheckBarrierNesting(const parser::OpenMPSimpleStandaloneConstruct &x); |
319 | void CheckScan(const parser::OpenMPSimpleStandaloneConstruct &x); |
320 | void ChecksOnOrderedAsStandalone(); |
321 | void CheckOrderedDependClause(std::optional<std::int64_t> orderedValue); |
322 | void CheckReductionArraySection( |
323 | const parser::OmpObjectList &ompObjectList, llvm::omp::Clause clauseId); |
324 | void CheckArraySection(const parser::ArrayElement &arrayElement, |
325 | const parser::Name &name, const llvm::omp::Clause clause); |
326 | void CheckSharedBindingInOuterContext( |
327 | const parser::OmpObjectList &ompObjectList); |
328 | void CheckIfContiguous(const parser::OmpObject &object); |
329 | const parser::Name *GetObjectName(const parser::OmpObject &object); |
330 | const parser::OmpObjectList *GetOmpObjectList(const parser::OmpClause &); |
331 | void CheckPredefinedAllocatorRestriction(const parser::CharBlock &source, |
332 | const parser::OmpObjectList &ompObjectList); |
333 | void CheckPredefinedAllocatorRestriction( |
334 | const parser::CharBlock &source, const parser::Name &name); |
335 | bool isPredefinedAllocator{false}; |
336 | |
337 | void CheckAllowedRequiresClause(llvmOmpClause clause); |
338 | bool deviceConstructFound_{false}; |
339 | |
340 | void CheckAlignValue(const parser::OmpClause &); |
341 | |
342 | void AddEndDirectiveClauses(const parser::OmpClauseList &clauses); |
343 | |
344 | void EnterDirectiveNest(const int index) { directiveNest_[index]++; } |
345 | void ExitDirectiveNest(const int index) { directiveNest_[index]--; } |
346 | int GetDirectiveNest(const int index) { return directiveNest_[index]; } |
347 | inline void ErrIfAllocatableVariable(const parser::Variable &); |
348 | inline void ErrIfLHSAndRHSSymbolsMatch( |
349 | const parser::Variable &, const parser::Expr &); |
350 | inline void ErrIfNonScalarAssignmentStmt( |
351 | const parser::Variable &, const parser::Expr &); |
352 | enum directiveNestType : int { |
353 | SIMDNest, |
354 | TargetBlockOnlyTeams, |
355 | TargetNest, |
356 | DeclarativeNest, |
357 | ContextSelectorNest, |
358 | MetadirectiveNest, |
359 | LastType = MetadirectiveNest, |
360 | }; |
361 | int directiveNest_[LastType + 1] = {0}; |
362 | |
363 | SymbolSourceMap deferredNonVariables_; |
364 | |
365 | using LoopConstruct = std::variant<const parser::DoConstruct *, |
366 | const parser::OpenMPLoopConstruct *>; |
367 | std::vector<LoopConstruct> loopStack_; |
368 | }; |
369 | |
370 | /// Find a duplicate entry in the range, and return an iterator to it. |
371 | /// If there are no duplicate entries, return nullopt. |
372 | template <typename LessTy, typename RangeTy, typename IterTy> |
373 | std::optional<IterTy> OmpStructureChecker::FindDuplicate(RangeTy &&range) { |
374 | // Deal with iterators, since the actual elements may be rvalues (i.e. |
375 | // have no addresses), for example with custom-constructed ranges that |
376 | // are not simple c.begin()..c.end(). |
377 | std::set<IterTy, LessTy> uniq; |
378 | for (auto it{range.begin()}, end{range.end()}; it != end; ++it) { |
379 | if (!uniq.insert(it).second) { |
380 | return it; |
381 | } |
382 | } |
383 | return std::nullopt; |
384 | } |
385 | |
386 | } // namespace Fortran::semantics |
387 | #endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ |
388 | |