This is the first part of the effort to make parsing of clause modifiers more uniform and robust. Currently, when multiple modifiers are allowed, the parser will expect them to appear in a hard-coded order. Additionally, modifier properties (such as "ultimate") are checked separately for each case. The overall plan is 1. Extract all modifiers into their own top-level classes, and then equip them with sets of common properties that will allow performing the property checks generically, without refering to the specific kind of the modifier. 2. Define a parser (as a separate class) for each modifier. 3. For each clause define a union (std::variant) of all allowable modifiers, and parse the modifiers as a list of these unions. The intent is also to isolate parts of the code that could eventually be auto-generated. OpenMP modifier overhaul: #1/3
300 lines
13 KiB
C++
300 lines
13 KiB
C++
//===-- lib/Semantics/check-omp-structure.h ---------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OpenMP structure validity check list
|
|
// 1. invalid clauses on directive
|
|
// 2. invalid repeated clauses on directive
|
|
// 3. TODO: invalid nesting of regions
|
|
|
|
#ifndef FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
|
|
#define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
|
|
|
|
#include "check-directive-structure.h"
|
|
#include "flang/Common/enum-set.h"
|
|
#include "flang/Parser/parse-tree.h"
|
|
#include "flang/Semantics/openmp-directive-sets.h"
|
|
#include "flang/Semantics/semantics.h"
|
|
#include "llvm/Frontend/OpenMP/OMPConstants.h"
|
|
|
|
using OmpClauseSet =
|
|
Fortran::common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;
|
|
|
|
#define GEN_FLANG_DIRECTIVE_CLAUSE_SETS
|
|
#include "llvm/Frontend/OpenMP/OMP.inc"
|
|
|
|
namespace llvm {
|
|
namespace omp {
|
|
static OmpClauseSet privateSet{
|
|
Clause::OMPC_private, Clause::OMPC_firstprivate, Clause::OMPC_lastprivate};
|
|
static OmpClauseSet privateReductionSet{
|
|
OmpClauseSet{Clause::OMPC_reduction} | privateSet};
|
|
// omp.td cannot differentiate allowed/not allowed clause list for few
|
|
// directives for fortran. nowait is not allowed on begin directive clause list
|
|
// for below list of directives. Directives with conflicting list of clauses are
|
|
// included in below list.
|
|
static const OmpDirectiveSet noWaitClauseNotAllowedSet{
|
|
Directive::OMPD_do,
|
|
Directive::OMPD_do_simd,
|
|
Directive::OMPD_sections,
|
|
Directive::OMPD_single,
|
|
Directive::OMPD_workshare,
|
|
};
|
|
} // namespace omp
|
|
} // namespace llvm
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
// Mapping from 'Symbol' to 'Source' to keep track of the variables
|
|
// used in multiple clauses
|
|
using SymbolSourceMap = std::multimap<const Symbol *, parser::CharBlock>;
|
|
// Multimap to check the triple <current_dir, enclosing_dir, enclosing_clause>
|
|
using DirectivesClauseTriple = std::multimap<llvm::omp::Directive,
|
|
std::pair<llvm::omp::Directive, const OmpClauseSet>>;
|
|
|
|
class OmpStructureChecker
|
|
: public DirectiveStructureChecker<llvm::omp::Directive, llvm::omp::Clause,
|
|
parser::OmpClause, llvm::omp::Clause_enumSize> {
|
|
public:
|
|
using Base = DirectiveStructureChecker<llvm::omp::Directive,
|
|
llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;
|
|
|
|
OmpStructureChecker(SemanticsContext &context)
|
|
: DirectiveStructureChecker(context,
|
|
#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
|
|
#include "llvm/Frontend/OpenMP/OMP.inc"
|
|
) {
|
|
}
|
|
using llvmOmpClause = const llvm::omp::Clause;
|
|
using ReductionModifier = parser::OmpReductionClause::ReductionModifier;
|
|
|
|
void Enter(const parser::OpenMPConstruct &);
|
|
void Leave(const parser::OpenMPConstruct &);
|
|
void Enter(const parser::OpenMPLoopConstruct &);
|
|
void Leave(const parser::OpenMPLoopConstruct &);
|
|
void Enter(const parser::OmpEndLoopDirective &);
|
|
void Leave(const parser::OmpEndLoopDirective &);
|
|
|
|
void Enter(const parser::OpenMPBlockConstruct &);
|
|
void Leave(const parser::OpenMPBlockConstruct &);
|
|
void Leave(const parser::OmpBeginBlockDirective &);
|
|
void Enter(const parser::OmpEndBlockDirective &);
|
|
void Leave(const parser::OmpEndBlockDirective &);
|
|
|
|
void Enter(const parser::OpenMPSectionsConstruct &);
|
|
void Leave(const parser::OpenMPSectionsConstruct &);
|
|
void Enter(const parser::OmpEndSectionsDirective &);
|
|
void Leave(const parser::OmpEndSectionsDirective &);
|
|
|
|
void Enter(const parser::OpenMPDeclareSimdConstruct &);
|
|
void Leave(const parser::OpenMPDeclareSimdConstruct &);
|
|
void Enter(const parser::OpenMPDeclarativeAllocate &);
|
|
void Leave(const parser::OpenMPDeclarativeAllocate &);
|
|
void Enter(const parser::OpenMPDeclareMapperConstruct &);
|
|
void Leave(const parser::OpenMPDeclareMapperConstruct &);
|
|
void Enter(const parser::OpenMPDeclareTargetConstruct &);
|
|
void Leave(const parser::OpenMPDeclareTargetConstruct &);
|
|
void Enter(const parser::OpenMPDepobjConstruct &);
|
|
void Leave(const parser::OpenMPDepobjConstruct &);
|
|
void Enter(const parser::OmpDeclareTargetWithList &);
|
|
void Enter(const parser::OmpDeclareTargetWithClause &);
|
|
void Leave(const parser::OmpDeclareTargetWithClause &);
|
|
void Enter(const parser::OpenMPExecutableAllocate &);
|
|
void Leave(const parser::OpenMPExecutableAllocate &);
|
|
void Enter(const parser::OpenMPAllocatorsConstruct &);
|
|
void Leave(const parser::OpenMPAllocatorsConstruct &);
|
|
void Enter(const parser::OpenMPRequiresConstruct &);
|
|
void Leave(const parser::OpenMPRequiresConstruct &);
|
|
void Enter(const parser::OpenMPThreadprivate &);
|
|
void Leave(const parser::OpenMPThreadprivate &);
|
|
|
|
void Enter(const parser::OpenMPSimpleStandaloneConstruct &);
|
|
void Leave(const parser::OpenMPSimpleStandaloneConstruct &);
|
|
void Enter(const parser::OpenMPFlushConstruct &);
|
|
void Leave(const parser::OpenMPFlushConstruct &);
|
|
void Enter(const parser::OpenMPCancelConstruct &);
|
|
void Leave(const parser::OpenMPCancelConstruct &);
|
|
void Enter(const parser::OpenMPCancellationPointConstruct &);
|
|
void Leave(const parser::OpenMPCancellationPointConstruct &);
|
|
void Enter(const parser::OpenMPCriticalConstruct &);
|
|
void Leave(const parser::OpenMPCriticalConstruct &);
|
|
void Enter(const parser::OpenMPAtomicConstruct &);
|
|
void Leave(const parser::OpenMPAtomicConstruct &);
|
|
|
|
void Leave(const parser::OmpClauseList &);
|
|
void Enter(const parser::OmpClause &);
|
|
|
|
void Enter(const parser::OmpAtomicRead &);
|
|
void Leave(const parser::OmpAtomicRead &);
|
|
void Enter(const parser::OmpAtomicWrite &);
|
|
void Leave(const parser::OmpAtomicWrite &);
|
|
void Enter(const parser::OmpAtomicUpdate &);
|
|
void Leave(const parser::OmpAtomicUpdate &);
|
|
void Enter(const parser::OmpAtomicCapture &);
|
|
void Leave(const parser::OmpAtomic &);
|
|
|
|
void Enter(const parser::DoConstruct &);
|
|
void Leave(const parser::DoConstruct &);
|
|
|
|
#define GEN_FLANG_CLAUSE_CHECK_ENTER
|
|
#include "llvm/Frontend/OpenMP/OMP.inc"
|
|
|
|
private:
|
|
bool CheckAllowedClause(llvmOmpClause clause);
|
|
bool IsVariableListItem(const Symbol &sym);
|
|
bool IsExtendedListItem(const Symbol &sym);
|
|
std::optional<bool> IsContiguous(const parser::OmpObject &object);
|
|
void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,
|
|
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
|
|
const std::string &clauseName);
|
|
void CheckMultListItems();
|
|
void CheckStructureElement(const parser::OmpObjectList &ompObjectList,
|
|
const llvm::omp::Clause clause);
|
|
bool HasInvalidWorksharingNesting(
|
|
const parser::CharBlock &, const OmpDirectiveSet &);
|
|
bool IsCloselyNestedRegion(const OmpDirectiveSet &set);
|
|
void HasInvalidTeamsNesting(
|
|
const llvm::omp::Directive &dir, const parser::CharBlock &source);
|
|
void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
|
|
void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
|
|
// specific clause related
|
|
bool ScheduleModifierHasType(const parser::OmpScheduleClause &,
|
|
const parser::OmpScheduleModifierType::ModType &);
|
|
void CheckAllowedMapTypes(const parser::OmpMapClause::Type &,
|
|
const std::list<parser::OmpMapClause::Type> &);
|
|
llvm::StringRef getClauseName(llvm::omp::Clause clause) override;
|
|
llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;
|
|
|
|
template <typename T> struct DefaultLess {
|
|
bool operator()(const T *a, const T *b) const { return *a < *b; }
|
|
};
|
|
template <typename T, typename Less = DefaultLess<T>>
|
|
const T *FindDuplicateEntry(const std::list<T> &);
|
|
|
|
void CheckDependList(const parser::DataRef &);
|
|
void CheckDependArraySection(
|
|
const common::Indirection<parser::ArrayElement> &, const parser::Name &);
|
|
void CheckDoacross(const parser::OmpDoacross &doa);
|
|
bool IsDataRefTypeParamInquiry(const parser::DataRef *dataRef);
|
|
void CheckIsVarPartOfAnotherVar(const parser::CharBlock &source,
|
|
const parser::OmpObjectList &objList, llvm::StringRef clause = "");
|
|
void CheckThreadprivateOrDeclareTargetVar(
|
|
const parser::OmpObjectList &objList);
|
|
void CheckSymbolNames(
|
|
const parser::CharBlock &source, const parser::OmpObjectList &objList);
|
|
void CheckIntentInPointer(
|
|
const parser::OmpObjectList &, const llvm::omp::Clause);
|
|
void GetSymbolsInObjectList(const parser::OmpObjectList &, SymbolSourceMap &);
|
|
void CheckDefinableObjects(SymbolSourceMap &, const llvm::omp::Clause);
|
|
void CheckCopyingPolymorphicAllocatable(
|
|
SymbolSourceMap &, const llvm::omp::Clause);
|
|
void CheckPrivateSymbolsInOuterCxt(
|
|
SymbolSourceMap &, DirectivesClauseTriple &, const llvm::omp::Clause);
|
|
const parser::Name GetLoopIndex(const parser::DoConstruct *x);
|
|
void SetLoopInfo(const parser::OpenMPLoopConstruct &x);
|
|
void CheckIsLoopIvPartOfClause(
|
|
llvmOmpClause clause, const parser::OmpObjectList &ompObjectList);
|
|
bool CheckTargetBlockOnlyTeams(const parser::Block &);
|
|
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
|
|
|
|
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
|
|
void CheckIteratorModifier(const parser::OmpIterator &x);
|
|
void CheckLoopItrVariableIsInt(const parser::OpenMPLoopConstruct &x);
|
|
void CheckDoWhile(const parser::OpenMPLoopConstruct &x);
|
|
void CheckAssociatedLoopConstraints(const parser::OpenMPLoopConstruct &x);
|
|
template <typename T, typename D> bool IsOperatorValid(const T &, const D &);
|
|
void CheckAtomicMemoryOrderClause(
|
|
const parser::OmpAtomicClauseList *, const parser::OmpAtomicClauseList *);
|
|
void CheckAtomicUpdateStmt(const parser::AssignmentStmt &);
|
|
void CheckAtomicCaptureStmt(const parser::AssignmentStmt &);
|
|
void CheckAtomicWriteStmt(const parser::AssignmentStmt &);
|
|
void CheckAtomicCaptureConstruct(const parser::OmpAtomicCapture &);
|
|
void CheckAtomicConstructStructure(const parser::OpenMPAtomicConstruct &);
|
|
void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
|
|
void CheckSIMDNest(const parser::OpenMPConstruct &x);
|
|
void CheckTargetNest(const parser::OpenMPConstruct &x);
|
|
void CheckTargetUpdate();
|
|
void CheckDependenceType(const parser::OmpDependenceType::Value &x);
|
|
void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x);
|
|
void CheckCancellationNest(
|
|
const parser::CharBlock &source, const parser::OmpCancelType::Type &type);
|
|
std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);
|
|
bool CheckReductionOperators(const parser::OmpClause::Reduction &);
|
|
bool CheckIntrinsicOperator(
|
|
const parser::DefinedOperator::IntrinsicOperator &);
|
|
void CheckReductionTypeList(const parser::OmpClause::Reduction &);
|
|
void CheckReductionModifier(const ReductionModifier &);
|
|
void CheckMasterNesting(const parser::OpenMPBlockConstruct &x);
|
|
void ChecksOnOrderedAsBlock();
|
|
void CheckBarrierNesting(const parser::OpenMPSimpleStandaloneConstruct &x);
|
|
void CheckScan(const parser::OpenMPSimpleStandaloneConstruct &x);
|
|
void ChecksOnOrderedAsStandalone();
|
|
void CheckOrderedDependClause(std::optional<std::int64_t> orderedValue);
|
|
void CheckReductionArraySection(const parser::OmpObjectList &ompObjectList);
|
|
void CheckIntentInPointerAndDefinable(
|
|
const parser::OmpObjectList &, const llvm::omp::Clause);
|
|
void CheckArraySection(const parser::ArrayElement &arrayElement,
|
|
const parser::Name &name, const llvm::omp::Clause clause);
|
|
void CheckSharedBindingInOuterContext(
|
|
const parser::OmpObjectList &ompObjectList);
|
|
void CheckIfContiguous(const parser::OmpObject &object);
|
|
const parser::Name *GetObjectName(const parser::OmpObject &object);
|
|
const parser::OmpObjectList *GetOmpObjectList(const parser::OmpClause &);
|
|
void CheckPredefinedAllocatorRestriction(const parser::CharBlock &source,
|
|
const parser::OmpObjectList &ompObjectList);
|
|
void CheckPredefinedAllocatorRestriction(
|
|
const parser::CharBlock &source, const parser::Name &name);
|
|
bool isPredefinedAllocator{false};
|
|
|
|
void CheckAllowedRequiresClause(llvmOmpClause clause);
|
|
bool deviceConstructFound_{false};
|
|
|
|
void EnterDirectiveNest(const int index) { directiveNest_[index]++; }
|
|
void ExitDirectiveNest(const int index) { directiveNest_[index]--; }
|
|
int GetDirectiveNest(const int index) { return directiveNest_[index]; }
|
|
template <typename D> void CheckHintClause(D *, D *);
|
|
inline void ErrIfAllocatableVariable(const parser::Variable &);
|
|
inline void ErrIfLHSAndRHSSymbolsMatch(
|
|
const parser::Variable &, const parser::Expr &);
|
|
inline void ErrIfNonScalarAssignmentStmt(
|
|
const parser::Variable &, const parser::Expr &);
|
|
enum directiveNestType {
|
|
SIMDNest,
|
|
TargetBlockOnlyTeams,
|
|
TargetNest,
|
|
LastType
|
|
};
|
|
int directiveNest_[LastType + 1] = {0};
|
|
|
|
SymbolSourceMap deferredNonVariables_;
|
|
|
|
using LoopConstruct = std::variant<const parser::DoConstruct *,
|
|
const parser::OpenMPLoopConstruct *>;
|
|
std::vector<LoopConstruct> loopStack_;
|
|
};
|
|
|
|
template <typename T, typename Less>
|
|
const T *OmpStructureChecker::FindDuplicateEntry(const std::list<T> &list) {
|
|
// Add elements of the list to a set. If the insertion fails, return
|
|
// the address of the failing element.
|
|
|
|
// The objects of type T may not be copyable, so add their addresses
|
|
// to the set. The set will need to compare the actual objects, so
|
|
// the custom comparator is provided.
|
|
std::set<const T *, Less> uniq;
|
|
|
|
for (const T &item : list) {
|
|
if (!uniq.insert(&item).second) {
|
|
return &item;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace Fortran::semantics
|
|
#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
|