Files
clang-p2996/flang/lib/Semantics/check-omp-structure.h
Krzysztof Parzyszek 141d390dcb [flang][OpenMP] Overhaul implementation of ATOMIC construct (#137852)
The parser will accept a wide variety of illegal attempts at forming an
ATOMIC construct, leaving it to the semantic analysis to diagnose any
issues. This consolidates the analysis into one place and allows us to
produce more informative diagnostics.

The parser's outcome will be parser::OpenMPAtomicConstruct object
holding the directive, parser::Body, and an optional end-directive. The
prior variety of OmpAtomicXyz classes, as well as OmpAtomicClause have
been removed. READ, WRITE, etc. are now proper clauses.

The semantic analysis consistently operates on "evaluation"
representations, mainly evaluate::Expr (as SomeExpr) and
evaluate::Assignment. The results of the semantic analysis are stored in
a mutable member of the OpenMPAtomicConstruct node. This follows a
precedent of having `typedExpr` member in parser::Expr, for example.
This allows the lowering code to avoid duplicated handling of AST nodes.

Using a BLOCK construct containing multiple statements for an ATOMIC
construct that requires multiple statements is now allowed. In fact, any
nesting of such BLOCK constructs is allowed.

This implementation will parse, and perform semantic checks for both
conditional-update and conditional-update-capture, although no MLIR will
be generated for those. Instead, a TODO error will be issues prior to
lowering.

The allowed forms of the ATOMIC construct were based on the OpenMP 6.0
spec.
2025-06-11 10:05:34 -05:00

388 lines
18 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 {
struct AnalyzedCondStmt;
// 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;
void Enter(const parser::OpenMPConstruct &);
void Leave(const parser::OpenMPConstruct &);
void Enter(const parser::OpenMPInteropConstruct &);
void Leave(const parser::OpenMPInteropConstruct &);
void Enter(const parser::OpenMPDeclarativeConstruct &);
void Leave(const parser::OpenMPDeclarativeConstruct &);
void Enter(const parser::OpenMPLoopConstruct &);
void Leave(const parser::OpenMPLoopConstruct &);
void Enter(const parser::OmpEndLoopDirective &);
void Leave(const parser::OmpEndLoopDirective &);
void Enter(const parser::OpenMPAssumeConstruct &);
void Leave(const parser::OpenMPAssumeConstruct &);
void Enter(const parser::OpenMPDeclarativeAssumes &);
void Leave(const parser::OpenMPDeclarativeAssumes &);
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::OmpDeclareVariantDirective &);
void Leave(const parser::OmpDeclareVariantDirective &);
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::OpenMPDeclareReductionConstruct &);
void Leave(const parser::OpenMPDeclareReductionConstruct &);
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::OpenMPDispatchConstruct &);
void Leave(const parser::OpenMPDispatchConstruct &);
void Enter(const parser::OmpErrorDirective &);
void Leave(const parser::OmpErrorDirective &);
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::DoConstruct &);
void Leave(const parser::DoConstruct &);
void Enter(const parser::OmpDirectiveSpecification &);
void Leave(const parser::OmpDirectiveSpecification &);
void Enter(const parser::OmpMetadirectiveDirective &);
void Leave(const parser::OmpMetadirectiveDirective &);
void Enter(const parser::OmpContextSelector &);
void Leave(const parser::OmpContextSelector &);
#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);
bool IsCommonBlock(const Symbol &sym);
std::optional<bool> IsContiguous(const parser::OmpObject &object);
void CheckVariableListItem(const SymbolSourceMap &symbols);
void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
const std::string &clauseName);
void CheckMultListItems();
void CheckStructureComponent(
const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
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
void CheckAllowedMapTypes(const parser::OmpMapType::Value &,
const std::list<parser::OmpMapType::Value> &);
const std::list<parser::OmpTraitProperty> &GetTraitPropertyList(
const parser::OmpTraitSelector &);
std::optional<llvm::omp::Clause> GetClauseFromProperty(
const parser::OmpTraitProperty &);
void CheckTraitSelectorList(const std::list<parser::OmpTraitSelector> &);
void CheckTraitSetSelector(const parser::OmpTraitSetSelector &);
void CheckTraitScore(const parser::OmpTraitScore &);
bool VerifyTraitPropertyLists(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitSelector(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitADMO(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitCondition(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitDeviceNum(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitRequires(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitSimd(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
llvm::StringRef getClauseName(llvm::omp::Clause clause) override;
llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;
template < //
typename LessTy, typename RangeTy,
typename IterTy = decltype(std::declval<RangeTy>().begin())>
std::optional<IterTy> FindDuplicate(RangeTy &&);
const Symbol *GetObjectSymbol(const parser::OmpObject &object);
const Symbol *GetArgumentSymbol(const parser::OmpArgument &argument);
std::optional<parser::CharBlock> GetObjectSource(
const parser::OmpObject &object);
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 CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,
const parser::OmpObject &obj, llvm::StringRef clause = "");
void CheckVarIsNotPartOfAnotherVar(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(SymbolSourceMap &, const llvm::omp::Clause);
void CheckProcedurePointer(SymbolSourceMap &, const llvm::omp::Clause);
void CheckCrayPointee(const parser::OmpObjectList &objectList,
llvm::StringRef clause, bool suggestToUseCrayPointer = true);
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 CheckStorageOverlap(const evaluate::Expr<evaluate::SomeType> &,
llvm::ArrayRef<evaluate::Expr<evaluate::SomeType>>, parser::CharBlock);
void ErrorShouldBeVariable(const MaybeExpr &expr, parser::CharBlock source);
void CheckAtomicType(
SymbolRef sym, parser::CharBlock source, std::string_view name);
void CheckAtomicVariable(
const evaluate::Expr<evaluate::SomeType> &, parser::CharBlock);
std::pair<const parser::ExecutionPartConstruct *,
const parser::ExecutionPartConstruct *>
CheckUpdateCapture(const parser::ExecutionPartConstruct *ec1,
const parser::ExecutionPartConstruct *ec2, parser::CharBlock source);
void CheckAtomicCaptureAssignment(const evaluate::Assignment &capture,
const SomeExpr &atom, parser::CharBlock source);
void CheckAtomicReadAssignment(
const evaluate::Assignment &read, parser::CharBlock source);
void CheckAtomicWriteAssignment(
const evaluate::Assignment &write, parser::CharBlock source);
void CheckAtomicUpdateAssignment(
const evaluate::Assignment &update, parser::CharBlock source);
void CheckAtomicConditionalUpdateAssignment(const SomeExpr &cond,
parser::CharBlock condSource, const evaluate::Assignment &assign,
parser::CharBlock assignSource);
void CheckAtomicConditionalUpdateStmt(
const AnalyzedCondStmt &update, parser::CharBlock source);
void CheckAtomicUpdateOnly(const parser::OpenMPAtomicConstruct &x,
const parser::Block &body, parser::CharBlock source);
void CheckAtomicConditionalUpdate(const parser::OpenMPAtomicConstruct &x,
const parser::Block &body, parser::CharBlock source);
void CheckAtomicUpdateCapture(const parser::OpenMPAtomicConstruct &x,
const parser::Block &body, parser::CharBlock source);
void CheckAtomicConditionalUpdateCapture(
const parser::OpenMPAtomicConstruct &x, const parser::Block &body,
parser::CharBlock source);
void CheckAtomicRead(const parser::OpenMPAtomicConstruct &x);
void CheckAtomicWrite(const parser::OpenMPAtomicConstruct &x);
void CheckAtomicUpdate(const parser::OpenMPAtomicConstruct &x);
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);
std::optional<llvm::omp::Directive> GetCancelType(
llvm::omp::Directive cancelDir, const parser::CharBlock &cancelSource,
const std::optional<parser::OmpClauseList> &maybeClauses);
void CheckCancellationNest(
const parser::CharBlock &source, llvm::omp::Directive type);
std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);
void CheckReductionObjects(
const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
bool CheckReductionOperator(const parser::OmpReductionIdentifier &ident,
parser::CharBlock source, llvm::omp::Clause clauseId);
void CheckReductionObjectTypes(const parser::OmpObjectList &objects,
const parser::OmpReductionIdentifier &ident);
void CheckReductionModifier(const parser::OmpReductionModifier &);
void CheckLastprivateModifier(const parser::OmpLastprivateModifier &);
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, llvm::omp::Clause clauseId);
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 CheckAlignValue(const parser::OmpClause &);
void AddEndDirectiveClauses(const parser::OmpClauseList &clauses);
void EnterDirectiveNest(const int index) { directiveNest_[index]++; }
void ExitDirectiveNest(const int index) { directiveNest_[index]--; }
int GetDirectiveNest(const int index) { return directiveNest_[index]; }
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 : int {
SIMDNest,
TargetBlockOnlyTeams,
TargetNest,
DeclarativeNest,
ContextSelectorNest,
MetadirectiveNest,
LastType = MetadirectiveNest,
};
int directiveNest_[LastType + 1] = {0};
SymbolSourceMap deferredNonVariables_;
using LoopConstruct = std::variant<const parser::DoConstruct *,
const parser::OpenMPLoopConstruct *>;
std::vector<LoopConstruct> loopStack_;
};
/// Find a duplicate entry in the range, and return an iterator to it.
/// If there are no duplicate entries, return nullopt.
template <typename LessTy, typename RangeTy, typename IterTy>
std::optional<IterTy> OmpStructureChecker::FindDuplicate(RangeTy &&range) {
// Deal with iterators, since the actual elements may be rvalues (i.e.
// have no addresses), for example with custom-constructed ranges that
// are not simple c.begin()..c.end().
std::set<IterTy, LessTy> uniq;
for (auto it{range.begin()}, end{range.end()}; it != end; ++it) {
if (!uniq.insert(it).second) {
return it;
}
}
return std::nullopt;
}
} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_