427 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9// OpenMP structure validity check list10// 1. invalid clauses on directive11// 2. invalid repeated clauses on directive12// 3. TODO: invalid nesting of regions13 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 23using OmpClauseSet =24 Fortran::common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;25 26#define GEN_FLANG_DIRECTIVE_CLAUSE_SETS27#include "llvm/Frontend/OpenMP/OMP.inc"28 29namespace llvm {30namespace omp {31static OmpClauseSet privateSet{32 Clause::OMPC_private, Clause::OMPC_firstprivate, Clause::OMPC_lastprivate};33static OmpClauseSet privateReductionSet{34 OmpClauseSet{Clause::OMPC_reduction} | privateSet};35// omp.td cannot differentiate allowed/not allowed clause list for few36// directives for fortran. nowait is not allowed on begin directive clause list37// for below list of directives. Directives with conflicting list of clauses are38// included in below list.39static const OmpDirectiveSet noWaitClauseNotAllowedSet{40 Directive::OMPD_do,41 Directive::OMPD_do_simd,42 Directive::OMPD_sections,43 Directive::OMPD_single,44 Directive::OMPD_workshare,45};46} // namespace omp47} // namespace llvm48 49namespace Fortran::semantics {50struct AnalyzedCondStmt;51 52// Mapping from 'Symbol' to 'Source' to keep track of the variables53// used in multiple clauses54using SymbolSourceMap = std::multimap<const Symbol *, parser::CharBlock>;55// Multimap to check the triple <current_dir, enclosing_dir, enclosing_clause>56using DirectivesClauseTriple = std::multimap<llvm::omp::Directive,57 std::pair<llvm::omp::Directive, const OmpClauseSet>>;58 59using OmpStructureCheckerBase = DirectiveStructureChecker<llvm::omp::Directive,60 llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;61 62class OmpStructureChecker : public OmpStructureCheckerBase {63public:64 using Base = OmpStructureCheckerBase;65 66 OmpStructureChecker(SemanticsContext &context);67 68 using llvmOmpClause = const llvm::omp::Clause;69 70 bool Enter(const parser::MainProgram &);71 void Leave(const parser::MainProgram &);72 bool Enter(const parser::BlockData &);73 void Leave(const parser::BlockData &);74 bool Enter(const parser::Module &);75 void Leave(const parser::Module &);76 bool Enter(const parser::Submodule &);77 void Leave(const parser::Submodule &);78 bool Enter(const parser::SubroutineStmt &);79 bool Enter(const parser::EndSubroutineStmt &);80 bool Enter(const parser::FunctionStmt &);81 bool Enter(const parser::EndFunctionStmt &);82 bool Enter(const parser::BlockConstruct &);83 void Leave(const parser::BlockConstruct &);84 85 void Enter(const parser::SpecificationPart &);86 void Leave(const parser::SpecificationPart &);87 void Enter(const parser::ExecutionPart &);88 void Leave(const parser::ExecutionPart &);89 90 void Enter(const parser::OpenMPConstruct &);91 void Leave(const parser::OpenMPConstruct &);92 void Enter(const parser::OpenMPInteropConstruct &);93 void Leave(const parser::OpenMPInteropConstruct &);94 void Enter(const parser::OpenMPDeclarativeConstruct &);95 void Leave(const parser::OpenMPDeclarativeConstruct &);96 97 void Enter(const parser::OpenMPMisplacedEndDirective &);98 void Leave(const parser::OpenMPMisplacedEndDirective &);99 void Enter(const parser::OpenMPInvalidDirective &);100 void Leave(const parser::OpenMPInvalidDirective &);101 102 void Enter(const parser::OpenMPLoopConstruct &);103 void Leave(const parser::OpenMPLoopConstruct &);104 void Enter(const parser::OmpEndLoopDirective &);105 void Leave(const parser::OmpEndLoopDirective &);106 107 void Enter(const parser::OpenMPAssumeConstruct &);108 void Leave(const parser::OpenMPAssumeConstruct &);109 void Enter(const parser::OpenMPDeclarativeAssumes &);110 void Leave(const parser::OpenMPDeclarativeAssumes &);111 void Enter(const parser::OmpBlockConstruct &);112 void Leave(const parser::OmpBlockConstruct &);113 void Leave(const parser::OmpBeginDirective &);114 void Enter(const parser::OmpEndDirective &);115 void Leave(const parser::OmpEndDirective &);116 117 void Enter(const parser::OpenMPSectionsConstruct &);118 void Leave(const parser::OpenMPSectionsConstruct &);119 void Enter(const parser::OmpEndSectionsDirective &);120 void Leave(const parser::OmpEndSectionsDirective &);121 122 void Enter(const parser::OmpDeclareVariantDirective &);123 void Leave(const parser::OmpDeclareVariantDirective &);124 void Enter(const parser::OpenMPDeclareSimdConstruct &);125 void Leave(const parser::OpenMPDeclareSimdConstruct &);126 void Enter(const parser::OmpAllocateDirective &);127 void Leave(const parser::OmpAllocateDirective &);128 void Enter(const parser::OpenMPDeclareMapperConstruct &);129 void Leave(const parser::OpenMPDeclareMapperConstruct &);130 void Enter(const parser::OpenMPDeclareReductionConstruct &);131 void Leave(const parser::OpenMPDeclareReductionConstruct &);132 void Enter(const parser::OpenMPDeclareTargetConstruct &);133 void Leave(const parser::OpenMPDeclareTargetConstruct &);134 void Enter(const parser::OpenMPDepobjConstruct &);135 void Leave(const parser::OpenMPDepobjConstruct &);136 void Enter(const parser::OpenMPDispatchConstruct &);137 void Leave(const parser::OpenMPDispatchConstruct &);138 void Enter(const parser::OmpErrorDirective &);139 void Leave(const parser::OmpErrorDirective &);140 void Enter(const parser::OmpNothingDirective &);141 void Leave(const parser::OmpNothingDirective &);142 void Enter(const parser::OpenMPAllocatorsConstruct &);143 void Leave(const parser::OpenMPAllocatorsConstruct &);144 void Enter(const parser::OpenMPRequiresConstruct &);145 void Leave(const parser::OpenMPRequiresConstruct &);146 void Enter(const parser::OpenMPGroupprivate &);147 void Leave(const parser::OpenMPGroupprivate &);148 void Enter(const parser::OpenMPThreadprivate &);149 void Leave(const parser::OpenMPThreadprivate &);150 151 void Enter(const parser::OpenMPSimpleStandaloneConstruct &);152 void Leave(const parser::OpenMPSimpleStandaloneConstruct &);153 void Enter(const parser::OpenMPFlushConstruct &);154 void Leave(const parser::OpenMPFlushConstruct &);155 void Enter(const parser::OpenMPCancelConstruct &);156 void Leave(const parser::OpenMPCancelConstruct &);157 void Enter(const parser::OpenMPCancellationPointConstruct &);158 void Leave(const parser::OpenMPCancellationPointConstruct &);159 void Enter(const parser::OpenMPCriticalConstruct &);160 void Leave(const parser::OpenMPCriticalConstruct &);161 void Enter(const parser::OpenMPAtomicConstruct &);162 void Leave(const parser::OpenMPAtomicConstruct &);163 164 void Leave(const parser::OmpClauseList &);165 void Enter(const parser::OmpClause &);166 167 void Enter(const parser::DoConstruct &);168 void Leave(const parser::DoConstruct &);169 170 void Enter(const parser::OmpDirectiveSpecification &);171 void Leave(const parser::OmpDirectiveSpecification &);172 173 void Enter(const parser::OmpMetadirectiveDirective &);174 void Leave(const parser::OmpMetadirectiveDirective &);175 176 void Enter(const parser::OmpContextSelector &);177 void Leave(const parser::OmpContextSelector &);178 179#define GEN_FLANG_CLAUSE_CHECK_ENTER180#include "llvm/Frontend/OpenMP/OMP.inc"181 182private:183 bool CheckAllowedClause(llvmOmpClause clause);184 void CheckVariableListItem(const SymbolSourceMap &symbols);185 void CheckDirectiveSpelling(186 parser::CharBlock spelling, llvm::omp::Directive id);187 void AnalyzeObject(const parser::OmpObject &object);188 void AnalyzeObjects(const parser::OmpObjectList &objects);189 void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,190 const std::list<parser::Name> &nameList, const parser::CharBlock &item,191 const std::string &clauseName);192 void CheckMultListItems();193 void CheckStructureComponent(194 const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);195 bool HasInvalidWorksharingNesting(196 const parser::CharBlock &, const OmpDirectiveSet &);197 bool IsCloselyNestedRegion(const OmpDirectiveSet &set);198 bool IsNestedInDirective(llvm::omp::Directive directive);199 bool InTargetRegion();200 void HasInvalidTeamsNesting(201 const llvm::omp::Directive &dir, const parser::CharBlock &source);202 void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);203 void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);204 bool HasRequires(llvm::omp::Clause req);205 // specific clause related206 void CheckAllowedMapTypes(207 parser::OmpMapType::Value, llvm::ArrayRef<parser::OmpMapType::Value>);208 209 const std::list<parser::OmpTraitProperty> &GetTraitPropertyList(210 const parser::OmpTraitSelector &);211 std::optional<llvm::omp::Clause> GetClauseFromProperty(212 const parser::OmpTraitProperty &);213 214 void CheckTraitSelectorList(const std::list<parser::OmpTraitSelector> &);215 void CheckTraitSetSelector(const parser::OmpTraitSetSelector &);216 void CheckTraitScore(const parser::OmpTraitScore &);217 bool VerifyTraitPropertyLists(218 const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);219 void CheckTraitSelector(220 const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);221 void CheckTraitADMO(222 const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);223 void CheckTraitCondition(224 const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);225 void CheckTraitDeviceNum(226 const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);227 void CheckTraitRequires(228 const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);229 void CheckTraitSimd(230 const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);231 232 llvm::StringRef getClauseName(llvm::omp::Clause clause) override;233 llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;234 235 template < //236 typename LessTy, typename RangeTy,237 typename IterTy = decltype(std::declval<RangeTy>().begin())>238 std::optional<IterTy> FindDuplicate(RangeTy &&);239 240 void CheckDependList(const parser::DataRef &);241 void CheckDependArraySection(242 const common::Indirection<parser::ArrayElement> &, const parser::Name &);243 void CheckDoacross(const parser::OmpDoacross &doa);244 bool IsDataRefTypeParamInquiry(const parser::DataRef *dataRef);245 void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,246 const parser::OmpObject &obj, llvm::StringRef clause = "");247 void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,248 const parser::OmpObjectList &objList, llvm::StringRef clause = "");249 void CheckThreadprivateOrDeclareTargetVar(const parser::Designator &);250 void CheckThreadprivateOrDeclareTargetVar(const parser::Name &);251 void CheckThreadprivateOrDeclareTargetVar(const parser::OmpObject &);252 void CheckThreadprivateOrDeclareTargetVar(const parser::OmpObjectList &);253 void CheckSymbolName(254 const parser::CharBlock &source, const parser::OmpObject &object);255 void CheckSymbolNames(256 const parser::CharBlock &source, const parser::OmpObjectList &objList);257 void CheckIntentInPointer(SymbolSourceMap &, const llvm::omp::Clause);258 void CheckProcedurePointer(SymbolSourceMap &, const llvm::omp::Clause);259 void CheckCrayPointee(const parser::OmpObjectList &objectList,260 llvm::StringRef clause, bool suggestToUseCrayPointer = true);261 void GetSymbolsInObjectList(const parser::OmpObjectList &, SymbolSourceMap &);262 void CheckDefinableObjects(SymbolSourceMap &, const llvm::omp::Clause);263 void CheckCopyingPolymorphicAllocatable(264 SymbolSourceMap &, const llvm::omp::Clause);265 void CheckPrivateSymbolsInOuterCxt(266 SymbolSourceMap &, DirectivesClauseTriple &, const llvm::omp::Clause);267 const parser::Name GetLoopIndex(const parser::DoConstruct *x);268 void SetLoopInfo(const parser::OpenMPLoopConstruct &x);269 void CheckIsLoopIvPartOfClause(270 llvmOmpClause clause, const parser::OmpObjectList &ompObjectList);271 bool CheckTargetBlockOnlyTeams(const parser::Block &);272 void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);273 void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);274 void CheckIndividualAllocateDirective(275 const parser::OmpAllocateDirective &x, bool isExecutable);276 void CheckExecutableAllocateDirective(const parser::OmpAllocateDirective &x);277 278 void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);279 void CheckIteratorModifier(const parser::OmpIterator &x);280 void CheckLoopItrVariableIsInt(const parser::OpenMPLoopConstruct &x);281 void CheckDoWhile(const parser::OpenMPLoopConstruct &x);282 void CheckAssociatedLoopConstraints(const parser::OpenMPLoopConstruct &x);283 template <typename T, typename D> bool IsOperatorValid(const T &, const D &);284 285 void CheckStorageOverlap(const evaluate::Expr<evaluate::SomeType> &,286 llvm::ArrayRef<evaluate::Expr<evaluate::SomeType>>, parser::CharBlock);287 void ErrorShouldBeVariable(const MaybeExpr &expr, parser::CharBlock source);288 void CheckAtomicType(SymbolRef sym, parser::CharBlock source,289 std::string_view name, bool checkTypeOnPointer = true);290 void CheckAtomicVariable(const evaluate::Expr<evaluate::SomeType> &,291 parser::CharBlock, bool checkTypeOnPointer = true);292 std::pair<const parser::ExecutionPartConstruct *,293 const parser::ExecutionPartConstruct *>294 CheckUpdateCapture(const parser::ExecutionPartConstruct *ec1,295 const parser::ExecutionPartConstruct *ec2, parser::CharBlock source);296 void CheckAtomicCaptureAssignment(const evaluate::Assignment &capture,297 const SomeExpr &atom, parser::CharBlock source);298 void CheckAtomicReadAssignment(299 const evaluate::Assignment &read, parser::CharBlock source);300 void CheckAtomicWriteAssignment(301 const evaluate::Assignment &write, parser::CharBlock source);302 std::optional<evaluate::Assignment> CheckAtomicUpdateAssignment(303 const evaluate::Assignment &update, parser::CharBlock source);304 std::pair<bool, bool> CheckAtomicUpdateAssignmentRhs(const SomeExpr &atom,305 const SomeExpr &rhs, parser::CharBlock source, bool suppressDiagnostics);306 void CheckAtomicConditionalUpdateAssignment(const SomeExpr &cond,307 parser::CharBlock condSource, const evaluate::Assignment &assign,308 parser::CharBlock assignSource);309 void CheckAtomicConditionalUpdateStmt(310 const AnalyzedCondStmt &update, parser::CharBlock source);311 void CheckAtomicUpdateOnly(const parser::OpenMPAtomicConstruct &x,312 const parser::Block &body, parser::CharBlock source);313 void CheckAtomicConditionalUpdate(const parser::OpenMPAtomicConstruct &x,314 const parser::Block &body, parser::CharBlock source);315 void CheckAtomicUpdateCapture(const parser::OpenMPAtomicConstruct &x,316 const parser::Block &body, parser::CharBlock source);317 void CheckAtomicConditionalUpdateCapture(318 const parser::OpenMPAtomicConstruct &x, const parser::Block &body,319 parser::CharBlock source);320 void CheckAtomicRead(const parser::OpenMPAtomicConstruct &x);321 void CheckAtomicWrite(const parser::OpenMPAtomicConstruct &x);322 void CheckAtomicUpdate(const parser::OpenMPAtomicConstruct &x);323 324 void CheckLooprangeBounds(const parser::OpenMPLoopConstruct &x);325 void CheckNestedFuse(const parser::OpenMPLoopConstruct &x);326 void CheckDistLinear(const parser::OpenMPLoopConstruct &x);327 void CheckSIMDNest(const parser::OpenMPConstruct &x);328 void CheckNestedBlock(const parser::OpenMPLoopConstruct &x,329 const parser::Block &body, size_t &nestedCount);330 void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);331 void CheckFullUnroll(const parser::OpenMPLoopConstruct &x);332 void CheckTargetNest(const parser::OpenMPConstruct &x);333 void CheckTargetUpdate();334 void CheckTaskgraph(const parser::OmpBlockConstruct &x);335 void CheckDependenceType(const parser::OmpDependenceType::Value &x);336 void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x);337 std::optional<llvm::omp::Directive> GetCancelType(338 llvm::omp::Directive cancelDir, const parser::CharBlock &cancelSource,339 const std::optional<parser::OmpClauseList> &maybeClauses);340 void CheckCancellationNest(341 const parser::CharBlock &source, llvm::omp::Directive type);342 std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);343 void CheckReductionObjects(344 const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);345 bool CheckReductionOperator(const parser::OmpReductionIdentifier &ident,346 parser::CharBlock source, llvm::omp::Clause clauseId);347 void CheckReductionObjectTypes(const parser::OmpObjectList &objects,348 const parser::OmpReductionIdentifier &ident);349 void CheckReductionModifier(const parser::OmpReductionModifier &);350 void CheckLastprivateModifier(const parser::OmpLastprivateModifier &);351 void CheckMasterNesting(const parser::OmpBlockConstruct &x);352 void ChecksOnOrderedAsBlock();353 void CheckBarrierNesting(const parser::OpenMPSimpleStandaloneConstruct &x);354 void CheckScan(const parser::OpenMPSimpleStandaloneConstruct &x);355 void ChecksOnOrderedAsStandalone();356 void CheckOrderedDependClause(std::optional<std::int64_t> orderedValue);357 void CheckReductionArraySection(358 const parser::OmpObjectList &ompObjectList, llvm::omp::Clause clauseId);359 void CheckArraySection(const parser::ArrayElement &arrayElement,360 const parser::Name &name, const llvm::omp::Clause clause);361 void CheckSharedBindingInOuterContext(362 const parser::OmpObjectList &ompObjectList);363 void CheckIfContiguous(const parser::OmpObject &object);364 const parser::Name *GetObjectName(const parser::OmpObject &object);365 366 void CheckAllowedRequiresClause(llvmOmpClause clause);367 bool deviceConstructFound_{false};368 369 void AddEndDirectiveClauses(const parser::OmpClauseList &clauses);370 371 void EnterDirectiveNest(const int index) { directiveNest_[index]++; }372 void ExitDirectiveNest(const int index) { directiveNest_[index]--; }373 int GetDirectiveNest(const int index) { return directiveNest_[index]; }374 inline void ErrIfAllocatableVariable(const parser::Variable &);375 inline void ErrIfLHSAndRHSSymbolsMatch(376 const parser::Variable &, const parser::Expr &);377 inline void ErrIfNonScalarAssignmentStmt(378 const parser::Variable &, const parser::Expr &);379 enum directiveNestType : int {380 SIMDNest,381 TargetBlockOnlyTeams,382 TargetNest,383 DeclarativeNest,384 ContextSelectorNest,385 MetadirectiveNest,386 LastType = MetadirectiveNest,387 };388 int directiveNest_[LastType + 1] = {0};389 390 int allocateDirectiveLevel{0};391 parser::CharBlock visitedAtomicSource_;392 SymbolSourceMap deferredNonVariables_;393 394 using LoopConstruct = std::variant<const parser::DoConstruct *,395 const parser::OpenMPLoopConstruct *>;396 std::vector<LoopConstruct> loopStack_;397 // Scopes for scoping units.398 std::vector<const Scope *> scopeStack_;399 400 enum class PartKind : int {401 // There are also other "parts", such as internal-subprogram-part, etc,402 // but we're keeping track of these two for now.403 SpecificationPart,404 ExecutionPart,405 };406 std::vector<PartKind> partStack_;407};408 409/// Find a duplicate entry in the range, and return an iterator to it.410/// If there are no duplicate entries, return nullopt.411template <typename LessTy, typename RangeTy, typename IterTy>412std::optional<IterTy> OmpStructureChecker::FindDuplicate(RangeTy &&range) {413 // Deal with iterators, since the actual elements may be rvalues (i.e.414 // have no addresses), for example with custom-constructed ranges that415 // are not simple c.begin()..c.end().416 std::set<IterTy, LessTy> uniq;417 for (auto it{range.begin()}, end{range.end()}; it != end; ++it) {418 if (!uniq.insert(it).second) {419 return it;420 }421 }422 return std::nullopt;423}424 425} // namespace Fortran::semantics426#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_427