2447 lines · cpp
1//===-- lib/Parser/openmp-parsers.cpp -------------------------------------===//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// Top-level grammar specification for OpenMP.10// See OpenMP-4.5-grammar.txt for documentation.11 12#include "basic-parsers.h"13#include "expr-parsers.h"14#include "misc-parsers.h"15#include "stmt-parser.h"16#include "token-parsers.h"17#include "type-parser-implementation.h"18#include "flang/Parser/openmp-utils.h"19#include "flang/Parser/parse-tree.h"20#include "flang/Parser/tools.h"21#include "llvm/ADT/ArrayRef.h"22#include "llvm/ADT/Bitset.h"23#include "llvm/ADT/STLExtras.h"24#include "llvm/ADT/StringRef.h"25#include "llvm/ADT/StringSet.h"26#include "llvm/Frontend/OpenMP/OMP.h"27#include "llvm/Support/MathExtras.h"28 29#include <algorithm>30#include <cctype>31#include <iterator>32#include <list>33#include <optional>34#include <set>35#include <string>36#include <tuple>37#include <type_traits>38#include <utility>39#include <variant>40#include <vector>41 42// OpenMP Directives and Clauses43namespace Fortran::parser {44using namespace Fortran::parser::omp;45 46using DirectiveSet =47 llvm::Bitset<llvm::NextPowerOf2(llvm::omp::Directive_enumSize)>;48 49// Helper function to print the buffer contents starting at the current point.50[[maybe_unused]] static std::string ahead(const ParseState &state) {51 return std::string(52 state.GetLocation(), std::min<size_t>(64, state.BytesRemaining()));53}54 55constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok;56constexpr auto endOmpLine = space >> endOfLine;57 58constexpr auto logicalConstantExpr{logical(constantExpr)};59constexpr auto scalarLogicalConstantExpr{scalar(logicalConstantExpr)};60 61// Parser that wraps the result of another parser into a Block. If the given62// parser succeeds, the result is a block containing the ExecutionPartConstruct63// result of the argument parser. Otherwise the parser fails.64template <typename ExecParser> struct AsBlockParser {65 using resultType = Block;66 static_assert(67 std::is_same_v<typename ExecParser::resultType, ExecutionPartConstruct>);68 69 constexpr AsBlockParser(ExecParser epc) : epc_(epc) {}70 std::optional<resultType> Parse(ParseState &state) const {71 if (auto &&exec{attempt(epc_).Parse(state)}) {72 Block body;73 body.push_back(std::move(*exec));74 return std::move(body); // std::move for GCC 7.5.075 }76 return std::nullopt;77 }78 79private:80 const ExecParser epc_;81};82 83template <typename ExecParser,84 typename = std::enable_if<std::is_same_v<typename ExecParser::resultType,85 ExecutionPartConstruct>>>86constexpr auto asBlock(ExecParser epc) {87 return AsBlockParser<ExecParser>(epc);88}89 90// Given a parser for a single element, and a parser for a list of elements91// of the same type, create a parser that constructs the entire list by having92// the single element be the head of the list, and the rest be the tail.93template <typename ParserH, typename ParserT> struct ConsParser {94 static_assert(std::is_same_v<std::list<typename ParserH::resultType>,95 typename ParserT::resultType>);96 97 using resultType = typename ParserT::resultType;98 constexpr ConsParser(ParserH h, ParserT t) : head_(h), tail_(t) {}99 100 std::optional<resultType> Parse(ParseState &state) const {101 if (auto &&first{head_.Parse(state)}) {102 if (auto rest{tail_.Parse(state)}) {103 rest->push_front(std::move(*first));104 return std::move(*rest);105 }106 }107 return std::nullopt;108 }109 110private:111 const ParserH head_;112 const ParserT tail_;113};114 115template <typename ParserH, typename ParserT,116 typename ValueH = typename ParserH::resultType,117 typename ValueT = typename ParserT::resultType,118 typename = std::enable_if_t<std::is_same_v<std::list<ValueH>, ValueT>>>119constexpr auto cons(ParserH head, ParserT tail) {120 return ConsParser<ParserH, ParserT>(head, tail);121}122 123// Given a parser P for a wrapper class, invoke P, and if it succeeds return124// the wrapped object.125template <typename Parser> struct UnwrapParser {126 static_assert(127 Parser::resultType::WrapperTrait::value && "Wrapper class required");128 using resultType = decltype(Parser::resultType::v);129 constexpr UnwrapParser(Parser p) : parser_(p) {}130 131 std::optional<resultType> Parse(ParseState &state) const {132 if (auto result{parser_.Parse(state)}) {133 return result->v;134 }135 return std::nullopt;136 }137 138private:139 const Parser parser_;140};141 142template <typename Parser> constexpr auto unwrap(const Parser &p) {143 return UnwrapParser<Parser>(p);144}145 146// Check (without advancing the parsing location) if the next thing in the147// input would be accepted by the "checked" parser, and if so, run the "parser"148// parser.149// The intended use is with the "checker" parser being some token, followed150// by a more complex parser that consumes the token plus more things, e.g.151// "PARALLEL"_id >= Parser<OmpDirectiveSpecification>{}.152//153// The >= has a higher precedence than ||, so it can be used just like >>154// in an alternatives parser without parentheses.155template <typename PA, typename PB>156constexpr auto operator>=(PA checker, PB parser) {157 return lookAhead(checker) >> parser;158}159 160// This parser succeeds if the given parser succeeds, and the result161// satisfies the given condition. Specifically, it succeeds if:162// 1. The parser given as the argument succeeds, and163// 2. The condition function (called with PA::resultType) returns true164// for the result.165template <typename PA, typename CF> struct PredicatedParser {166 using resultType = typename PA::resultType;167 168 constexpr PredicatedParser(PA parser, CF condition)169 : parser_(parser), condition_(condition) {}170 171 std::optional<resultType> Parse(ParseState &state) const {172 if (auto result{parser_.Parse(state)}; result && condition_(*result)) {173 return result;174 }175 return std::nullopt;176 }177 178private:179 const PA parser_;180 const CF condition_;181};182 183template <typename PA, typename CF>184constexpr auto predicated(PA parser, CF condition) {185 return PredicatedParser(parser, condition);186}187 188/// Parse OpenMP directive name (this includes compound directives).189struct OmpDirectiveNameParser {190 using resultType = OmpDirectiveName;191 using Token = TokenStringMatch<false, false>;192 193 std::optional<resultType> Parse(ParseState &state) const {194 if (state.BytesRemaining() == 0) {195 return std::nullopt;196 }197 auto begin{state.GetLocation()};198 char next{static_cast<char>(std::tolower(*begin))};199 200 for (const NameWithId &nid : directives_starting_with(next)) {201 if (attempt(Token(nid.first.data())).Parse(state)) {202 OmpDirectiveName n;203 n.v = nid.second;204 n.source = parser::CharBlock(begin, state.GetLocation());205 return n;206 }207 }208 return std::nullopt;209 }210 211private:212 using NameWithId = std::pair<std::string, llvm::omp::Directive>;213 using ConstIterator = std::vector<NameWithId>::const_iterator;214 215 llvm::iterator_range<ConstIterator> directives_starting_with(216 char initial) const;217 void initTokens(std::vector<NameWithId>[]) const;218};219 220llvm::iterator_range<OmpDirectiveNameParser::ConstIterator>221OmpDirectiveNameParser::directives_starting_with(char initial) const {222 static const std::vector<NameWithId> empty{};223 if (initial < 'a' || initial > 'z') {224 return llvm::make_range(std::cbegin(empty), std::cend(empty));225 }226 227 static std::vector<NameWithId> table['z' - 'a' + 1];228 [[maybe_unused]] static bool init = (initTokens(table), true);229 230 int index = initial - 'a';231 return llvm::make_range(std::cbegin(table[index]), std::cend(table[index]));232}233 234void OmpDirectiveNameParser::initTokens(std::vector<NameWithId> table[]) const {235 for (size_t i{0}, e{llvm::omp::Directive_enumSize}; i != e; ++i) {236 llvm::StringSet spellings;237 auto id{static_cast<llvm::omp::Directive>(i)};238 for (unsigned version : llvm::omp::getOpenMPVersions()) {239 spellings.insert(llvm::omp::getOpenMPDirectiveName(id, version));240 }241 for (auto &[name, _] : spellings) {242 char initial{static_cast<char>(std::tolower(name.front()))};243 table[initial - 'a'].emplace_back(name.str(), id);244 }245 }246 // Sort the table with respect to the directive name length in a descending247 // order. This is to make sure that longer names are tried first, before248 // any potential prefix (e.g. "target update" before "target").249 for (int initial{'a'}; initial != 'z' + 1; ++initial) {250 llvm::stable_sort(table[initial - 'a'],251 [](auto &a, auto &b) { return a.first.size() > b.first.size(); });252 }253}254 255// --- Modifier helpers -----------------------------------------------256 257template <typename Clause, typename Separator> struct ModifierList {258 constexpr ModifierList(Separator sep) : sep_(sep) {}259 constexpr ModifierList(const ModifierList &) = default;260 constexpr ModifierList(ModifierList &&) = default;261 262 using resultType = std::list<typename Clause::Modifier>;263 264 std::optional<resultType> Parse(ParseState &state) const {265 auto listp{nonemptySeparated(Parser<typename Clause::Modifier>{}, sep_)};266 if (auto result{attempt(listp).Parse(state)}) {267 if (!attempt(":"_tok).Parse(state)) {268 return std::nullopt;269 }270 return std::move(result);271 }272 return resultType{};273 }274 275private:276 const Separator sep_;277};278 279// Use a function to create ModifierList because functions allow "partial"280// template argument deduction: "modifierList<Clause>(sep)" would be legal,281// while "ModifierList<Clause>(sep)" would complain about a missing template282// argument "Separator".283template <typename Clause, typename Separator>284constexpr ModifierList<Clause, Separator> modifierList(Separator sep) {285 return ModifierList<Clause, Separator>(sep);286}287 288// Parse the input as any modifier from ClauseTy, but only succeed if289// the result was the SpecificTy. It requires that SpecificTy is one290// of the alternatives in ClauseTy::Modifier.291// The reason to have this is that ClauseTy::Modifier has "source",292// while specific modifiers don't. This class allows to parse a specific293// modifier together with obtaining its location.294template <typename SpecificTy, typename ClauseTy>295struct SpecificModifierParser {296 using resultType = typename ClauseTy::Modifier;297 std::optional<resultType> Parse(ParseState &state) const {298 if (auto result{attempt(Parser<resultType>{}).Parse(state)}) {299 if (std::holds_alternative<SpecificTy>(result->u)) {300 return result;301 }302 }303 return std::nullopt;304 }305};306 307// --- Iterator helpers -----------------------------------------------308 309static EntityDecl MakeEntityDecl(ObjectName &&name) {310 return EntityDecl(311 /*ObjectName=*/std::move(name), std::optional<ArraySpec>{},312 std::optional<CoarraySpec>{}, std::optional<CharLength>{},313 std::optional<Initialization>{});314}315 316// [5.0:47:17-18] In an iterator-specifier, if the iterator-type is not317// specified then the type of that iterator is default integer.318// [5.0:49:14] The iterator-type must be an integer type.319static std::list<EntityDecl> makeEntityList(std::list<ObjectName> &&names) {320 std::list<EntityDecl> entities;321 322 for (auto iter = names.begin(), end = names.end(); iter != end; ++iter) {323 entities.push_back(MakeEntityDecl(std::move(*iter)));324 }325 return entities;326}327 328static TypeDeclarationStmt makeIterSpecDecl(329 DeclarationTypeSpec &&spec, std::list<ObjectName> &&names) {330 return TypeDeclarationStmt(331 std::move(spec), std::list<AttrSpec>{}, makeEntityList(std::move(names)));332}333 334static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {335 // Assume INTEGER without kind selector.336 DeclarationTypeSpec typeSpec(337 IntrinsicTypeSpec{IntegerTypeSpec{std::nullopt}});338 339 return TypeDeclarationStmt(std::move(typeSpec), std::list<AttrSpec>{},340 makeEntityList(std::move(names)));341}342 343// --- Stylized expression handling -----------------------------------344 345// OpenMP has a concept of am "OpenMP stylized expression". Syntactially346// it looks like a typical Fortran expression (or statement), except:347// - the only variables allowed in it are OpenMP special variables, the348// exact set of these variables depends on the specific case of the349// stylized expression350// - the special OpenMP variables present may assume one or more types,351// and the expression should be semantically valid for each type.352//353// The stylized expression can be thought of as a template, which will be354// instantiated for each type provided somewhere in the context in which355// the stylized expression appears.356//357// AST nodes:358// - OmpStylizedExpression: contains the source string for the expression,359// plus the list of instances (OmpStylizedInstance).360// - OmpStylizedInstance: corresponds to the instantiation of the stylized361// expression for a specific type. The way that the type is specified is362// by creating declarations (OmpStylizedDeclaration) for the special363// variables. Together with the AST tree corresponding to the stylized364// expression the instantiation has enough information for semantic365// analysis. Each instance has its own scope, and the special variables366// have their own Symbol's (local to the scope).367// - OmpStylizedDeclaration: encapsulates the information that the visitors368// in resolve-names can use to "emulate" a declaration for a special369// variable and allow name resolution in the instantiation AST to work.370//371// Implementation specifics:372// The semantic analysis stores "evaluate::Expr" in each AST node rooted373// in parser::Expr (in the typedExpr member). The evaluate::Expr is specific374// to a given type, and so to allow different types for a given expression,375// for each type a separate copy of the parser::Expr subtree is created.376// Normally, AST nodes are non-copyable (copy-ctor is deleted), so to create377// several copies of a subtree, the same source string is parsed several378// times. The ParseState member in OmpStylizedExpression is the parser state379// immediately before the stylized expression.380//381// Initially, when OmpStylizedExpression is first created, the expression is382// parsed as if it was an actual code, but this parsing is only done to383// establish where the stylized expression ends (in the source). The source384// and the initial parser state are stored in the object, and the instance385// list is empty.386// Once the parsing of the containing OmpDirectiveSpecification completes,387// a post-processing "parser" (OmpStylizedInstanceCreator) executes. This388// post-processor examines the directive specification to see if it expects389// any stylized expressions to be contained in it, and then instantiates390// them for each such directive.391 392template <typename A> struct NeverParser {393 using resultType = A;394 std::optional<resultType> Parse(ParseState &state) const {395 // Always fail, but without any messages.396 return std::nullopt;397 }398};399 400template <typename A> constexpr auto never() { return NeverParser<A>{}; }401 402// Parser for optional<T> which always succeeds and returns std::nullptr.403// It's only needed to produce "std::optional<CallStmt::Chevrons>" in404// CallStmt.405template <typename A, typename B = void> struct NullParser;406template <typename B> struct NullParser<std::optional<B>> {407 using resultType = std::optional<B>;408 std::optional<resultType> Parse(ParseState &) const {409 return resultType{std::nullopt};410 }411};412 413template <typename A> constexpr auto null() { return NullParser<A>{}; }414 415// OmpStylizedDeclaration and OmpStylizedInstance are helper classes, and416// don't correspond to anything in the source. Their parsers should still417// exist, but they should never be executed.418TYPE_PARSER(construct<OmpStylizedDeclaration>(never<OmpStylizedDeclaration>()))419TYPE_PARSER(construct<OmpStylizedInstance>(never<OmpStylizedInstance>()))420 421TYPE_PARSER( //422 construct<OmpStylizedInstance::Instance>(Parser<AssignmentStmt>{}) ||423 construct<OmpStylizedInstance::Instance>(424 sourced(construct<CallStmt>(Parser<ProcedureDesignator>{},425 null<std::optional<CallStmt::Chevrons>>(),426 parenthesized(optionalList(actualArgSpec))))) ||427 construct<OmpStylizedInstance::Instance>(indirect(expr)))428 429struct OmpStylizedExpressionParser {430 using resultType = OmpStylizedExpression;431 432 std::optional<resultType> Parse(ParseState &state) const {433 auto *saved{new ParseState(state)};434 auto getSource{verbatim(Parser<OmpStylizedInstance::Instance>{} >> ok)};435 if (auto &&ok{getSource.Parse(state)}) {436 OmpStylizedExpression result{std::list<OmpStylizedInstance>{}};437 result.source = ok->source;438 result.state = saved;439 // result.v remains empty440 return std::move(result);441 }442 delete saved;443 return std::nullopt;444 }445};446 447static void Instantiate(OmpStylizedExpression &ose,448 llvm::ArrayRef<const OmpTypeName *> types, llvm::ArrayRef<CharBlock> vars) {449 // 1. For each var in the vars list, declare it with the corresponding450 // type from types.451 // 2. Run the parser to get the AST for the stylized expression.452 // 3. Create OmpStylizedInstance and append it to the list in ose.453 assert(types.size() == vars.size() && "List size mismatch");454 // A ParseState object is irreversibly modified during parsing (in455 // particular, it cannot be rewound to an earlier position in the source).456 // Because of that we need to create a local copy for each instantiation.457 // If rewinding was possible, we could just use the current one, and we458 // wouldn't need to save it in the AST node.459 ParseState state{DEREF(ose.state)};460 461 std::list<OmpStylizedDeclaration> decls;462 for (auto [type, var] : llvm::zip_equal(types, vars)) {463 decls.emplace_back(OmpStylizedDeclaration{464 common::Reference(*type), MakeEntityDecl(Name{var})});465 }466 467 if (auto &&instance{Parser<OmpStylizedInstance::Instance>{}.Parse(state)}) {468 ose.v.emplace_back(469 OmpStylizedInstance{std::move(decls), std::move(*instance)});470 }471}472 473static void InstantiateForTypes(OmpStylizedExpression &ose,474 const OmpTypeNameList &typeNames, llvm::ArrayRef<CharBlock> vars) {475 // For each type in the type list, declare all variables in vars with476 // that type, and complete the instantiation.477 for (const OmpTypeName &t : typeNames.v) {478 std::vector<const OmpTypeName *> types(vars.size(), &t);479 Instantiate(ose, types, vars);480 }481}482 483static void InstantiateDeclareReduction(OmpDirectiveSpecification &spec) {484 // There can be arguments/clauses that don't make sense, that analysis485 // is left until semantic checks. Tolerate any unexpected stuff.486 auto *rspec{GetFirstArgument<OmpReductionSpecifier>(spec)};487 if (!rspec) {488 return;489 }490 491 const OmpTypeNameList *typeNames{nullptr};492 493 if (auto *cexpr{494 const_cast<OmpCombinerExpression *>(GetCombinerExpr(*rspec))}) {495 typeNames = &std::get<OmpTypeNameList>(rspec->t);496 497 InstantiateForTypes(*cexpr, *typeNames, OmpCombinerExpression::Variables());498 delete cexpr->state;499 cexpr->state = nullptr;500 } else {501 // If there are no types, there is nothing else to do.502 return;503 }504 505 for (const OmpClause &clause : spec.Clauses().v) {506 llvm::omp::Clause id{clause.Id()};507 if (id == llvm::omp::Clause::OMPC_initializer) {508 if (auto *iexpr{const_cast<OmpInitializerExpression *>(509 GetInitializerExpr(clause))}) {510 InstantiateForTypes(511 *iexpr, *typeNames, OmpInitializerExpression::Variables());512 delete iexpr->state;513 iexpr->state = nullptr;514 }515 }516 }517}518 519static void InstantiateStylizedDirective(OmpDirectiveSpecification &spec) {520 const OmpDirectiveName &dirName{spec.DirName()};521 if (dirName.v == llvm::omp::Directive::OMPD_declare_reduction) {522 InstantiateDeclareReduction(spec);523 }524}525 526template <typename P,527 typename = std::enable_if_t<528 std::is_same_v<typename P::resultType, OmpDirectiveSpecification>>>529struct OmpStylizedInstanceCreator {530 using resultType = OmpDirectiveSpecification;531 constexpr OmpStylizedInstanceCreator(P p) : parser_(p) {}532 533 std::optional<resultType> Parse(ParseState &state) const {534 if (auto &&spec{parser_.Parse(state)}) {535 InstantiateStylizedDirective(*spec);536 return std::move(spec);537 }538 return std::nullopt;539 }540 541private:542 const P parser_;543};544 545template <typename P>546OmpStylizedInstanceCreator(P) -> OmpStylizedInstanceCreator<P>;547 548// --- Parsers for types ----------------------------------------------549 550TYPE_PARSER( //551 sourced(construct<OmpTypeName>(Parser<DeclarationTypeSpec>{})) ||552 sourced(construct<OmpTypeName>(Parser<TypeSpec>{})))553 554// --- Parsers for arguments ------------------------------------------555 556// At the moment these are only directive arguments. This is needed for557// parsing directive-specification.558 559TYPE_PARSER( //560 construct<OmpLocator>(Parser<OmpObject>{}) ||561 construct<OmpLocator>(Parser<FunctionReference>{}))562 563TYPE_PARSER(construct<OmpBaseVariantNames>(564 Parser<OmpObject>{} / ":", Parser<OmpObject>{}))565 566// Make the parsing of OmpArgument directive-sensitive. The issue is that567// name1:name2 can match either OmpBaseVariantNames or OmpReductionSpecifier.568// In the former case, "name2" is a name of a function, in the latter, of a569// type. To resolve the conflict we need information provided by name570// resolution, but by that time we can't modify the AST anymore, and the571// name resolution may have implicitly declared a symbol, or issued a message.572template <llvm::omp::Directive Id = llvm::omp::Directive::OMPD_unknown>573struct OmpArgumentParser {574 using resultType = OmpArgument;575 576 std::optional<resultType> Parse(ParseState &state) const {577 constexpr auto parser{sourced(first( //578 construct<OmpArgument>(Parser<OmpMapperSpecifier>{}),579 // By default, prefer OmpReductionSpecifier over OmpBaseVariantNames.580 construct<OmpArgument>(Parser<OmpReductionSpecifier>{}),581 construct<OmpArgument>(Parser<OmpLocator>{})))};582 return parser.Parse(state);583 }584};585 586template <>587struct OmpArgumentParser<llvm::omp::Directive::OMPD_declare_variant> {588 using resultType = OmpArgument;589 590 std::optional<resultType> Parse(ParseState &state) const {591 constexpr auto parser{sourced(first( //592 construct<OmpArgument>(Parser<OmpMapperSpecifier>{}),593 // In DECLARE_VARIANT parse OmpBaseVariantNames instead of594 // OmpReductionSpecifier.595 construct<OmpArgument>(Parser<OmpBaseVariantNames>{}),596 construct<OmpArgument>(Parser<OmpLocator>{})))};597 return parser.Parse(state);598 }599};600 601TYPE_PARSER(construct<OmpLocatorList>(nonemptyList(Parser<OmpLocator>{})))602 603template <llvm::omp::Directive Id = llvm::omp::Directive::OMPD_unknown>604struct OmpArgumentListParser {605 using resultType = OmpArgumentList;606 607 std::optional<resultType> Parse(ParseState &state) const {608 return sourced(609 construct<OmpArgumentList>(nonemptyList(OmpArgumentParser<Id>{})))610 .Parse(state);611 }612};613 614// 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list)615TYPE_PARSER(construct<OmpReductionIdentifier>(Parser<DefinedOperator>{}) ||616 construct<OmpReductionIdentifier>(Parser<ProcedureDesignator>{}))617 618TYPE_PARSER(construct<OmpReductionSpecifier>( //619 Parser<OmpReductionIdentifier>{},620 ":"_tok >> nonemptyList(Parser<OmpTypeName>{}),621 maybe(":"_tok >> Parser<OmpCombinerExpression>{})))622 623// --- Parsers for context traits -------------------------------------624 625static std::string nameToString(Name &&name) { return name.ToString(); }626 627TYPE_PARSER(sourced(construct<OmpTraitPropertyName>( //628 construct<OmpTraitPropertyName>(space >> charLiteralConstantWithoutKind) ||629 construct<OmpTraitPropertyName>(630 applyFunction(nameToString, Parser<Name>{})))))631 632TYPE_PARSER(sourced(construct<OmpTraitScore>( //633 "SCORE"_id >> parenthesized(scalarIntExpr))))634 635TYPE_PARSER(sourced(construct<OmpTraitPropertyExtension::Complex>(636 Parser<OmpTraitPropertyName>{},637 parenthesized(nonemptySeparated(638 indirect(Parser<OmpTraitPropertyExtension>{}), ",")))))639 640TYPE_PARSER(sourced(construct<OmpTraitPropertyExtension>(641 construct<OmpTraitPropertyExtension>(642 Parser<OmpTraitPropertyExtension::Complex>{}) ||643 construct<OmpTraitPropertyExtension>(Parser<OmpTraitPropertyName>{}) ||644 construct<OmpTraitPropertyExtension>(scalarExpr))))645 646TYPE_PARSER(construct<OmpTraitSelectorName::Value>(647 "ARCH"_id >> pure(OmpTraitSelectorName::Value::Arch) ||648 "ATOMIC_DEFAULT_MEM_ORDER"_id >>649 pure(OmpTraitSelectorName::Value::Atomic_Default_Mem_Order) ||650 "CONDITION"_id >> pure(OmpTraitSelectorName::Value::Condition) ||651 "DEVICE_NUM"_id >> pure(OmpTraitSelectorName::Value::Device_Num) ||652 "EXTENSION"_id >> pure(OmpTraitSelectorName::Value::Extension) ||653 "ISA"_id >> pure(OmpTraitSelectorName::Value::Isa) ||654 "KIND"_id >> pure(OmpTraitSelectorName::Value::Kind) ||655 "REQUIRES"_id >> pure(OmpTraitSelectorName::Value::Requires) ||656 "SIMD"_id >> pure(OmpTraitSelectorName::Value::Simd) ||657 "UID"_id >> pure(OmpTraitSelectorName::Value::Uid) ||658 "VENDOR"_id >> pure(OmpTraitSelectorName::Value::Vendor)))659 660TYPE_PARSER(sourced(construct<OmpTraitSelectorName>(661 // Parse predefined names first (because of SIMD).662 construct<OmpTraitSelectorName>(Parser<OmpTraitSelectorName::Value>{}) ||663 construct<OmpTraitSelectorName>(unwrap(OmpDirectiveNameParser{})) ||664 // identifier-or-string for extensions665 construct<OmpTraitSelectorName>(666 applyFunction(nameToString, Parser<Name>{})) ||667 construct<OmpTraitSelectorName>(space >> charLiteralConstantWithoutKind))))668 669// Parser for OmpTraitSelector::Properties670template <typename... PropParser>671static constexpr auto propertyListParser(PropParser... pp) {672 // Parse the property list "(score(expr): item1...)" in three steps:673 // 1. Parse the "("674 // 2. Parse the optional "score(expr):"675 // 3. Parse the "item1, ...)", together with the ")".676 // The reason for including the ")" in the 3rd step is to force parsing677 // the entire list in each of the alternative property parsers. Otherwise,678 // the name parser could stop after "foo" in "(foo, bar(1))", without679 // allowing the next parser to give the list a try.680 using P = OmpTraitProperty;681 return maybe("(" >> //682 construct<OmpTraitSelector::Properties>(683 maybe(Parser<OmpTraitScore>{} / ":"),684 (attempt(nonemptyList(sourced(construct<P>(pp))) / ")") || ...)));685}686 687// Parser for OmpTraitSelector688struct TraitSelectorParser {689 using resultType = OmpTraitSelector;690 691 constexpr TraitSelectorParser(Parser<OmpTraitSelectorName> p) : np(p) {}692 693 std::optional<resultType> Parse(ParseState &state) const {694 auto name{attempt(np).Parse(state)};695 if (!name.has_value()) {696 return std::nullopt;697 }698 699 // Default fallback parser for lists that cannot be parser using the700 // primary property parser.701 auto extParser{Parser<OmpTraitPropertyExtension>{}};702 703 if (auto *v{std::get_if<OmpTraitSelectorName::Value>(&name->u)}) {704 // (*) The comments below show the sections of the OpenMP spec that705 // describe given trait. The cases marked with a (*) are those where706 // the spec doesn't assign any list-type to these traits, but for707 // convenience they can be treated as if they were.708 switch (*v) {709 // name-list properties710 case OmpTraitSelectorName::Value::Arch: // [6.0:319:18]711 case OmpTraitSelectorName::Value::Extension: // [6.0:319:30]712 case OmpTraitSelectorName::Value::Isa: // [6.0:319:15]713 case OmpTraitSelectorName::Value::Kind: // [6.0:319:10]714 case OmpTraitSelectorName::Value::Uid: // [6.0:319:23](*)715 case OmpTraitSelectorName::Value::Vendor: { // [6.0:319:27]716 auto pp{propertyListParser(Parser<OmpTraitPropertyName>{}, extParser)};717 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));718 }719 // clause-list720 case OmpTraitSelectorName::Value::Atomic_Default_Mem_Order:721 // [6.0:321:26-29](*)722 case OmpTraitSelectorName::Value::Requires: // [6.0:319:33]723 case OmpTraitSelectorName::Value::Simd: { // [6.0:318:31]724 auto pp{propertyListParser(indirect(Parser<OmpClause>{}), extParser)};725 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));726 }727 // expr-list728 case OmpTraitSelectorName::Value::Condition: // [6.0:321:33](*)729 case OmpTraitSelectorName::Value::Device_Num: { // [6.0:321:23-24](*)730 auto pp{propertyListParser(scalarExpr, extParser)};731 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));732 }733 } // switch734 } else {735 // The other alternatives are `llvm::omp::Directive`, and `std::string`.736 // The former doesn't take any properties[1], the latter is a name of an737 // extension[2].738 // [1] [6.0:319:1-2]739 // [2] [6.0:319:36-37]740 auto pp{propertyListParser(extParser)};741 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));742 }743 744 llvm_unreachable("Unhandled trait name?");745 }746 747private:748 const Parser<OmpTraitSelectorName> np;749};750 751TYPE_PARSER(sourced(construct<OmpTraitSelector>(752 sourced(TraitSelectorParser(Parser<OmpTraitSelectorName>{})))))753 754TYPE_PARSER(construct<OmpTraitSetSelectorName::Value>(755 "CONSTRUCT"_id >> pure(OmpTraitSetSelectorName::Value::Construct) ||756 "DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Device) ||757 "IMPLEMENTATION"_id >>758 pure(OmpTraitSetSelectorName::Value::Implementation) ||759 "TARGET_DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Target_Device) ||760 "USER"_id >> pure(OmpTraitSetSelectorName::Value::User)))761 762TYPE_PARSER(sourced(construct<OmpTraitSetSelectorName>(763 Parser<OmpTraitSetSelectorName::Value>{})))764 765TYPE_PARSER(sourced(construct<OmpTraitSetSelector>( //766 Parser<OmpTraitSetSelectorName>{},767 "=" >> braced(nonemptySeparated(Parser<OmpTraitSelector>{}, ",")))))768 769TYPE_PARSER(sourced(construct<OmpContextSelectorSpecification>(770 nonemptySeparated(Parser<OmpTraitSetSelector>{}, ","))))771 772// Note: OmpContextSelector is a type alias.773 774// --- Parsers for clause modifiers -----------------------------------775 776TYPE_PARSER(construct<OmpAccessGroup>( //777 "CGROUP" >> pure(OmpAccessGroup::Value::Cgroup)))778 779TYPE_PARSER(construct<OmpAlignment>(scalarIntExpr))780 781TYPE_PARSER(construct<OmpAlignModifier>( //782 "ALIGN" >> parenthesized(scalarIntExpr)))783 784TYPE_PARSER(construct<OmpAllocatorComplexModifier>(785 "ALLOCATOR" >> parenthesized(scalarIntExpr)))786 787TYPE_PARSER(construct<OmpAllocatorSimpleModifier>(scalarIntExpr))788 789TYPE_PARSER(construct<OmpAlwaysModifier>( //790 "ALWAYS" >> pure(OmpAlwaysModifier::Value::Always)))791 792TYPE_PARSER(construct<OmpAttachModifier::Value>(793 "ALWAYS" >> pure(OmpAttachModifier::Value::Always) ||794 "AUTO" >> pure(OmpAttachModifier::Value::Auto) ||795 "NEVER" >> pure(OmpAttachModifier::Value::Never)))796 797TYPE_PARSER(construct<OmpAttachModifier>( //798 "ATTACH" >> parenthesized(Parser<OmpAttachModifier::Value>{})))799 800TYPE_PARSER(construct<OmpAutomapModifier>(801 "AUTOMAP" >> pure(OmpAutomapModifier::Value::Automap)))802 803TYPE_PARSER(construct<OmpChunkModifier>( //804 "SIMD" >> pure(OmpChunkModifier::Value::Simd)))805 806TYPE_PARSER(construct<OmpCloseModifier>( //807 "CLOSE" >> pure(OmpCloseModifier::Value::Close)))808 809TYPE_PARSER(construct<OmpDeleteModifier>( //810 "DELETE" >> pure(OmpDeleteModifier::Value::Delete)))811 812TYPE_PARSER(construct<OmpDependenceType>(813 "SINK" >> pure(OmpDependenceType::Value::Sink) ||814 "SOURCE" >> pure(OmpDependenceType::Value::Source)))815 816TYPE_PARSER(construct<OmpDeviceModifier>(817 "ANCESTOR" >> pure(OmpDeviceModifier::Value::Ancestor) ||818 "DEVICE_NUM" >> pure(OmpDeviceModifier::Value::Device_Num)))819 820TYPE_PARSER(construct<OmpDirectiveNameModifier>(OmpDirectiveNameParser{}))821 822TYPE_PARSER(construct<OmpExpectation>( //823 "PRESENT" >> pure(OmpExpectation::Value::Present)))824 825TYPE_PARSER(construct<OmpFallbackModifier>("FALLBACK"_tok >>826 parenthesized( //827 "ABORT" >> pure(OmpFallbackModifier::Value::Abort) ||828 "DEFAULT_MEM" >> pure(OmpFallbackModifier::Value::Default_Mem) ||829 "NULL" >> pure(OmpFallbackModifier::Value::Null))))830 831TYPE_PARSER(construct<OmpInteropRuntimeIdentifier>(832 construct<OmpInteropRuntimeIdentifier>(charLiteralConstant) ||833 construct<OmpInteropRuntimeIdentifier>(scalarIntConstantExpr)))834 835TYPE_PARSER(construct<OmpInteropPreference>(verbatim("PREFER_TYPE"_tok) >>836 parenthesized(nonemptyList(Parser<OmpInteropRuntimeIdentifier>{}))))837 838TYPE_PARSER(construct<OmpInteropType>(839 "TARGETSYNC" >> pure(OmpInteropType::Value::TargetSync) ||840 "TARGET" >> pure(OmpInteropType::Value::Target)))841 842TYPE_PARSER(construct<OmpIteratorSpecifier>(843 // Using Parser<TypeDeclarationStmt> or Parser<EntityDecl> has the problem844 // that they will attempt to treat what follows the '=' as initialization.845 // There are several issues with that,846 // 1. integer :: i = 0:10 will be parsed as "integer :: i = 0", followed847 // by triplet ":10".848 // 2. integer :: j = i:10 will be flagged as an error because the849 // initializer 'i' must be constant (in declarations). In an iterator850 // specifier the 'j' is not an initializer and can be a variable.851 (applyFunction<TypeDeclarationStmt>(makeIterSpecDecl,852 Parser<DeclarationTypeSpec>{} / maybe("::"_tok),853 nonemptyList(Parser<ObjectName>{}) / "="_tok) ||854 applyFunction<TypeDeclarationStmt>(855 makeIterSpecDecl, nonemptyList(Parser<ObjectName>{}) / "="_tok)),856 subscriptTriplet))857 858// [5.0] 2.1.6 iterator -> iterator-specifier-list859TYPE_PARSER(construct<OmpIterator>( //860 "ITERATOR" >>861 parenthesized(nonemptyList(sourced(Parser<OmpIteratorSpecifier>{})))))862 863TYPE_PARSER(construct<OmpLastprivateModifier>(864 "CONDITIONAL" >> pure(OmpLastprivateModifier::Value::Conditional)))865 866// 2.15.3.7 LINEAR (linear-list: linear-step)867// linear-list -> list | modifier(list)868// linear-modifier -> REF | VAL | UVAL869TYPE_PARSER(construct<OmpLinearModifier>( //870 "REF" >> pure(OmpLinearModifier::Value::Ref) ||871 "VAL" >> pure(OmpLinearModifier::Value::Val) ||872 "UVAL" >> pure(OmpLinearModifier::Value::Uval)))873 874TYPE_PARSER(construct<OmpMapper>( //875 "MAPPER"_tok >> parenthesized(Parser<ObjectName>{})))876 877// map-type -> ALLOC | DELETE | FROM | RELEASE | STORAGE | TO | TOFROM878TYPE_PARSER(construct<OmpMapType>( //879 "ALLOC" >> pure(OmpMapType::Value::Alloc) ||880 // Parse "DELETE" as OmpDeleteModifier881 "FROM" >> pure(OmpMapType::Value::From) ||882 "RELEASE" >> pure(OmpMapType::Value::Release) ||883 "STORAGE" >> pure(OmpMapType::Value::Storage) ||884 "TO"_id >> pure(OmpMapType::Value::To) ||885 "TOFROM" >> pure(OmpMapType::Value::Tofrom)))886 887TYPE_PARSER(construct<OmpOrderModifier>(888 "REPRODUCIBLE" >> pure(OmpOrderModifier::Value::Reproducible) ||889 "UNCONSTRAINED" >> pure(OmpOrderModifier::Value::Unconstrained)))890 891TYPE_PARSER(construct<OmpOrderingModifier>(892 "MONOTONIC" >> pure(OmpOrderingModifier::Value::Monotonic) ||893 "NONMONOTONIC" >> pure(OmpOrderingModifier::Value::Nonmonotonic) ||894 "SIMD" >> pure(OmpOrderingModifier::Value::Simd)))895 896TYPE_PARSER(construct<OmpPrescriptiveness>(897 "STRICT" >> pure(OmpPrescriptiveness::Value::Strict)))898 899TYPE_PARSER(construct<OmpPresentModifier>( //900 "PRESENT" >> pure(OmpPresentModifier::Value::Present)))901 902TYPE_PARSER(construct<OmpReductionModifier>(903 "INSCAN" >> pure(OmpReductionModifier::Value::Inscan) ||904 "TASK" >> pure(OmpReductionModifier::Value::Task) ||905 "DEFAULT" >> pure(OmpReductionModifier::Value::Default)))906 907TYPE_PARSER(construct<OmpRefModifier>( //908 "REF_PTEE" >> pure(OmpRefModifier::Value::Ref_Ptee) ||909 "REF_PTR"_id >> pure(OmpRefModifier::Value::Ref_Ptr) ||910 "REF_PTR_PTEE" >> pure(OmpRefModifier::Value::Ref_Ptr_Ptee)))911 912TYPE_PARSER(construct<OmpSelfModifier>( //913 "SELF" >> pure(OmpSelfModifier::Value::Self)))914 915TYPE_PARSER(construct<OmpStepComplexModifier>( //916 "STEP" >> parenthesized(scalarIntExpr)))917 918TYPE_PARSER(construct<OmpStepSimpleModifier>(scalarIntExpr))919 920TYPE_PARSER(construct<OmpTaskDependenceType>(921 "DEPOBJ" >> pure(OmpTaskDependenceType::Value::Depobj) ||922 "IN"_id >> pure(OmpTaskDependenceType::Value::In) ||923 "INOUT"_id >> pure(OmpTaskDependenceType::Value::Inout) ||924 "INOUTSET"_id >> pure(OmpTaskDependenceType::Value::Inoutset) ||925 "MUTEXINOUTSET" >> pure(OmpTaskDependenceType::Value::Mutexinoutset) ||926 "OUT" >> pure(OmpTaskDependenceType::Value::Out)))927 928TYPE_PARSER(construct<OmpVariableCategory>(929 "AGGREGATE" >> pure(OmpVariableCategory::Value::Aggregate) ||930 "ALL"_id >> pure(OmpVariableCategory::Value::All) ||931 "ALLOCATABLE" >> pure(OmpVariableCategory::Value::Allocatable) ||932 "POINTER" >> pure(OmpVariableCategory::Value::Pointer) ||933 "SCALAR" >> pure(OmpVariableCategory::Value::Scalar)))934 935TYPE_PARSER(construct<OmpxHoldModifier>( //936 "OMPX_HOLD" >> pure(OmpxHoldModifier::Value::Ompx_Hold)))937 938// This could be auto-generated.939TYPE_PARSER(940 sourced(construct<OmpAffinityClause::Modifier>(Parser<OmpIterator>{})))941 942TYPE_PARSER(943 sourced(construct<OmpAlignedClause::Modifier>(Parser<OmpAlignment>{})))944 945TYPE_PARSER(sourced(construct<OmpAllocateClause::Modifier>(sourced(946 construct<OmpAllocateClause::Modifier>(Parser<OmpAlignModifier>{}) ||947 construct<OmpAllocateClause::Modifier>(948 Parser<OmpAllocatorComplexModifier>{}) ||949 construct<OmpAllocateClause::Modifier>(950 Parser<OmpAllocatorSimpleModifier>{})))))951 952TYPE_PARSER(sourced(953 construct<OmpDefaultmapClause::Modifier>(Parser<OmpVariableCategory>{})))954 955TYPE_PARSER(sourced(construct<OmpDependClause::TaskDep::Modifier>(sourced(956 construct<OmpDependClause::TaskDep::Modifier>(Parser<OmpIterator>{}) ||957 construct<OmpDependClause::TaskDep::Modifier>(958 Parser<OmpTaskDependenceType>{})))))959 960TYPE_PARSER( //961 sourced(construct<OmpDynGroupprivateClause::Modifier>(962 Parser<OmpAccessGroup>{})) ||963 sourced(construct<OmpDynGroupprivateClause::Modifier>(964 Parser<OmpFallbackModifier>{})))965 966TYPE_PARSER(967 sourced(construct<OmpDeviceClause::Modifier>(Parser<OmpDeviceModifier>{})))968 969TYPE_PARSER(970 sourced(construct<OmpEnterClause::Modifier>(Parser<OmpAutomapModifier>{})))971 972TYPE_PARSER(sourced(construct<OmpFromClause::Modifier>(973 sourced(construct<OmpFromClause::Modifier>(Parser<OmpExpectation>{}) ||974 construct<OmpFromClause::Modifier>(Parser<OmpMapper>{}) ||975 construct<OmpFromClause::Modifier>(Parser<OmpIterator>{})))))976 977TYPE_PARSER(sourced(978 construct<OmpGrainsizeClause::Modifier>(Parser<OmpPrescriptiveness>{})))979 980TYPE_PARSER(sourced(981 construct<OmpIfClause::Modifier>(Parser<OmpDirectiveNameModifier>{})))982 983TYPE_PARSER(sourced(984 construct<OmpInitClause::Modifier>(985 construct<OmpInitClause::Modifier>(Parser<OmpInteropPreference>{})) ||986 construct<OmpInitClause::Modifier>(Parser<OmpInteropType>{})))987 988TYPE_PARSER(sourced(construct<OmpInReductionClause::Modifier>(989 Parser<OmpReductionIdentifier>{})))990 991TYPE_PARSER(sourced(construct<OmpLastprivateClause::Modifier>(992 Parser<OmpLastprivateModifier>{})))993 994TYPE_PARSER(sourced(995 construct<OmpLinearClause::Modifier>(Parser<OmpLinearModifier>{}) ||996 construct<OmpLinearClause::Modifier>(Parser<OmpStepComplexModifier>{}) ||997 construct<OmpLinearClause::Modifier>(Parser<OmpStepSimpleModifier>{})))998 999TYPE_PARSER(sourced(construct<OmpMapClause::Modifier>(1000 sourced(construct<OmpMapClause::Modifier>(Parser<OmpAlwaysModifier>{}) ||1001 construct<OmpMapClause::Modifier>(Parser<OmpAttachModifier>{}) ||1002 construct<OmpMapClause::Modifier>(Parser<OmpCloseModifier>{}) ||1003 construct<OmpMapClause::Modifier>(Parser<OmpDeleteModifier>{}) ||1004 construct<OmpMapClause::Modifier>(Parser<OmpPresentModifier>{}) ||1005 construct<OmpMapClause::Modifier>(Parser<OmpRefModifier>{}) ||1006 construct<OmpMapClause::Modifier>(Parser<OmpSelfModifier>{}) ||1007 construct<OmpMapClause::Modifier>(Parser<OmpMapper>{}) ||1008 construct<OmpMapClause::Modifier>(Parser<OmpIterator>{}) ||1009 construct<OmpMapClause::Modifier>(Parser<OmpMapType>{}) ||1010 construct<OmpMapClause::Modifier>(Parser<OmpxHoldModifier>{})))))1011 1012TYPE_PARSER(1013 sourced(construct<OmpOrderClause::Modifier>(Parser<OmpOrderModifier>{})))1014 1015TYPE_PARSER(sourced(1016 construct<OmpNumTasksClause::Modifier>(Parser<OmpPrescriptiveness>{})))1017 1018TYPE_PARSER(sourced(construct<OmpReductionClause::Modifier>(sourced(1019 construct<OmpReductionClause::Modifier>(Parser<OmpReductionModifier>{}) ||1020 construct<OmpReductionClause::Modifier>(1021 Parser<OmpReductionIdentifier>{})))))1022 1023TYPE_PARSER(sourced(construct<OmpScheduleClause::Modifier>(sourced(1024 construct<OmpScheduleClause::Modifier>(Parser<OmpChunkModifier>{}) ||1025 construct<OmpScheduleClause::Modifier>(Parser<OmpOrderingModifier>{})))))1026 1027TYPE_PARSER(sourced(construct<OmpTaskReductionClause::Modifier>(1028 Parser<OmpReductionIdentifier>{})))1029 1030TYPE_PARSER(sourced(construct<OmpToClause::Modifier>(1031 sourced(construct<OmpToClause::Modifier>(Parser<OmpExpectation>{}) ||1032 construct<OmpToClause::Modifier>(Parser<OmpMapper>{}) ||1033 construct<OmpToClause::Modifier>(Parser<OmpIterator>{})))))1034 1035TYPE_PARSER(sourced(construct<OmpWhenClause::Modifier>( //1036 Parser<OmpContextSelector>{})))1037 1038TYPE_PARSER(construct<OmpAppendArgsClause::OmpAppendOp>(1039 "INTEROP" >> parenthesized(nonemptyList(Parser<OmpInteropType>{}))))1040 1041TYPE_PARSER(construct<OmpAdjustArgsClause::OmpAdjustOp>(1042 "NOTHING" >> pure(OmpAdjustArgsClause::OmpAdjustOp::Value::Nothing) ||1043 "NEED_DEVICE_PTR" >>1044 pure(OmpAdjustArgsClause::OmpAdjustOp::Value::Need_Device_Ptr)))1045 1046// --- Parsers for clauses --------------------------------------------1047 1048/// `MOBClause` is a clause that has a1049/// std::tuple<Modifiers, OmpObjectList, bool>.1050/// Helper function to create a typical modifiers-objects clause, where the1051/// commas separating individual modifiers are optional, and the clause1052/// contains a bool member to indicate whether it was fully comma-separated1053/// or not.1054template <bool CommaSeparated, typename MOBClause>1055static inline MOBClause makeMobClause(1056 std::list<typename MOBClause::Modifier> &&mods, OmpObjectList &&objs) {1057 if (!mods.empty()) {1058 return MOBClause{std::move(mods), std::move(objs), CommaSeparated};1059 } else {1060 using ListTy = std::list<typename MOBClause::Modifier>;1061 return MOBClause{std::optional<ListTy>{}, std::move(objs), CommaSeparated};1062 }1063}1064 1065TYPE_PARSER(construct<OmpAdjustArgsClause>(1066 (Parser<OmpAdjustArgsClause::OmpAdjustOp>{} / ":"),1067 Parser<OmpObjectList>{}))1068 1069// [5.0] 2.10.1 affinity([aff-modifier:] locator-list)1070// aff-modifier: interator-modifier1071TYPE_PARSER(construct<OmpAffinityClause>(1072 maybe(nonemptyList(Parser<OmpAffinityClause::Modifier>{}) / ":"),1073 Parser<OmpObjectList>{}))1074 1075// 2.4 Requires construct [OpenMP 5.0]1076// atomic-default-mem-order-clause ->1077// acq_rel1078// acquire1079// relaxed1080// release1081// seq_cst1082TYPE_PARSER(construct<OmpAtomicDefaultMemOrderClause>(1083 "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) ||1084 "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) ||1085 "RELAXED" >> pure(common::OmpMemoryOrderType::Relaxed) ||1086 "RELEASE" >> pure(common::OmpMemoryOrderType::Release) ||1087 "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst)))1088 1089TYPE_PARSER(construct<OmpCancellationConstructTypeClause>(1090 OmpDirectiveNameParser{}, maybe(parenthesized(scalarLogicalExpr))))1091 1092TYPE_PARSER(construct<OmpAppendArgsClause>(1093 nonemptyList(Parser<OmpAppendArgsClause::OmpAppendOp>{})))1094 1095// 2.15.3.1 DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE)1096TYPE_PARSER(construct<OmpDefaultClause::DataSharingAttribute>(1097 "PRIVATE" >> pure(OmpDefaultClause::DataSharingAttribute::Private) ||1098 "FIRSTPRIVATE" >>1099 pure(OmpDefaultClause::DataSharingAttribute::Firstprivate) ||1100 "SHARED" >> pure(OmpDefaultClause::DataSharingAttribute::Shared) ||1101 "NONE" >> pure(OmpDefaultClause::DataSharingAttribute::None)))1102 1103TYPE_PARSER(construct<OmpDefaultClause>(1104 construct<OmpDefaultClause>(1105 Parser<OmpDefaultClause::DataSharingAttribute>{}) ||1106 construct<OmpDefaultClause>(indirect(Parser<OmpDirectiveSpecification>{}))))1107 1108TYPE_PARSER(construct<OmpDynGroupprivateClause>(1109 maybe(nonemptyList(Parser<OmpDynGroupprivateClause::Modifier>{}) / ":"),1110 scalarIntExpr))1111 1112TYPE_PARSER(construct<OmpEnterClause>(1113 maybe(nonemptyList(Parser<OmpEnterClause::Modifier>{}) / ":"),1114 Parser<OmpObjectList>{}))1115 1116TYPE_PARSER(construct<OmpFailClause>(1117 "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) ||1118 "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) ||1119 "RELAXED" >> pure(common::OmpMemoryOrderType::Relaxed) ||1120 "RELEASE" >> pure(common::OmpMemoryOrderType::Release) ||1121 "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst)))1122 1123TYPE_PARSER(construct<OmpGraphIdClause>(scalarIntExpr))1124 1125TYPE_PARSER(construct<OmpGraphResetClause>(scalarLogicalExpr))1126 1127// 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD)1128TYPE_PARSER(construct<OmpProcBindClause>(1129 "CLOSE" >> pure(OmpProcBindClause::AffinityPolicy::Close) ||1130 "MASTER" >> pure(OmpProcBindClause::AffinityPolicy::Master) ||1131 "PRIMARY" >> pure(OmpProcBindClause::AffinityPolicy::Primary) ||1132 "SPREAD" >> pure(OmpProcBindClause::AffinityPolicy::Spread)))1133 1134TYPE_PARSER(construct<OmpMapClause>(1135 applyFunction<OmpMapClause>(makeMobClause<true>,1136 modifierList<OmpMapClause>(","_tok), Parser<OmpObjectList>{}) ||1137 applyFunction<OmpMapClause>(makeMobClause<false>,1138 modifierList<OmpMapClause>(maybe(","_tok)), Parser<OmpObjectList>{})))1139 1140// [OpenMP 5.0]1141// 2.19.7.2 defaultmap(implicit-behavior[:variable-category])1142// implicit-behavior -> ALLOC | TO | FROM | TOFROM | FIRSRTPRIVATE | NONE |1143// DEFAULT | PRESENT1144// variable-category -> ALL | SCALAR | AGGREGATE | ALLOCATABLE | POINTER1145TYPE_PARSER(construct<OmpDefaultmapClause>(1146 construct<OmpDefaultmapClause::ImplicitBehavior>(1147 "ALLOC" >> pure(OmpDefaultmapClause::ImplicitBehavior::Alloc) ||1148 "TO"_id >> pure(OmpDefaultmapClause::ImplicitBehavior::To) ||1149 "FROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::From) ||1150 "TOFROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::Tofrom) ||1151 "FIRSTPRIVATE" >>1152 pure(OmpDefaultmapClause::ImplicitBehavior::Firstprivate) ||1153 "NONE" >> pure(OmpDefaultmapClause::ImplicitBehavior::None) ||1154 "DEFAULT" >> pure(OmpDefaultmapClause::ImplicitBehavior::Default) ||1155 "PRESENT" >> pure(OmpDefaultmapClause::ImplicitBehavior::Present)),1156 maybe(":" >> nonemptyList(Parser<OmpDefaultmapClause::Modifier>{}))))1157 1158TYPE_PARSER(construct<OmpScheduleClause::Kind>(1159 "STATIC" >> pure(OmpScheduleClause::Kind::Static) ||1160 "DYNAMIC" >> pure(OmpScheduleClause::Kind::Dynamic) ||1161 "GUIDED" >> pure(OmpScheduleClause::Kind::Guided) ||1162 "AUTO" >> pure(OmpScheduleClause::Kind::Auto) ||1163 "RUNTIME" >> pure(OmpScheduleClause::Kind::Runtime)))1164 1165TYPE_PARSER(construct<OmpScheduleClause>(1166 maybe(nonemptyList(Parser<OmpScheduleClause::Modifier>{}) / ":"),1167 Parser<OmpScheduleClause::Kind>{}, maybe("," >> scalarIntExpr)))1168 1169// device([ device-modifier :] scalar-integer-expression)1170TYPE_PARSER(construct<OmpDeviceClause>(1171 maybe(nonemptyList(Parser<OmpDeviceClause::Modifier>{}) / ":"),1172 scalarIntExpr))1173 1174// device_type(any | host | nohost)1175TYPE_PARSER(construct<OmpDeviceTypeClause>(1176 "ANY" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Any) ||1177 "HOST" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Host) ||1178 "NOHOST" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Nohost)))1179 1180// 2.12 IF (directive-name-modifier: scalar-logical-expr)1181TYPE_PARSER(construct<OmpIfClause>(1182 maybe(nonemptyList(Parser<OmpIfClause::Modifier>{}) / ":"),1183 scalarLogicalExpr))1184 1185TYPE_PARSER(construct<OmpReductionClause>(1186 maybe(nonemptyList(Parser<OmpReductionClause::Modifier>{}) / ":"),1187 Parser<OmpObjectList>{}))1188 1189TYPE_PARSER(construct<OmpReplayableClause>(scalarLogicalConstantExpr))1190 1191// OMP 5.0 2.19.5.6 IN_REDUCTION (reduction-identifier: variable-name-list)1192TYPE_PARSER(construct<OmpInReductionClause>(1193 maybe(nonemptyList(Parser<OmpInReductionClause::Modifier>{}) / ":"),1194 Parser<OmpObjectList>{}))1195 1196TYPE_PARSER(construct<OmpTaskReductionClause>(1197 maybe(nonemptyList(Parser<OmpTaskReductionClause::Modifier>{}) / ":"),1198 Parser<OmpObjectList>{}))1199 1200TYPE_PARSER(construct<OmpTransparentClause>(scalarIntExpr))1201 1202// OMP 5.0 2.11.4 allocate-clause -> ALLOCATE ([allocator:] variable-name-list)1203// OMP 5.2 2.13.4 allocate-clause -> ALLOCATE ([allocate-modifier1204// [, allocate-modifier] :]1205// variable-name-list)1206// allocate-modifier -> allocator | align1207TYPE_PARSER(construct<OmpAllocateClause>(1208 maybe(nonemptyList(Parser<OmpAllocateClause::Modifier>{}) / ":"),1209 Parser<OmpObjectList>{}))1210 1211// iteration-offset -> +/- non-negative-constant-expr1212TYPE_PARSER(construct<OmpIterationOffset>(1213 Parser<DefinedOperator>{}, scalarIntConstantExpr))1214 1215// iteration -> iteration-variable [+/- nonnegative-scalar-integer-constant]1216TYPE_PARSER(construct<OmpIteration>(name, maybe(Parser<OmpIterationOffset>{})))1217 1218TYPE_PARSER(construct<OmpIterationVector>(nonemptyList(Parser<OmpIteration>{})))1219 1220TYPE_PARSER(construct<OmpDoacross>(1221 construct<OmpDoacross>(construct<OmpDoacross::Sink>(1222 "SINK"_tok >> ":"_tok >> Parser<OmpIterationVector>{})) ||1223 construct<OmpDoacross>(construct<OmpDoacross::Source>("SOURCE"_tok))))1224 1225TYPE_CONTEXT_PARSER("Omp Depend clause"_en_US,1226 construct<OmpDependClause>(1227 // Try to parse OmpDoacross first, because TaskDep will succeed on1228 // "sink: xxx", interpreting it to not have any modifiers, and "sink"1229 // being an OmpObject. Parsing of the TaskDep variant will stop right1230 // after the "sink", leaving the ": xxx" unvisited.1231 construct<OmpDependClause>(Parser<OmpDoacross>{}) ||1232 // Parse TaskDep after Doacross.1233 construct<OmpDependClause>(construct<OmpDependClause::TaskDep>(1234 maybe(nonemptyList(Parser<OmpDependClause::TaskDep::Modifier>{}) /1235 ": "),1236 Parser<OmpObjectList>{}))))1237 1238TYPE_CONTEXT_PARSER("Omp Doacross clause"_en_US,1239 construct<OmpDoacrossClause>(Parser<OmpDoacross>{}))1240 1241TYPE_PARSER(construct<OmpFromClause>(1242 applyFunction<OmpFromClause>(makeMobClause<true>,1243 modifierList<OmpFromClause>(","_tok), Parser<OmpObjectList>{}) ||1244 applyFunction<OmpFromClause>(makeMobClause<false>,1245 modifierList<OmpFromClause>(maybe(","_tok)), Parser<OmpObjectList>{})))1246 1247TYPE_PARSER(construct<OmpToClause>(1248 applyFunction<OmpToClause>(makeMobClause<true>,1249 modifierList<OmpToClause>(","_tok), Parser<OmpObjectList>{}) ||1250 applyFunction<OmpToClause>(makeMobClause<false>,1251 modifierList<OmpToClause>(maybe(","_tok)), Parser<OmpObjectList>{})))1252 1253OmpLinearClause makeLinearFromOldSyntax(OmpLinearClause::Modifier &&lm,1254 OmpObjectList &&objs, std::optional<OmpLinearClause::Modifier> &&ssm) {1255 std::list<OmpLinearClause::Modifier> mods;1256 mods.emplace_back(std::move(lm));1257 if (ssm) {1258 mods.emplace_back(std::move(*ssm));1259 }1260 return OmpLinearClause{std::move(objs),1261 mods.empty() ? decltype(mods){} : std::move(mods),1262 /*PostModified=*/false};1263}1264 1265TYPE_PARSER(1266 // Parse the "modifier(x)" first, because syntacticaly it will match1267 // an array element (i.e. a list item).1268 // LINEAR(linear-modifier(list) [: step-simple-modifier])1269 construct<OmpLinearClause>( //1270 applyFunction<OmpLinearClause>(makeLinearFromOldSyntax,1271 SpecificModifierParser<OmpLinearModifier, OmpLinearClause>{},1272 parenthesized(Parser<OmpObjectList>{}),1273 maybe(":"_tok >> SpecificModifierParser<OmpStepSimpleModifier,1274 OmpLinearClause>{}))) ||1275 // LINEAR(list [: modifiers])1276 construct<OmpLinearClause>( //1277 Parser<OmpObjectList>{},1278 maybe(":"_tok >> nonemptyList(Parser<OmpLinearClause::Modifier>{})),1279 /*PostModified=*/pure(true)))1280 1281TYPE_PARSER(construct<OmpLoopRangeClause>(1282 scalarIntConstantExpr, "," >> scalarIntConstantExpr))1283 1284// OpenMPv5.2 12.5.2 detach-clause -> DETACH (event-handle)1285TYPE_PARSER(construct<OmpDetachClause>(Parser<OmpObject>{}))1286 1287TYPE_PARSER(construct<OmpHintClause>(scalarIntConstantExpr))1288 1289// init clause1290TYPE_PARSER(construct<OmpInitClause>(1291 maybe(nonemptyList(Parser<OmpInitClause::Modifier>{}) / ":"),1292 Parser<OmpObject>{}))1293 1294// 2.8.1 ALIGNED (list: alignment)1295TYPE_PARSER(construct<OmpAlignedClause>(Parser<OmpObjectList>{},1296 maybe(":" >> nonemptyList(Parser<OmpAlignedClause::Modifier>{}))))1297 1298TYPE_PARSER( //1299 construct<OmpUpdateClause>(parenthesized(Parser<OmpDependenceType>{})) ||1300 construct<OmpUpdateClause>(parenthesized(Parser<OmpTaskDependenceType>{})))1301 1302TYPE_PARSER(construct<OmpOrderClause>(1303 maybe(nonemptyList(Parser<OmpOrderClause::Modifier>{}) / ":"),1304 "CONCURRENT" >> pure(OmpOrderClause::Ordering::Concurrent)))1305 1306TYPE_PARSER(construct<OmpMatchClause>(1307 Parser<traits::OmpContextSelectorSpecification>{}))1308 1309TYPE_PARSER(construct<OmpOtherwiseClause>(1310 maybe(indirect(Parser<OmpDirectiveSpecification>{}))))1311 1312TYPE_PARSER(construct<OmpWhenClause>(1313 maybe(nonemptyList(Parser<OmpWhenClause::Modifier>{}) / ":"),1314 maybe(indirect(1315 OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{})))))1316 1317// OMP 5.2 12.6.1 grainsize([ prescriptiveness :] scalar-integer-expression)1318TYPE_PARSER(construct<OmpGrainsizeClause>(1319 maybe(nonemptyList(Parser<OmpGrainsizeClause::Modifier>{}) / ":"),1320 scalarIntExpr))1321 1322// OMP 5.2 12.6.2 num_tasks([ prescriptiveness :] scalar-integer-expression)1323TYPE_PARSER(construct<OmpNumTasksClause>(1324 maybe(nonemptyList(Parser<OmpNumTasksClause::Modifier>{}) / ":"),1325 scalarIntExpr))1326 1327TYPE_PARSER( //1328 construct<OmpObject>(designator) ||1329 "/" >> construct<OmpObject>(name) / "/" ||1330 construct<OmpObject>(sourced(construct<OmpObject::Invalid>(1331 "//"_tok >> pure(OmpObject::Invalid::Kind::BlankCommonBlock)))))1332 1333// OMP 5.0 2.19.4.5 LASTPRIVATE ([lastprivate-modifier :] list)1334TYPE_PARSER(construct<OmpLastprivateClause>(1335 maybe(nonemptyList(Parser<OmpLastprivateClause::Modifier>{}) / ":"),1336 Parser<OmpObjectList>{}))1337 1338// OMP 5.2 11.7.1 BIND ( PARALLEL | TEAMS | THREAD )1339TYPE_PARSER(construct<OmpBindClause>(1340 "PARALLEL" >> pure(OmpBindClause::Binding::Parallel) ||1341 "TEAMS" >> pure(OmpBindClause::Binding::Teams) ||1342 "THREAD" >> pure(OmpBindClause::Binding::Thread)))1343 1344TYPE_PARSER(construct<OmpAlignClause>(scalarIntConstantExpr))1345 1346TYPE_PARSER(construct<OmpAtClause>(1347 "EXECUTION" >> pure(OmpAtClause::ActionTime::Execution) ||1348 "COMPILATION" >> pure(OmpAtClause::ActionTime::Compilation)))1349 1350TYPE_PARSER(construct<OmpSeverityClause>(1351 "FATAL" >> pure(OmpSeverityClause::Severity::Fatal) ||1352 "WARNING" >> pure(OmpSeverityClause::Severity::Warning)))1353 1354TYPE_PARSER(construct<OmpMessageClause>(expr))1355 1356TYPE_PARSER(construct<OmpHoldsClause>(indirect(expr)))1357TYPE_PARSER(construct<OmpAbsentClause>(many(maybe(","_tok) >>1358 construct<llvm::omp::Directive>(unwrap(OmpDirectiveNameParser{})))))1359TYPE_PARSER(construct<OmpContainsClause>(many(maybe(","_tok) >>1360 construct<llvm::omp::Directive>(unwrap(OmpDirectiveNameParser{})))))1361 1362TYPE_PARSER( //1363 "ABSENT" >> construct<OmpClause>(construct<OmpClause::Absent>(1364 parenthesized(Parser<OmpAbsentClause>{}))) ||1365 "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||1366 "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||1367 "ADJUST_ARGS" >> construct<OmpClause>(construct<OmpClause::AdjustArgs>(1368 parenthesized(Parser<OmpAdjustArgsClause>{}))) ||1369 "AFFINITY" >> construct<OmpClause>(construct<OmpClause::Affinity>(1370 parenthesized(Parser<OmpAffinityClause>{}))) ||1371 "ALIGN" >> construct<OmpClause>(construct<OmpClause::Align>(1372 parenthesized(Parser<OmpAlignClause>{}))) ||1373 "ALIGNED" >> construct<OmpClause>(construct<OmpClause::Aligned>(1374 parenthesized(Parser<OmpAlignedClause>{}))) ||1375 "ALLOCATE" >> construct<OmpClause>(construct<OmpClause::Allocate>(1376 parenthesized(Parser<OmpAllocateClause>{}))) ||1377 "APPEND_ARGS" >> construct<OmpClause>(construct<OmpClause::AppendArgs>(1378 parenthesized(Parser<OmpAppendArgsClause>{}))) ||1379 "ALLOCATOR" >> construct<OmpClause>(construct<OmpClause::Allocator>(1380 parenthesized(scalarIntExpr))) ||1381 "AT" >> construct<OmpClause>(construct<OmpClause::At>(1382 parenthesized(Parser<OmpAtClause>{}))) ||1383 "ATOMIC_DEFAULT_MEM_ORDER" >>1384 construct<OmpClause>(construct<OmpClause::AtomicDefaultMemOrder>(1385 parenthesized(Parser<OmpAtomicDefaultMemOrderClause>{}))) ||1386 "BIND" >> construct<OmpClause>(construct<OmpClause::Bind>(1387 parenthesized(Parser<OmpBindClause>{}))) ||1388 "CAPTURE" >> construct<OmpClause>(construct<OmpClause::Capture>()) ||1389 "COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>(1390 parenthesized(scalarIntConstantExpr))) ||1391 "COMPARE" >> construct<OmpClause>(construct<OmpClause::Compare>()) ||1392 "CONTAINS" >> construct<OmpClause>(construct<OmpClause::Contains>(1393 parenthesized(Parser<OmpContainsClause>{}))) ||1394 "COPYIN" >> construct<OmpClause>(construct<OmpClause::Copyin>(1395 parenthesized(Parser<OmpObjectList>{}))) ||1396 "COPYPRIVATE" >> construct<OmpClause>(construct<OmpClause::Copyprivate>(1397 (parenthesized(Parser<OmpObjectList>{})))) ||1398 "DEFAULT"_id >> construct<OmpClause>(construct<OmpClause::Default>(1399 parenthesized(Parser<OmpDefaultClause>{}))) ||1400 "DEFAULTMAP" >> construct<OmpClause>(construct<OmpClause::Defaultmap>(1401 parenthesized(Parser<OmpDefaultmapClause>{}))) ||1402 "DEPEND" >> construct<OmpClause>(construct<OmpClause::Depend>(1403 parenthesized(Parser<OmpDependClause>{}))) ||1404 "DESTROY" >>1405 construct<OmpClause>(construct<OmpClause::Destroy>(maybe(parenthesized(1406 construct<OmpDestroyClause>(Parser<OmpObject>{}))))) ||1407 "DEVICE" >> construct<OmpClause>(construct<OmpClause::Device>(1408 parenthesized(Parser<OmpDeviceClause>{}))) ||1409 "DEVICE_SAFESYNC" >>1410 construct<OmpClause>(construct<OmpClause::DeviceSafesync>(1411 maybe(parenthesized(scalarLogicalConstantExpr)))) ||1412 "DEVICE_TYPE" >> construct<OmpClause>(construct<OmpClause::DeviceType>(1413 parenthesized(Parser<OmpDeviceTypeClause>{}))) ||1414 "DIST_SCHEDULE" >>1415 construct<OmpClause>(construct<OmpClause::DistSchedule>(1416 parenthesized("STATIC" >> maybe("," >> scalarIntExpr)))) ||1417 "DOACROSS" >>1418 construct<OmpClause>(parenthesized(Parser<OmpDoacrossClause>{})) ||1419 "DYNAMIC_ALLOCATORS" >>1420 construct<OmpClause>(construct<OmpClause::DynamicAllocators>(1421 maybe(parenthesized(scalarLogicalConstantExpr)))) ||1422 "DYN_GROUPPRIVATE" >>1423 construct<OmpClause>(construct<OmpClause::DynGroupprivate>(1424 parenthesized(Parser<OmpDynGroupprivateClause>{}))) ||1425 "ENTER" >> construct<OmpClause>(construct<OmpClause::Enter>(1426 parenthesized(Parser<OmpEnterClause>{}))) ||1427 "EXCLUSIVE" >> construct<OmpClause>(construct<OmpClause::Exclusive>(1428 parenthesized(Parser<OmpObjectList>{}))) ||1429 "FAIL" >> construct<OmpClause>(construct<OmpClause::Fail>(1430 parenthesized(Parser<OmpFailClause>{}))) ||1431 "FILTER" >> construct<OmpClause>(construct<OmpClause::Filter>(1432 parenthesized(scalarIntExpr))) ||1433 "FINAL" >> construct<OmpClause>(construct<OmpClause::Final>(1434 parenthesized(scalarLogicalExpr))) ||1435 "FIRSTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Firstprivate>(1436 parenthesized(Parser<OmpObjectList>{}))) ||1437 "FROM" >> construct<OmpClause>(construct<OmpClause::From>(1438 parenthesized(Parser<OmpFromClause>{}))) ||1439 "FULL" >> construct<OmpClause>(construct<OmpClause::Full>()) ||1440 "GRAINSIZE" >> construct<OmpClause>(construct<OmpClause::Grainsize>(1441 parenthesized(Parser<OmpGrainsizeClause>{}))) ||1442 "GRAPH_ID" >> construct<OmpClause>(construct<OmpClause::GraphId>(1443 parenthesized(Parser<OmpGraphIdClause>{}))) ||1444 "GRAPH_RESET" >>1445 construct<OmpClause>(construct<OmpClause::GraphReset>(1446 maybe(parenthesized(Parser<OmpGraphResetClause>{})))) ||1447 "HAS_DEVICE_ADDR" >>1448 construct<OmpClause>(construct<OmpClause::HasDeviceAddr>(1449 parenthesized(Parser<OmpObjectList>{}))) ||1450 "HINT" >> construct<OmpClause>(construct<OmpClause::Hint>(1451 parenthesized(Parser<OmpHintClause>{}))) ||1452 "HOLDS" >> construct<OmpClause>(construct<OmpClause::Holds>(1453 parenthesized(Parser<OmpHoldsClause>{}))) ||1454 "IF" >> construct<OmpClause>(construct<OmpClause::If>(1455 parenthesized(Parser<OmpIfClause>{}))) ||1456 "INBRANCH" >> construct<OmpClause>(construct<OmpClause::Inbranch>()) ||1457 "INDIRECT" >> construct<OmpClause>(construct<OmpClause::Indirect>(1458 maybe(parenthesized(scalarLogicalExpr)))) ||1459 "INIT" >> construct<OmpClause>(construct<OmpClause::Init>(1460 parenthesized(Parser<OmpInitClause>{}))) ||1461 "INCLUSIVE" >> construct<OmpClause>(construct<OmpClause::Inclusive>(1462 parenthesized(Parser<OmpObjectList>{}))) ||1463 "INITIALIZER" >> construct<OmpClause>(construct<OmpClause::Initializer>(1464 parenthesized(Parser<OmpInitializerClause>{}))) ||1465 "IS_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::IsDevicePtr>(1466 parenthesized(Parser<OmpObjectList>{}))) ||1467 "LASTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Lastprivate>(1468 parenthesized(Parser<OmpLastprivateClause>{}))) ||1469 "LINEAR" >> construct<OmpClause>(construct<OmpClause::Linear>(1470 parenthesized(Parser<OmpLinearClause>{}))) ||1471 "LINK" >> construct<OmpClause>(construct<OmpClause::Link>(1472 parenthesized(Parser<OmpObjectList>{}))) ||1473 "LOOPRANGE" >> construct<OmpClause>(construct<OmpClause::Looprange>(1474 parenthesized(Parser<OmpLoopRangeClause>{}))) ||1475 "MAP" >> construct<OmpClause>(construct<OmpClause::Map>(1476 parenthesized(Parser<OmpMapClause>{}))) ||1477 "MATCH" >> construct<OmpClause>(construct<OmpClause::Match>(1478 parenthesized(Parser<OmpMatchClause>{}))) ||1479 "MERGEABLE" >> construct<OmpClause>(construct<OmpClause::Mergeable>()) ||1480 "MESSAGE" >> construct<OmpClause>(construct<OmpClause::Message>(1481 parenthesized(Parser<OmpMessageClause>{}))) ||1482 "NOCONTEXT" >> construct<OmpClause>(construct<OmpClause::Nocontext>(1483 parenthesized(scalarLogicalExpr))) ||1484 "NOGROUP" >> construct<OmpClause>(construct<OmpClause::Nogroup>()) ||1485 "NONTEMPORAL" >> construct<OmpClause>(construct<OmpClause::Nontemporal>(1486 parenthesized(nonemptyList(name)))) ||1487 "NOTINBRANCH" >>1488 construct<OmpClause>(construct<OmpClause::Notinbranch>()) ||1489 "NOVARIANTS" >> construct<OmpClause>(construct<OmpClause::Novariants>(1490 parenthesized(scalarLogicalExpr))) ||1491 "NOWAIT" >> construct<OmpClause>(construct<OmpClause::Nowait>()) ||1492 "NO_OPENMP"_id >> construct<OmpClause>(construct<OmpClause::NoOpenmp>()) ||1493 "NO_OPENMP_ROUTINES" >>1494 construct<OmpClause>(construct<OmpClause::NoOpenmpRoutines>()) ||1495 "NO_PARALLELISM" >>1496 construct<OmpClause>(construct<OmpClause::NoParallelism>()) ||1497 "NUM_TASKS" >> construct<OmpClause>(construct<OmpClause::NumTasks>(1498 parenthesized(Parser<OmpNumTasksClause>{}))) ||1499 "NUM_TEAMS" >> construct<OmpClause>(construct<OmpClause::NumTeams>(1500 parenthesized(scalarIntExpr))) ||1501 "NUM_THREADS" >> construct<OmpClause>(construct<OmpClause::NumThreads>(1502 parenthesized(scalarIntExpr))) ||1503 "OMPX_BARE" >> construct<OmpClause>(construct<OmpClause::OmpxBare>()) ||1504 "ORDER" >> construct<OmpClause>(construct<OmpClause::Order>(1505 parenthesized(Parser<OmpOrderClause>{}))) ||1506 "ORDERED" >> construct<OmpClause>(construct<OmpClause::Ordered>(1507 maybe(parenthesized(scalarIntConstantExpr)))) ||1508 "OTHERWISE" >> construct<OmpClause>(construct<OmpClause::Otherwise>(1509 maybe(parenthesized(Parser<OmpOtherwiseClause>{})))) ||1510 "PARTIAL" >> construct<OmpClause>(construct<OmpClause::Partial>(1511 maybe(parenthesized(scalarIntConstantExpr)))) ||1512 "PRIORITY" >> construct<OmpClause>(construct<OmpClause::Priority>(1513 parenthesized(scalarIntExpr))) ||1514 "PRIVATE" >> construct<OmpClause>(construct<OmpClause::Private>(1515 parenthesized(Parser<OmpObjectList>{}))) ||1516 "PROC_BIND" >> construct<OmpClause>(construct<OmpClause::ProcBind>(1517 parenthesized(Parser<OmpProcBindClause>{}))) ||1518 "REDUCTION"_id >> construct<OmpClause>(construct<OmpClause::Reduction>(1519 parenthesized(Parser<OmpReductionClause>{}))) ||1520 "IN_REDUCTION" >> construct<OmpClause>(construct<OmpClause::InReduction>(1521 parenthesized(Parser<OmpInReductionClause>{}))) ||1522 "DETACH" >> construct<OmpClause>(construct<OmpClause::Detach>(1523 parenthesized(Parser<OmpDetachClause>{}))) ||1524 "TASK_REDUCTION" >>1525 construct<OmpClause>(construct<OmpClause::TaskReduction>(1526 parenthesized(Parser<OmpTaskReductionClause>{}))) ||1527 "READ" >> construct<OmpClause>(construct<OmpClause::Read>()) ||1528 "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>()) ||1529 "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) ||1530 "REPLAYABLE" >> construct<OmpClause>(construct<OmpClause::Replayable>(1531 maybe(parenthesized(Parser<OmpReplayableClause>{})))) ||1532 "REVERSE_OFFLOAD" >>1533 construct<OmpClause>(construct<OmpClause::ReverseOffload>(1534 maybe(parenthesized(scalarLogicalConstantExpr)))) ||1535 "SAFELEN" >> construct<OmpClause>(construct<OmpClause::Safelen>(1536 parenthesized(scalarIntConstantExpr))) ||1537 "SCHEDULE" >> construct<OmpClause>(construct<OmpClause::Schedule>(1538 parenthesized(Parser<OmpScheduleClause>{}))) ||1539 "SEQ_CST" >> construct<OmpClause>(construct<OmpClause::SeqCst>()) ||1540 "SELF_MAPS" >> construct<OmpClause>(construct<OmpClause::SelfMaps>(1541 maybe(parenthesized(scalarLogicalConstantExpr)))) ||1542 "SEVERITY" >> construct<OmpClause>(construct<OmpClause::Severity>(1543 parenthesized(Parser<OmpSeverityClause>{}))) ||1544 "SHARED" >> construct<OmpClause>(construct<OmpClause::Shared>(1545 parenthesized(Parser<OmpObjectList>{}))) ||1546 "SIMD"_id >> construct<OmpClause>(construct<OmpClause::Simd>()) ||1547 "SIMDLEN" >> construct<OmpClause>(construct<OmpClause::Simdlen>(1548 parenthesized(scalarIntConstantExpr))) ||1549 "SIZES" >> construct<OmpClause>(construct<OmpClause::Sizes>(1550 parenthesized(nonemptyList(scalarIntExpr)))) ||1551 "PERMUTATION" >> construct<OmpClause>(construct<OmpClause::Permutation>(1552 parenthesized(nonemptyList(scalarIntExpr)))) ||1553 "THREADS" >> construct<OmpClause>(construct<OmpClause::Threads>()) ||1554 "THREAD_LIMIT" >> construct<OmpClause>(construct<OmpClause::ThreadLimit>(1555 parenthesized(scalarIntExpr))) ||1556 "TO" >> construct<OmpClause>(construct<OmpClause::To>(1557 parenthesized(Parser<OmpToClause>{}))) ||1558 "TRANSPARENT" >>1559 construct<OmpClause>(construct<OmpClause::Transparent>(1560 maybe(parenthesized(Parser<OmpTransparentClause>{})))) ||1561 "USE" >> construct<OmpClause>(construct<OmpClause::Use>(1562 parenthesized(Parser<OmpObject>{}))) ||1563 "USE_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::UseDevicePtr>(1564 parenthesized(Parser<OmpObjectList>{}))) ||1565 "USE_DEVICE_ADDR" >>1566 construct<OmpClause>(construct<OmpClause::UseDeviceAddr>(1567 parenthesized(Parser<OmpObjectList>{}))) ||1568 "UNIFIED_ADDRESS" >>1569 construct<OmpClause>(construct<OmpClause::UnifiedAddress>(1570 maybe(parenthesized(scalarLogicalConstantExpr)))) ||1571 "UNIFIED_SHARED_MEMORY" >>1572 construct<OmpClause>(construct<OmpClause::UnifiedSharedMemory>(1573 maybe(parenthesized(scalarLogicalConstantExpr)))) ||1574 "UNIFORM" >> construct<OmpClause>(construct<OmpClause::Uniform>(1575 parenthesized(nonemptyList(name)))) ||1576 "UNTIED" >> construct<OmpClause>(construct<OmpClause::Untied>()) ||1577 "UPDATE" >> construct<OmpClause>(construct<OmpClause::Update>(1578 maybe(Parser<OmpUpdateClause>{}))) ||1579 "WHEN" >> construct<OmpClause>(construct<OmpClause::When>(1580 parenthesized(Parser<OmpWhenClause>{}))) ||1581 "WRITE" >> construct<OmpClause>(construct<OmpClause::Write>()) ||1582 // Cancellable constructs1583 construct<OmpClause>(construct<OmpClause::CancellationConstructType>(1584 Parser<OmpCancellationConstructTypeClause>{})))1585 1586// [Clause, [Clause], ...]1587TYPE_PARSER(sourced(construct<OmpClauseList>(1588 many(maybe(","_tok) >> sourced(Parser<OmpClause>{})))))1589 1590// 2.1 (variable | /common-block/ | array-sections)1591TYPE_PARSER(construct<OmpObjectList>(nonemptyList(Parser<OmpObject>{})))1592 1593// --- Parsers for directives and constructs --------------------------1594 1595static inline constexpr auto IsDirective(llvm::omp::Directive dir) {1596 return [dir](const OmpDirectiveName &name) -> bool { return dir == name.v; };1597}1598 1599static inline constexpr auto IsMemberOf(const DirectiveSet &dirs) {1600 return [&dirs](const OmpDirectiveName &name) -> bool {1601 return dirs.test(llvm::to_underlying(name.v));1602 };1603}1604 1605constexpr auto validEPC{//1606 predicated(executionPartConstruct, [](auto &epc) {1607 return !Unwrap<OpenMPMisplacedEndDirective>(epc) &&1608 !Unwrap<OpenMPMisplacedEndDirective>(epc);1609 })};1610 1611constexpr auto validBlock{many(validEPC)};1612 1613TYPE_PARSER(sourced(construct<OmpDirectiveName>(OmpDirectiveNameParser{})))1614 1615OmpDirectiveSpecification static makeFlushFromOldSyntax(Verbatim &&text,1616 std::optional<OmpClauseList> &&clauses,1617 std::optional<OmpArgumentList> &&args,1618 OmpDirectiveSpecification::Flags &&flags) {1619 return OmpDirectiveSpecification{OmpDirectiveName(text), std::move(args),1620 std::move(clauses), std::move(flags)};1621}1622 1623TYPE_PARSER(1624 // Parse the old syntax: FLUSH [clauses] [(objects)]1625 sourced(construct<OmpDirectiveSpecification>(1626 // Force this old-syntax parser to fail for FLUSH followed by '('.1627 // Otherwise it could succeed on the new syntax but have one of1628 // lists absent in the parsed result.1629 // E.g. for FLUSH(x) SEQ_CST it would find no clauses following1630 // the directive name, parse the argument list "(x)" and stop.1631 applyFunction<OmpDirectiveSpecification>(makeFlushFromOldSyntax,1632 verbatim("FLUSH"_tok) / !lookAhead("("_tok),1633 maybe(Parser<OmpClauseList>{}),1634 maybe(parenthesized(1635 OmpArgumentListParser<llvm::omp::Directive::OMPD_flush>{})),1636 pure(OmpDirectiveSpecification::Flags(1637 {OmpDirectiveSpecification::Flag::DeprecatedSyntax}))))) ||1638 // Parse DECLARE_VARIANT individually, because the "[base:]variant"1639 // argument will conflict with DECLARE_REDUCTION's "ident:types...".1640 predicated(Parser<OmpDirectiveName>{},1641 IsDirective(llvm::omp::Directive::OMPD_declare_variant)) >=1642 sourced(construct<OmpDirectiveSpecification>(1643 sourced(OmpDirectiveNameParser{}),1644 maybe(parenthesized(OmpArgumentListParser<1645 llvm::omp::Directive::OMPD_declare_variant>{})),1646 maybe(Parser<OmpClauseList>{}),1647 pure(OmpDirectiveSpecification::Flags()))) ||1648 // Parse the standard syntax: directive [(arguments)] [clauses]1649 sourced(construct<OmpDirectiveSpecification>( //1650 sourced(OmpDirectiveNameParser{}),1651 maybe(parenthesized(OmpArgumentListParser<>{})),1652 maybe(Parser<OmpClauseList>{}),1653 pure(OmpDirectiveSpecification::Flags()))))1654 1655static bool IsStandaloneOrdered(const OmpDirectiveSpecification &dirSpec) {1656 // An ORDERED construct is standalone if it has DOACROSS or DEPEND clause.1657 return dirSpec.DirId() == llvm::omp::Directive::OMPD_ordered &&1658 llvm::any_of(dirSpec.Clauses().v, [](const OmpClause &clause) {1659 llvm::omp::Clause id{clause.Id()};1660 return id == llvm::omp::Clause::OMPC_depend ||1661 id == llvm::omp::Clause::OMPC_doacross;1662 });1663}1664 1665struct StrictlyStructuredBlockParser {1666 using resultType = Block;1667 1668 std::optional<resultType> Parse(ParseState &state) const {1669 // Detect BLOCK construct without parsing the entire thing.1670 if (lookAhead(skipStuffBeforeStatement >> "BLOCK"_tok).Parse(state)) {1671 if (auto &&epc{executionPartConstruct.Parse(state)}) {1672 if (GetFortranBlockConstruct(*epc) != nullptr) {1673 Block body;1674 body.emplace_back(std::move(*epc));1675 return std::move(body);1676 }1677 }1678 }1679 return std::nullopt;1680 }1681};1682 1683struct LooselyStructuredBlockParser {1684 using resultType = Block;1685 1686 std::optional<resultType> Parse(ParseState &state) const {1687 // Detect BLOCK construct without parsing the entire thing.1688 if (lookAhead(skipStuffBeforeStatement >> "BLOCK"_tok).Parse(state)) {1689 return std::nullopt;1690 }1691 if (auto &&body{validBlock.Parse(state)}) {1692 // Empty body is ok.1693 return std::move(body);1694 }1695 return std::nullopt;1696 }1697};1698 1699struct NonBlockDoConstructParser {1700 using resultType = Block;1701 1702 std::optional<resultType> Parse(ParseState &state) const {1703 std::set<Label> labels;1704 Block body;1705 1706 // Parse nests like1707 // do 20 i = 1, n LabelDoStmt.t<Label> = 201708 // do 10 j = 1, m1709 // ...1710 // 10 continue Statement<...>.label = 101711 // 20 continue1712 1713 // Keep parsing ExecutionPartConstructs until the set of open label-do1714 // statements becomes empty, or until the EPC parser fails.1715 auto processEpc{[&](ExecutionPartConstruct &&epc) {1716 if (auto &&label{GetStatementLabel(epc)}) {1717 labels.erase(*label);1718 }1719 if (auto *labelDo{Unwrap<LabelDoStmt>(epc)}) {1720 labels.insert(std::get<Label>(labelDo->t));1721 }1722 body.push_back(std::move(epc));1723 }};1724 1725 auto nonBlockDo{predicated(executionPartConstruct,1726 [](auto &epc) { return Unwrap<LabelDoStmt>(epc); })};1727 1728 if (auto &&nbd{nonBlockDo.Parse(state)}) {1729 processEpc(std::move(*nbd));1730 while (auto &&epc{attempt(validEPC).Parse(state)}) {1731 processEpc(std::move(*epc));1732 if (labels.empty()) {1733 break;1734 }1735 }1736 }1737 1738 if (!body.empty()) {1739 return std::move(body);1740 }1741 return std::nullopt;1742 }1743};1744 1745struct LoopNestParser {1746 using resultType = Block;1747 1748 std::optional<resultType> Parse(ParseState &state) const {1749 // Parse !$DIR as an ExecutionPartConstruct1750 auto fortranDirective{predicated(executionPartConstruct,1751 [](auto &epc) { return Unwrap<CompilerDirective>(epc); })};1752 // Parse DO loop as an ExecutionPartConstruct1753 auto fortranDoConstruct{predicated(executionPartConstruct,1754 [&](auto &epc) { return Unwrap<DoConstruct>(epc); })};1755 ParseState backtrack{state};1756 1757 Block body;1758 llvm::move(*many(fortranDirective).Parse(state), std::back_inserter(body));1759 1760 if (auto &&doLoop{attempt(fortranDoConstruct).Parse(state)}) {1761 body.push_back(std::move(*doLoop));1762 return std::move(body);1763 }1764 if (auto &&labelDo{attempt(NonBlockDoConstructParser{}).Parse(state)}) {1765 llvm::move(*labelDo, std::back_inserter(body));1766 return std::move(body);1767 }1768 if (auto &&sblock{attempt(StrictlyStructuredBlockParser{}).Parse(state)}) {1769 llvm::move(*sblock, std::back_inserter(body));1770 return std::move(body);1771 }1772 // If it's neither a DO-loop, nor a BLOCK, undo the parsing of the1773 // directives and fail.1774 state = backtrack;1775 return std::nullopt;1776 }1777};1778 1779TYPE_PARSER(construct<OmpErrorDirective>(1780 predicated(Parser<OmpDirectiveName>{},1781 IsDirective(llvm::omp::Directive::OMPD_error)) >=1782 Parser<OmpDirectiveSpecification>{}))1783 1784TYPE_PARSER(construct<OmpNothingDirective>(1785 predicated(Parser<OmpDirectiveName>{},1786 IsDirective(llvm::omp::Directive::OMPD_nothing)) >=1787 Parser<OmpDirectiveSpecification>{}))1788 1789TYPE_PARSER( //1790 sourced(construct<OpenMPUtilityConstruct>(Parser<OmpErrorDirective>{})) ||1791 sourced(construct<OpenMPUtilityConstruct>(Parser<OmpNothingDirective>{})))1792 1793TYPE_PARSER(construct<OmpMetadirectiveDirective>(1794 predicated(Parser<OmpDirectiveName>{},1795 IsDirective(llvm::omp::Directive::OMPD_metadirective)) >=1796 Parser<OmpDirectiveSpecification>{}))1797 1798struct OmpBeginDirectiveParser {1799 using resultType = OmpDirectiveSpecification;1800 1801 constexpr OmpBeginDirectiveParser(DirectiveSet dirs) : dirs_(dirs) {}1802 constexpr OmpBeginDirectiveParser(llvm::omp::Directive dir) {1803 dirs_.set(llvm::to_underlying(dir));1804 }1805 1806 std::optional<resultType> Parse(ParseState &state) const {1807 auto &&p{predicated(Parser<OmpDirectiveName>{}, IsMemberOf(dirs_)) >=1808 Parser<OmpDirectiveSpecification>{}};1809 return p.Parse(state);1810 }1811 1812private:1813 DirectiveSet dirs_;1814};1815 1816struct OmpEndDirectiveParser {1817 using resultType = OmpDirectiveSpecification;1818 1819 constexpr OmpEndDirectiveParser(DirectiveSet dirs) : dirs_(dirs) {}1820 constexpr OmpEndDirectiveParser(llvm::omp::Directive dir) {1821 dirs_.set(llvm::to_underlying(dir));1822 }1823 1824 std::optional<resultType> Parse(ParseState &state) const {1825 if (startOmpLine.Parse(state)) {1826 if (auto endToken{verbatim("END"_sptok).Parse(state)}) {1827 if (auto &&dirSpec{OmpBeginDirectiveParser(dirs_).Parse(state)}) {1828 // Extend the "source" on both the OmpDirectiveName and the1829 // OmpDirectiveNameSpecification.1830 CharBlock &nameSource{std::get<OmpDirectiveName>(dirSpec->t).source};1831 nameSource.ExtendToCover(endToken->source);1832 dirSpec->source.ExtendToCover(endToken->source);1833 return std::move(*dirSpec);1834 }1835 }1836 }1837 return std::nullopt;1838 }1839 1840private:1841 DirectiveSet dirs_;1842};1843 1844struct OmpStatementConstructParser {1845 using resultType = OmpBlockConstruct;1846 1847 constexpr OmpStatementConstructParser(llvm::omp::Directive dir) : dir_(dir) {}1848 1849 std::optional<resultType> Parse(ParseState &state) const {1850 if (auto begin{OmpBeginDirectiveParser(dir_).Parse(state)}) {1851 Block body;1852 if (auto stmt{attempt(validEPC).Parse(state)}) {1853 body.emplace_back(std::move(*stmt));1854 }1855 // Allow empty block. Check for this in semantics.1856 1857 auto end{maybe(OmpEndDirectiveParser{dir_}).Parse(state)};1858 return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)),1859 std::move(body),1860 llvm::transformOptional(std::move(*end),1861 [](auto &&s) { return OmpEndDirective(std::move(s)); })};1862 }1863 return std::nullopt;1864 }1865 1866private:1867 llvm::omp::Directive dir_;1868};1869 1870struct OmpBlockConstructParser {1871 using resultType = OmpBlockConstruct;1872 1873 constexpr OmpBlockConstructParser(llvm::omp::Directive dir) : dir_(dir) {}1874 1875 std::optional<resultType> Parse(ParseState &state) const {1876 if (auto &&begin{OmpBeginDirectiveParser(dir_).Parse(state)}) {1877 if (IsStandaloneOrdered(*begin)) {1878 return std::nullopt;1879 }1880 if (auto &&body{attempt(StrictlyStructuredBlockParser{}).Parse(state)}) {1881 // Try strictly-structured block with an optional end-directive1882 auto end{maybe(OmpEndDirectiveParser{dir_}).Parse(state)};1883 return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)),1884 std::move(*body),1885 llvm::transformOptional(std::move(*end),1886 [](auto &&s) { return OmpEndDirective(std::move(s)); })};1887 } else if (auto &&body{1888 attempt(LooselyStructuredBlockParser{}).Parse(state)}) {1889 // Try loosely-structured block with a mandatory end-directive.1890 auto end{maybe(OmpEndDirectiveParser{dir_}).Parse(state)};1891 // Delay the error for a missing end-directive until semantics so that1892 // we have better control over the output.1893 return OmpBlockConstruct{OmpBeginDirective(std::move(*begin)),1894 std::move(*body),1895 llvm::transformOptional(std::move(*end),1896 [](auto &&s) { return OmpEndDirective(std::move(s)); })};1897 }1898 }1899 return std::nullopt;1900 }1901 1902private:1903 llvm::omp::Directive dir_;1904};1905 1906struct OmpLoopConstructParser {1907 using resultType = OpenMPLoopConstruct;1908 1909 constexpr OmpLoopConstructParser(DirectiveSet dirs) : dirs_(dirs) {}1910 1911 std::optional<resultType> Parse(ParseState &state) const {1912 auto ompLoopConstruct{asBlock(predicated(executionPartConstruct,1913 [](auto &epc) { return Unwrap<OpenMPLoopConstruct>(epc); }))};1914 auto loopItem{LoopNestParser{} || ompLoopConstruct};1915 1916 if (auto &&begin{OmpBeginDirectiveParser(dirs_).Parse(state)}) {1917 auto loopDir{begin->DirName().v};1918 auto assoc{llvm::omp::getDirectiveAssociation(loopDir)};1919 if (assoc == llvm::omp::Association::LoopNest) {1920 if (auto &&item{attempt(loopItem).Parse(state)}) {1921 auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};1922 return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),1923 std::move(*item),1924 llvm::transformOptional(std::move(*end),1925 [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};1926 } else if (auto &&empty{pure<Block>().Parse(state)}) {1927 // Allow empty body.1928 auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};1929 return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),1930 std::move(*empty),1931 llvm::transformOptional(std::move(*end),1932 [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};1933 }1934 } else if (assoc == llvm::omp::Association::LoopSeq) {1935 // Parse loop sequence as a block.1936 if (auto &&body{validBlock.Parse(state)}) {1937 auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};1938 return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),1939 std::move(*body),1940 llvm::transformOptional(std::move(*end),1941 [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};1942 }1943 } else {1944 llvm_unreachable("Unexpected association");1945 }1946 }1947 return std::nullopt;1948 }1949 1950private:1951 DirectiveSet dirs_;1952};1953 1954struct OmpDeclarativeAllocateParser {1955 using resultType = OmpAllocateDirective;1956 1957 std::optional<resultType> Parse(ParseState &state) const {1958 constexpr llvm::omp::Directive dir{llvm::omp::Directive::OMPD_allocate};1959 if (auto &&begin{attempt(OmpBeginDirectiveParser(dir)).Parse(state)}) {1960 Block empty;1961 auto end{maybe(OmpEndDirectiveParser{dir}).Parse(state)};1962 return OmpAllocateDirective{std::move(*begin), std::move(empty),1963 llvm::transformOptional(std::move(*end),1964 [](auto &&s) { return OmpEndDirective(std::move(s)); })};1965 }1966 return std::nullopt;1967 }1968};1969 1970struct OmpExecutableAllocateParser {1971 using resultType = OmpAllocateDirective;1972 1973 std::optional<resultType> Parse(ParseState &state) const {1974 OmpStatementConstructParser p{llvm::omp::Directive::OMPD_allocate};1975 return construct<OmpAllocateDirective>(p).Parse(state);1976 }1977};1978 1979TYPE_PARSER(sourced(construct<OpenMPAllocatorsConstruct>(1980 OmpStatementConstructParser{llvm::omp::Directive::OMPD_allocators})))1981 1982TYPE_PARSER(sourced(construct<OpenMPDispatchConstruct>(1983 OmpStatementConstructParser{llvm::omp::Directive::OMPD_dispatch})))1984 1985// Parser for an arbitrary OpenMP ATOMIC construct.1986//1987// Depending on circumstances, an ATOMIC construct applies to one or more1988// following statements. In certain cases when a single statement is1989// expected, the end-directive is optional. The specifics depend on both1990// the clauses used, and the form of the executable statement. To emit1991// more meaningful messages in case of errors, the exact analysis of the1992// structure of the construct will be delayed until semantic checks.1993//1994// The parser will first try the case when the end-directive is present,1995// and will parse at most "BodyLimit" (and potentially zero) constructs1996// while looking for the end-directive before it gives up.1997// Then it will assume that no end-directive is present, and will try to1998// parse a single executable construct as the body of the construct.1999//2000// The limit on the number of constructs is there to reduce the amount of2001// unnecessary parsing when the end-directive is absent. It's higher than2002// the maximum number of statements in any valid construct to accept cases2003// when extra statements are present by mistake.2004// A problem can occur when atomic constructs without end-directive follow2005// each other closely, e.g.2006// !$omp atomic write2007// x = v2008// !$omp atomic update2009// x = x + 12010// ...2011// The speculative parsing will become "recursive", and has the potential2012// to take a (practically) infinite amount of time given a sufficiently2013// large number of such constructs in a row. Since atomic constructs cannot2014// contain other OpenMP constructs, guarding against recursive calls to the2015// atomic construct parser solves the problem.2016struct OmpAtomicConstructParser {2017 using resultType = OpenMPAtomicConstruct;2018 2019 static constexpr size_t BodyLimit{5};2020 2021 std::optional<resultType> Parse(ParseState &state) const {2022 if (recursing_) {2023 return std::nullopt;2024 }2025 recursing_ = true;2026 2027 auto dirSpec{Parser<OmpDirectiveSpecification>{}.Parse(state)};2028 if (!dirSpec || dirSpec->DirId() != llvm::omp::Directive::OMPD_atomic) {2029 recursing_ = false;2030 return std::nullopt;2031 }2032 2033 TailType tail;2034 2035 if (ParseOne(tail, state)) {2036 if (!tail.first.empty()) {2037 if (auto &&rest{attempt(LimitedTailParser(BodyLimit)).Parse(state)}) {2038 for (auto &&s : rest->first) {2039 tail.first.emplace_back(std::move(s));2040 }2041 assert(!tail.second);2042 tail.second = std::move(rest->second);2043 }2044 }2045 recursing_ = false;2046 return OpenMPAtomicConstruct{OmpBeginDirective(std::move(*dirSpec)),2047 std::move(tail.first),2048 llvm::transformOptional(std::move(tail.second),2049 [](auto &&s) { return OmpEndDirective(std::move(s)); })};2050 }2051 2052 recursing_ = false;2053 return std::nullopt;2054 }2055 2056private:2057 // Begin-directive + TailType = entire construct.2058 using TailType = std::pair<Block, std::optional<OmpDirectiveSpecification>>;2059 2060 // Parse either an ExecutionPartConstruct, or atomic end-directive. When2061 // successful, record the result in the "tail" provided, otherwise fail.2062 static std::optional<Success> ParseOne(TailType &tail, ParseState &state) {2063 auto isUsable{[](const std::optional<ExecutionPartConstruct> &e) {2064 return e && !std::holds_alternative<ErrorRecovery>(e->u);2065 }};2066 auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};2067 if (auto &&stmt{attempt(validEPC).Parse(state)}; isUsable(stmt)) {2068 tail.first.emplace_back(std::move(*stmt));2069 } else if (auto &&dir{attempt(end).Parse(state)}) {2070 tail.second = std::move(*dir);2071 } else {2072 return std::nullopt;2073 }2074 return Success{};2075 }2076 2077 struct LimitedTailParser {2078 using resultType = TailType;2079 2080 constexpr LimitedTailParser(size_t count) : count_(count) {}2081 2082 std::optional<resultType> Parse(ParseState &state) const {2083 TailType tail;2084 2085 for (size_t i{0}; i != count_; ++i) {2086 if (ParseOne(tail, state)) {2087 if (tail.second) {2088 // Return when the end-directive was parsed.2089 return std::move(tail);2090 }2091 } else {2092 break;2093 }2094 }2095 return std::nullopt;2096 }2097 2098 private:2099 const size_t count_;2100 };2101 2102 // The recursion guard should become thread_local if parsing is ever2103 // parallelized.2104 static bool recursing_;2105};2106 2107bool OmpAtomicConstructParser::recursing_{false};2108 2109TYPE_PARSER(sourced( //2110 construct<OpenMPAtomicConstruct>(OmpAtomicConstructParser{})))2111 2112static bool IsSimpleStandalone(const OmpDirectiveName &name) {2113 switch (name.v) {2114 case llvm::omp::Directive::OMPD_barrier:2115 case llvm::omp::Directive::OMPD_scan:2116 case llvm::omp::Directive::OMPD_target_enter_data:2117 case llvm::omp::Directive::OMPD_target_exit_data:2118 case llvm::omp::Directive::OMPD_target_update:2119 case llvm::omp::Directive::OMPD_taskwait:2120 case llvm::omp::Directive::OMPD_taskyield:2121 return true;2122 default:2123 return false;2124 }2125}2126 2127TYPE_PARSER(sourced( //2128 construct<OpenMPSimpleStandaloneConstruct>(2129 predicated(OmpDirectiveNameParser{}, IsSimpleStandalone) >=2130 Parser<OmpDirectiveSpecification>{}) ||2131 construct<OpenMPSimpleStandaloneConstruct>(2132 predicated(Parser<OmpDirectiveSpecification>{}, IsStandaloneOrdered))))2133 2134TYPE_PARSER(sourced( //2135 construct<OpenMPFlushConstruct>(2136 predicated(OmpDirectiveNameParser{},2137 IsDirective(llvm::omp::Directive::OMPD_flush)) >=2138 Parser<OmpDirectiveSpecification>{})))2139 2140// 2.14.2 Cancellation Point construct2141TYPE_PARSER(sourced( //2142 construct<OpenMPCancellationPointConstruct>(2143 predicated(OmpDirectiveNameParser{},2144 IsDirective(llvm::omp::Directive::OMPD_cancellation_point)) >=2145 Parser<OmpDirectiveSpecification>{})))2146 2147// 2.14.1 Cancel construct2148TYPE_PARSER(sourced( //2149 construct<OpenMPCancelConstruct>(2150 predicated(OmpDirectiveNameParser{},2151 IsDirective(llvm::omp::Directive::OMPD_cancel)) >=2152 Parser<OmpDirectiveSpecification>{})))2153 2154TYPE_PARSER(sourced( //2155 construct<OpenMPDepobjConstruct>(2156 predicated(OmpDirectiveNameParser{},2157 IsDirective(llvm::omp::Directive::OMPD_depobj)) >=2158 Parser<OmpDirectiveSpecification>{})))2159 2160// OMP 5.2 14.1 Interop construct2161TYPE_PARSER(sourced( //2162 construct<OpenMPInteropConstruct>(2163 predicated(OmpDirectiveNameParser{},2164 IsDirective(llvm::omp::Directive::OMPD_interop)) >=2165 Parser<OmpDirectiveSpecification>{})))2166 2167// Standalone Constructs2168TYPE_PARSER(2169 sourced( //2170 construct<OpenMPStandaloneConstruct>(2171 Parser<OpenMPSimpleStandaloneConstruct>{}) ||2172 construct<OpenMPStandaloneConstruct>(Parser<OpenMPFlushConstruct>{}) ||2173 // Try CANCELLATION POINT before CANCEL.2174 construct<OpenMPStandaloneConstruct>(2175 Parser<OpenMPCancellationPointConstruct>{}) ||2176 construct<OpenMPStandaloneConstruct>(Parser<OpenMPCancelConstruct>{}) ||2177 construct<OpenMPStandaloneConstruct>(2178 Parser<OmpMetadirectiveDirective>{}) ||2179 construct<OpenMPStandaloneConstruct>(Parser<OpenMPDepobjConstruct>{}) ||2180 construct<OpenMPStandaloneConstruct>(2181 Parser<OpenMPInteropConstruct>{})) /2182 endOfLine)2183 2184TYPE_PARSER(construct<OmpInitializerClause>(Parser<OmpInitializerExpression>{}))2185 2186// OpenMP 5.2: 7.5.4 Declare Variant directive2187TYPE_PARSER(sourced(construct<OmpDeclareVariantDirective>(2188 predicated(Parser<OmpDirectiveName>{},2189 IsDirective(llvm::omp::Directive::OMPD_declare_variant)) >=2190 Parser<OmpDirectiveSpecification>{})))2191 2192// 2.16 Declare Reduction Construct2193TYPE_PARSER(sourced(construct<OpenMPDeclareReductionConstruct>(2194 predicated(Parser<OmpDirectiveName>{},2195 IsDirective(llvm::omp::Directive::OMPD_declare_reduction)) >=2196 OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{}))))2197 2198// 2.10.6 Declare Target Construct2199TYPE_PARSER(sourced(construct<OpenMPDeclareTargetConstruct>(2200 predicated(Parser<OmpDirectiveName>{},2201 IsDirective(llvm::omp::Directive::OMPD_declare_target)) >=2202 Parser<OmpDirectiveSpecification>{})))2203 2204static OmpMapperSpecifier ConstructOmpMapperSpecifier(2205 std::optional<Name> &&mapperName, TypeSpec &&typeSpec, Name &&varName) {2206 // If a name is present, parse: name ":" typeSpec "::" name2207 // This matches the syntax: <mapper-name> : <type-spec> :: <variable-name>2208 if (mapperName.has_value() && mapperName->ToString() != "default") {2209 return OmpMapperSpecifier{2210 mapperName->ToString(), std::move(typeSpec), std::move(varName)};2211 }2212 // If the name is missing, use the DerivedTypeSpec name to construct the2213 // default mapper name.2214 // This matches the syntax: <type-spec> :: <variable-name>2215 if (DerivedTypeSpec * derived{std::get_if<DerivedTypeSpec>(&typeSpec.u)}) {2216 return OmpMapperSpecifier{2217 std::get<Name>(derived->t).ToString() + llvm::omp::OmpDefaultMapperName,2218 std::move(typeSpec), std::move(varName)};2219 }2220 return OmpMapperSpecifier{std::string(llvm::omp::OmpDefaultMapperName),2221 std::move(typeSpec), std::move(varName)};2222}2223 2224// mapper-specifier2225TYPE_PARSER(applyFunction<OmpMapperSpecifier>(ConstructOmpMapperSpecifier,2226 maybe(name / ":" / !":"_tok), typeSpec / "::", name))2227 2228// OpenMP 5.2: 5.8.8 Declare Mapper Construct2229TYPE_PARSER(sourced(construct<OpenMPDeclareMapperConstruct>(2230 predicated(Parser<OmpDirectiveName>{},2231 IsDirective(llvm::omp::Directive::OMPD_declare_mapper)) >=2232 Parser<OmpDirectiveSpecification>{})))2233 2234TYPE_PARSER(construct<OmpCombinerExpression>(OmpStylizedExpressionParser{}))2235TYPE_PARSER(construct<OmpInitializerExpression>(OmpStylizedExpressionParser{}))2236 2237TYPE_PARSER(sourced(construct<OpenMPCriticalConstruct>(2238 OmpBlockConstructParser{llvm::omp::Directive::OMPD_critical})))2239 2240// 2.8.2 Declare Simd construct2241TYPE_PARSER(sourced(construct<OpenMPDeclareSimdConstruct>(2242 predicated(Parser<OmpDirectiveName>{},2243 IsDirective(llvm::omp::Directive::OMPD_declare_simd)) >=2244 Parser<OmpDirectiveSpecification>{})))2245 2246TYPE_PARSER(sourced( //2247 construct<OpenMPGroupprivate>(2248 predicated(OmpDirectiveNameParser{},2249 IsDirective(llvm::omp::Directive::OMPD_groupprivate)) >=2250 Parser<OmpDirectiveSpecification>{})))2251 2252// 2.4 Requires construct2253TYPE_PARSER(sourced(construct<OpenMPRequiresConstruct>(2254 predicated(OmpDirectiveNameParser{},2255 IsDirective(llvm::omp::Directive::OMPD_requires)) >=2256 Parser<OmpDirectiveSpecification>{})))2257 2258// 2.15.2 Threadprivate directive2259TYPE_PARSER(sourced( //2260 construct<OpenMPThreadprivate>(2261 predicated(OmpDirectiveNameParser{},2262 IsDirective(llvm::omp::Directive::OMPD_threadprivate)) >=2263 Parser<OmpDirectiveSpecification>{})))2264 2265// Assumes Construct2266TYPE_PARSER(sourced(construct<OpenMPDeclarativeAssumes>(2267 predicated(OmpDirectiveNameParser{},2268 IsDirective(llvm::omp::Directive::OMPD_assumes)) >=2269 Parser<OmpDirectiveSpecification>{})))2270 2271// Declarative constructs2272TYPE_PARSER(2273 startOmpLine >> withMessage("expected OpenMP construct"_err_en_US,2274 sourced(construct<OpenMPDeclarativeConstruct>(2275 Parser<OpenMPDeclarativeAssumes>{}) ||2276 construct<OpenMPDeclarativeConstruct>(2277 Parser<OpenMPDeclareReductionConstruct>{}) ||2278 construct<OpenMPDeclarativeConstruct>(2279 Parser<OpenMPDeclareMapperConstruct>{}) ||2280 construct<OpenMPDeclarativeConstruct>(2281 Parser<OpenMPDeclareSimdConstruct>{}) ||2282 construct<OpenMPDeclarativeConstruct>(2283 Parser<OpenMPDeclareTargetConstruct>{}) ||2284 construct<OpenMPDeclarativeConstruct>(2285 Parser<OmpDeclareVariantDirective>{}) ||2286 construct<OpenMPDeclarativeConstruct>(2287 sourced(OmpDeclarativeAllocateParser{})) ||2288 construct<OpenMPDeclarativeConstruct>(2289 Parser<OpenMPGroupprivate>{}) ||2290 construct<OpenMPDeclarativeConstruct>(2291 Parser<OpenMPRequiresConstruct>{}) ||2292 construct<OpenMPDeclarativeConstruct>(2293 Parser<OpenMPThreadprivate>{}) ||2294 construct<OpenMPDeclarativeConstruct>(2295 Parser<OpenMPUtilityConstruct>{}) ||2296 construct<OpenMPDeclarativeConstruct>(2297 Parser<OmpMetadirectiveDirective>{})) /2298 endOmpLine))2299 2300TYPE_PARSER(sourced(construct<OpenMPAssumeConstruct>(2301 OmpBlockConstructParser{llvm::omp::Directive::OMPD_assume})))2302 2303// Block Construct2304#define MakeBlockConstruct(dir) \2305 sourced(construct<OmpBlockConstruct>(OmpBlockConstructParser{dir}))2306TYPE_PARSER( //2307 MakeBlockConstruct(llvm::omp::Directive::OMPD_masked) ||2308 MakeBlockConstruct(llvm::omp::Directive::OMPD_master) ||2309 MakeBlockConstruct(llvm::omp::Directive::OMPD_ordered) ||2310 MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel_masked) ||2311 MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel_master) ||2312 MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel_workshare) ||2313 MakeBlockConstruct(llvm::omp::Directive::OMPD_parallel) ||2314 MakeBlockConstruct(llvm::omp::Directive::OMPD_scope) ||2315 MakeBlockConstruct(llvm::omp::Directive::OMPD_single) ||2316 MakeBlockConstruct(llvm::omp::Directive::OMPD_target_data) ||2317 MakeBlockConstruct(llvm::omp::Directive::OMPD_target_parallel) ||2318 MakeBlockConstruct(llvm::omp::Directive::OMPD_target_teams) ||2319 MakeBlockConstruct(2320 llvm::omp::Directive::OMPD_target_teams_workdistribute) ||2321 MakeBlockConstruct(llvm::omp::Directive::OMPD_target) ||2322 MakeBlockConstruct(llvm::omp::Directive::OMPD_task) ||2323 MakeBlockConstruct(llvm::omp::Directive::OMPD_taskgraph) ||2324 MakeBlockConstruct(llvm::omp::Directive::OMPD_taskgroup) ||2325 MakeBlockConstruct(llvm::omp::Directive::OMPD_teams) ||2326 MakeBlockConstruct(llvm::omp::Directive::OMPD_teams_workdistribute) ||2327 MakeBlockConstruct(llvm::omp::Directive::OMPD_workshare) ||2328 MakeBlockConstruct(llvm::omp::Directive::OMPD_workdistribute))2329#undef MakeBlockConstruct2330 2331// OMP SECTIONS Directive2332static constexpr DirectiveSet GetSectionsDirectives() {2333 using Directive = llvm::omp::Directive;2334 constexpr DirectiveSet sectionsDirectives{2335 unsigned(Directive::OMPD_sections),2336 unsigned(Directive::OMPD_parallel_sections),2337 };2338 return sectionsDirectives;2339}2340 2341// OMP BEGIN and END SECTIONS Directive2342TYPE_PARSER(construct<OmpBeginSectionsDirective>(2343 OmpBeginDirectiveParser(GetSectionsDirectives())))2344 2345TYPE_PARSER(construct<OmpEndSectionsDirective>(2346 OmpEndDirectiveParser(GetSectionsDirectives())))2347 2348static constexpr auto sectionDir{2349 startOmpLine >> (predicated(OmpDirectiveNameParser{},2350 IsDirective(llvm::omp::Directive::OMPD_section)) >=2351 Parser<OmpDirectiveSpecification>{})};2352 2353// OMP SECTIONS (OpenMP 5.0 - 2.8.1), PARALLEL SECTIONS (OpenMP 5.0 - 2.13.3)2354TYPE_PARSER(sourced(construct<OpenMPSectionsConstruct>(2355 Parser<OmpBeginSectionsDirective>{} / endOmpLine,2356 cons( //2357 construct<OpenMPConstruct>(sourced(2358 construct<OpenMPSectionConstruct>(maybe(sectionDir), validBlock))),2359 many(construct<OpenMPConstruct>(sourced(2360 construct<OpenMPSectionConstruct>(sectionDir, validBlock))))),2361 maybe(Parser<OmpEndSectionsDirective>{} / endOmpLine))))2362 2363static bool IsExecutionPart(const OmpDirectiveName &name) {2364 return name.IsExecutionPart();2365}2366 2367TYPE_PARSER(construct<OpenMPExecDirective>(2368 startOmpLine >> predicated(Parser<OmpDirectiveName>{}, IsExecutionPart)))2369 2370TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,2371 startOmpLine >>2372 withMessage("expected OpenMP construct"_err_en_US,2373 first(construct<OpenMPConstruct>(Parser<OpenMPSectionsConstruct>{}),2374 construct<OpenMPConstruct>(Parser<OpenMPLoopConstruct>{}),2375 construct<OpenMPConstruct>(2376 sourced(OmpExecutableAllocateParser{})),2377 construct<OpenMPConstruct>(Parser<OmpBlockConstruct>{}),2378 // OmpBlockConstruct is attempted before2379 // OpenMPStandaloneConstruct to resolve !$OMP ORDERED2380 construct<OpenMPConstruct>(Parser<OpenMPStandaloneConstruct>{}),2381 construct<OpenMPConstruct>(Parser<OpenMPAtomicConstruct>{}),2382 construct<OpenMPConstruct>(Parser<OpenMPUtilityConstruct>{}),2383 construct<OpenMPConstruct>(Parser<OpenMPDispatchConstruct>{}),2384 construct<OpenMPConstruct>(Parser<OpenMPAllocatorsConstruct>{}),2385 construct<OpenMPConstruct>(Parser<OpenMPAssumeConstruct>{}),2386 construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}))))2387 2388static constexpr DirectiveSet GetLoopDirectives() {2389 using Directive = llvm::omp::Directive;2390 constexpr DirectiveSet loopDirectives{2391 unsigned(Directive::OMPD_distribute),2392 unsigned(Directive::OMPD_distribute_parallel_do),2393 unsigned(Directive::OMPD_distribute_parallel_do_simd),2394 unsigned(Directive::OMPD_distribute_simd),2395 unsigned(Directive::OMPD_do),2396 unsigned(Directive::OMPD_do_simd),2397 unsigned(Directive::OMPD_loop),2398 unsigned(Directive::OMPD_masked_taskloop),2399 unsigned(Directive::OMPD_masked_taskloop_simd),2400 unsigned(Directive::OMPD_master_taskloop),2401 unsigned(Directive::OMPD_master_taskloop_simd),2402 unsigned(Directive::OMPD_parallel_do),2403 unsigned(Directive::OMPD_parallel_do_simd),2404 unsigned(Directive::OMPD_parallel_masked_taskloop),2405 unsigned(Directive::OMPD_parallel_masked_taskloop_simd),2406 unsigned(Directive::OMPD_parallel_master_taskloop),2407 unsigned(Directive::OMPD_parallel_master_taskloop_simd),2408 unsigned(Directive::OMPD_simd),2409 unsigned(Directive::OMPD_target_loop),2410 unsigned(Directive::OMPD_target_parallel_do),2411 unsigned(Directive::OMPD_target_parallel_do_simd),2412 unsigned(Directive::OMPD_target_parallel_loop),2413 unsigned(Directive::OMPD_target_simd),2414 unsigned(Directive::OMPD_target_teams_distribute),2415 unsigned(Directive::OMPD_target_teams_distribute_parallel_do),2416 unsigned(Directive::OMPD_target_teams_distribute_parallel_do_simd),2417 unsigned(Directive::OMPD_target_teams_distribute_simd),2418 unsigned(Directive::OMPD_target_teams_loop),2419 unsigned(Directive::OMPD_taskloop),2420 unsigned(Directive::OMPD_taskloop_simd),2421 unsigned(Directive::OMPD_teams_distribute),2422 unsigned(Directive::OMPD_teams_distribute_parallel_do),2423 unsigned(Directive::OMPD_teams_distribute_parallel_do_simd),2424 unsigned(Directive::OMPD_teams_distribute_simd),2425 unsigned(Directive::OMPD_teams_loop),2426 unsigned(Directive::OMPD_fuse),2427 unsigned(Directive::OMPD_tile),2428 unsigned(Directive::OMPD_unroll),2429 };2430 return loopDirectives;2431}2432 2433TYPE_PARSER(sourced(construct<OpenMPLoopConstruct>(2434 OmpLoopConstructParser(GetLoopDirectives()))))2435 2436static constexpr DirectiveSet GetAllDirectives() { //2437 return ~DirectiveSet{};2438}2439 2440TYPE_PARSER(construct<OpenMPMisplacedEndDirective>(2441 OmpEndDirectiveParser{GetAllDirectives()}))2442 2443TYPE_PARSER( //2444 startOmpLine >> sourced(construct<OpenMPInvalidDirective>(2445 !OmpDirectiveNameParser{} >> SkipTo<'\n'>{})))2446} // namespace Fortran::parser2447