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-exception
6//
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/parse-tree.h"
19#include "llvm/ADT/ArrayRef.h"
20#include "llvm/ADT/STLExtras.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/Frontend/OpenMP/OMP.h"
23
24// OpenMP Directives and Clauses
25namespace Fortran::parser {
26
27// Helper function to print the buffer contents starting at the current point.
28[[maybe_unused]] static std::string ahead(const ParseState &state) {
29 return std::string(
30 state.GetLocation(), std::min<size_t>(64, state.BytesRemaining()));
31}
32
33constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok;
34constexpr auto endOmpLine = space >> endOfLine;
35
36// Given a parser P for a wrapper class, invoke P, and if it succeeds return
37// the wrapped object.
38template <typename Parser> struct UnwrapParser {
39 static_assert(
40 Parser::resultType::WrapperTrait::value && "Wrapper class required");
41 using resultType = decltype(Parser::resultType::v);
42 constexpr UnwrapParser(Parser p) : parser_(p) {}
43
44 std::optional<resultType> Parse(ParseState &state) const {
45 if (auto result{parser_.Parse(state)}) {
46 return result->v;
47 }
48 return std::nullopt;
49 }
50
51private:
52 const Parser parser_;
53};
54
55template <typename Parser> constexpr auto unwrap(const Parser &p) {
56 return UnwrapParser<Parser>(p);
57}
58
59// Check (without advancing the parsing location) if the next thing in the
60// input would be accepted by the "checked" parser, and if so, run the "parser"
61// parser.
62// The intended use is with the "checker" parser being some token, followed
63// by a more complex parser that consumes the token plus more things, e.g.
64// "PARALLEL"_id >= Parser<OmpDirectiveSpecification>{}.
65//
66// The >= has a higher precedence than ||, so it can be used just like >>
67// in an alternatives parser without parentheses.
68template <typename PA, typename PB>
69constexpr auto operator>=(PA checker, PB parser) {
70 return lookAhead(checker) >> parser;
71}
72
73// This parser succeeds if the given parser succeeds, and the result
74// satisfies the given condition. Specifically, it succeeds if:
75// 1. The parser given as the argument succeeds, and
76// 2. The condition function (called with PA::resultType) returns true
77// for the result.
78template <typename PA, typename CF> struct PredicatedParser {
79 using resultType = typename PA::resultType;
80
81 constexpr PredicatedParser(PA parser, CF condition)
82 : parser_(parser), condition_(condition) {}
83
84 std::optional<resultType> Parse(ParseState &state) const {
85 if (auto result{parser_.Parse(state)}; result && condition_(*result)) {
86 return result;
87 }
88 return std::nullopt;
89 }
90
91private:
92 const PA parser_;
93 const CF condition_;
94};
95
96template <typename PA, typename CF>
97constexpr auto predicated(PA parser, CF condition) {
98 return PredicatedParser(parser, condition);
99}
100
101/// Parse OpenMP directive name (this includes compound directives).
102struct OmpDirectiveNameParser {
103 using resultType = OmpDirectiveName;
104 using Token = TokenStringMatch<false, false>;
105
106 std::optional<resultType> Parse(ParseState &state) const {
107 auto begin{state.GetLocation()};
108 for (const NameWithId &nid : directives()) {
109 if (attempt(Token(nid.first.data())).Parse(state)) {
110 OmpDirectiveName n;
111 n.v = nid.second;
112 n.source = parser::CharBlock(begin, state.GetLocation());
113 return n;
114 }
115 }
116 return std::nullopt;
117 }
118
119private:
120 using NameWithId = std::pair<std::string, llvm::omp::Directive>;
121
122 llvm::iterator_range<const NameWithId *> directives() const;
123 void initTokens(NameWithId *) const;
124};
125
126llvm::iterator_range<const OmpDirectiveNameParser::NameWithId *>
127OmpDirectiveNameParser::directives() const {
128 static NameWithId table[llvm::omp::Directive_enumSize];
129 [[maybe_unused]] static bool init = (initTokens(table), true);
130 return llvm::make_range(std::cbegin(table), std::cend(table));
131}
132
133void OmpDirectiveNameParser::initTokens(NameWithId *table) const {
134 for (size_t i{0}, e{llvm::omp::Directive_enumSize}; i != e; ++i) {
135 auto id{static_cast<llvm::omp::Directive>(i)};
136 llvm::StringRef name{
137 llvm::omp::getOpenMPDirectiveName(id, llvm::omp::FallbackVersion)};
138 table[i] = std::make_pair(x: name.str(), y&: id);
139 }
140 // Sort the table with respect to the directive name length in a descending
141 // order. This is to make sure that longer names are tried first, before
142 // any potential prefix (e.g. "target update" before "target").
143 std::sort(table, table + llvm::omp::Directive_enumSize,
144 [](auto &a, auto &b) { return a.first.size() > b.first.size(); });
145}
146
147// --- Modifier helpers -----------------------------------------------
148
149template <typename Clause, typename Separator> struct ModifierList {
150 constexpr ModifierList(Separator sep) : sep_(sep) {}
151 constexpr ModifierList(const ModifierList &) = default;
152 constexpr ModifierList(ModifierList &&) = default;
153
154 using resultType = std::list<typename Clause::Modifier>;
155
156 std::optional<resultType> Parse(ParseState &state) const {
157 auto listp{nonemptySeparated(Parser<typename Clause::Modifier>{}, sep_)};
158 if (auto result{attempt(listp).Parse(state)}) {
159 if (!attempt(":"_tok).Parse(state)) {
160 return std::nullopt;
161 }
162 return std::move(result);
163 }
164 return resultType{};
165 }
166
167private:
168 const Separator sep_;
169};
170
171// Use a function to create ModifierList because functions allow "partial"
172// template argument deduction: "modifierList<Clause>(sep)" would be legal,
173// while "ModifierList<Clause>(sep)" would complain about a missing template
174// argument "Separator".
175template <typename Clause, typename Separator>
176constexpr ModifierList<Clause, Separator> modifierList(Separator sep) {
177 return ModifierList<Clause, Separator>(sep);
178}
179
180// Parse the input as any modifier from ClauseTy, but only succeed if
181// the result was the SpecificTy. It requires that SpecificTy is one
182// of the alternatives in ClauseTy::Modifier.
183// The reason to have this is that ClauseTy::Modifier has "source",
184// while specific modifiers don't. This class allows to parse a specific
185// modifier together with obtaining its location.
186template <typename SpecificTy, typename ClauseTy>
187struct SpecificModifierParser {
188 using resultType = typename ClauseTy::Modifier;
189 std::optional<resultType> Parse(ParseState &state) const {
190 if (auto result{attempt(Parser<resultType>{}).Parse(state)}) {
191 if (std::holds_alternative<SpecificTy>(result->u)) {
192 return result;
193 }
194 }
195 return std::nullopt;
196 }
197};
198
199// --- Iterator helpers -----------------------------------------------
200
201// [5.0:47:17-18] In an iterator-specifier, if the iterator-type is not
202// specified then the type of that iterator is default integer.
203// [5.0:49:14] The iterator-type must be an integer type.
204static std::list<EntityDecl> makeEntityList(std::list<ObjectName> &&names) {
205 std::list<EntityDecl> entities;
206
207 for (auto iter = names.begin(), end = names.end(); iter != end; ++iter) {
208 EntityDecl entityDecl(
209 /*ObjectName=*/std::move(*iter), std::optional<ArraySpec>{},
210 std::optional<CoarraySpec>{}, std::optional<CharLength>{},
211 std::optional<Initialization>{});
212 entities.push_back(std::move(entityDecl));
213 }
214 return entities;
215}
216
217static TypeDeclarationStmt makeIterSpecDecl(
218 DeclarationTypeSpec &&spec, std::list<ObjectName> &&names) {
219 return TypeDeclarationStmt(
220 std::move(spec), std::list<AttrSpec>{}, makeEntityList(std::move(names)));
221}
222
223static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {
224 // Assume INTEGER without kind selector.
225 DeclarationTypeSpec typeSpec(
226 IntrinsicTypeSpec{IntegerTypeSpec{std::nullopt}});
227
228 return TypeDeclarationStmt(std::move(typeSpec), std::list<AttrSpec>{},
229 makeEntityList(std::move(names)));
230}
231
232// --- Parsers for arguments ------------------------------------------
233
234// At the moment these are only directive arguments. This is needed for
235// parsing directive-specification.
236
237TYPE_PARSER( //
238 construct<OmpLocator>(Parser<OmpObject>{}) ||
239 construct<OmpLocator>(Parser<FunctionReference>{}))
240
241TYPE_PARSER(sourced( //
242 construct<OmpArgument>(Parser<OmpMapperSpecifier>{}) ||
243 construct<OmpArgument>(Parser<OmpReductionSpecifier>{}) ||
244 construct<OmpArgument>(Parser<OmpLocator>{})))
245
246TYPE_PARSER(construct<OmpLocatorList>(nonemptyList(Parser<OmpLocator>{})))
247
248TYPE_PARSER(sourced( //
249 construct<OmpArgumentList>(nonemptyList(Parser<OmpArgument>{}))))
250
251TYPE_PARSER( //
252 construct<OmpTypeSpecifier>(Parser<DeclarationTypeSpec>{}) ||
253 construct<OmpTypeSpecifier>(Parser<TypeSpec>{}))
254
255TYPE_PARSER(construct<OmpReductionSpecifier>( //
256 Parser<OmpReductionIdentifier>{},
257 ":"_tok >> nonemptyList(Parser<OmpTypeSpecifier>{}),
258 maybe(":"_tok >> Parser<OmpReductionCombiner>{})))
259
260// --- Parsers for context traits -------------------------------------
261
262static std::string nameToString(Name &&name) { return name.ToString(); }
263
264TYPE_PARSER(sourced(construct<OmpTraitPropertyName>( //
265 construct<OmpTraitPropertyName>(space >> charLiteralConstantWithoutKind) ||
266 construct<OmpTraitPropertyName>(
267 applyFunction(nameToString, Parser<Name>{})))))
268
269TYPE_PARSER(sourced(construct<OmpTraitScore>( //
270 "SCORE"_id >> parenthesized(scalarIntExpr))))
271
272TYPE_PARSER(sourced(construct<OmpTraitPropertyExtension::Complex>(
273 Parser<OmpTraitPropertyName>{},
274 parenthesized(nonemptySeparated(
275 indirect(Parser<OmpTraitPropertyExtension>{}), ",")))))
276
277TYPE_PARSER(sourced(construct<OmpTraitPropertyExtension>(
278 construct<OmpTraitPropertyExtension>(
279 Parser<OmpTraitPropertyExtension::Complex>{}) ||
280 construct<OmpTraitPropertyExtension>(Parser<OmpTraitPropertyName>{}) ||
281 construct<OmpTraitPropertyExtension>(scalarExpr))))
282
283TYPE_PARSER(construct<OmpTraitSelectorName::Value>(
284 "ARCH"_id >> pure(OmpTraitSelectorName::Value::Arch) ||
285 "ATOMIC_DEFAULT_MEM_ORDER"_id >>
286 pure(OmpTraitSelectorName::Value::Atomic_Default_Mem_Order) ||
287 "CONDITION"_id >> pure(OmpTraitSelectorName::Value::Condition) ||
288 "DEVICE_NUM"_id >> pure(OmpTraitSelectorName::Value::Device_Num) ||
289 "EXTENSION"_id >> pure(OmpTraitSelectorName::Value::Extension) ||
290 "ISA"_id >> pure(OmpTraitSelectorName::Value::Isa) ||
291 "KIND"_id >> pure(OmpTraitSelectorName::Value::Kind) ||
292 "REQUIRES"_id >> pure(OmpTraitSelectorName::Value::Requires) ||
293 "SIMD"_id >> pure(OmpTraitSelectorName::Value::Simd) ||
294 "UID"_id >> pure(OmpTraitSelectorName::Value::Uid) ||
295 "VENDOR"_id >> pure(OmpTraitSelectorName::Value::Vendor)))
296
297TYPE_PARSER(sourced(construct<OmpTraitSelectorName>(
298 // Parse predefined names first (because of SIMD).
299 construct<OmpTraitSelectorName>(Parser<OmpTraitSelectorName::Value>{}) ||
300 construct<OmpTraitSelectorName>(unwrap(OmpDirectiveNameParser{})) ||
301 // identifier-or-string for extensions
302 construct<OmpTraitSelectorName>(
303 applyFunction(nameToString, Parser<Name>{})) ||
304 construct<OmpTraitSelectorName>(space >> charLiteralConstantWithoutKind))))
305
306// Parser for OmpTraitSelector::Properties
307template <typename... PropParser>
308static constexpr auto propertyListParser(PropParser... pp) {
309 // Parse the property list "(score(expr): item1...)" in three steps:
310 // 1. Parse the "("
311 // 2. Parse the optional "score(expr):"
312 // 3. Parse the "item1, ...)", together with the ")".
313 // The reason for including the ")" in the 3rd step is to force parsing
314 // the entire list in each of the alternative property parsers. Otherwise,
315 // the name parser could stop after "foo" in "(foo, bar(1))", without
316 // allowing the next parser to give the list a try.
317 using P = OmpTraitProperty;
318 return maybe("(" >> //
319 construct<OmpTraitSelector::Properties>(
320 maybe(Parser<OmpTraitScore>{} / ":"),
321 (attempt(nonemptyList(sourced(construct<P>(pp))) / ")") || ...)));
322}
323
324// Parser for OmpTraitSelector
325struct TraitSelectorParser {
326 using resultType = OmpTraitSelector;
327
328 constexpr TraitSelectorParser(Parser<OmpTraitSelectorName> p) : np(p) {}
329
330 std::optional<resultType> Parse(ParseState &state) const {
331 auto name{attempt(np).Parse(state)};
332 if (!name.has_value()) {
333 return std::nullopt;
334 }
335
336 // Default fallback parser for lists that cannot be parser using the
337 // primary property parser.
338 auto extParser{Parser<OmpTraitPropertyExtension>{}};
339
340 if (auto *v{std::get_if<OmpTraitSelectorName::Value>(&name->u)}) {
341 // (*) The comments below show the sections of the OpenMP spec that
342 // describe given trait. The cases marked with a (*) are those where
343 // the spec doesn't assign any list-type to these traits, but for
344 // convenience they can be treated as if they were.
345 switch (*v) {
346 // name-list properties
347 case OmpTraitSelectorName::Value::Arch: // [6.0:319:18]
348 case OmpTraitSelectorName::Value::Extension: // [6.0:319:30]
349 case OmpTraitSelectorName::Value::Isa: // [6.0:319:15]
350 case OmpTraitSelectorName::Value::Kind: // [6.0:319:10]
351 case OmpTraitSelectorName::Value::Uid: // [6.0:319:23](*)
352 case OmpTraitSelectorName::Value::Vendor: { // [6.0:319:27]
353 auto pp{propertyListParser(Parser<OmpTraitPropertyName>{}, extParser)};
354 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
355 }
356 // clause-list
357 case OmpTraitSelectorName::Value::Atomic_Default_Mem_Order:
358 // [6.0:321:26-29](*)
359 case OmpTraitSelectorName::Value::Requires: // [6.0:319:33]
360 case OmpTraitSelectorName::Value::Simd: { // [6.0:318:31]
361 auto pp{propertyListParser(indirect(Parser<OmpClause>{}), extParser)};
362 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
363 }
364 // expr-list
365 case OmpTraitSelectorName::Value::Condition: // [6.0:321:33](*)
366 case OmpTraitSelectorName::Value::Device_Num: { // [6.0:321:23-24](*)
367 auto pp{propertyListParser(scalarExpr, extParser)};
368 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
369 }
370 } // switch
371 } else {
372 // The other alternatives are `llvm::omp::Directive`, and `std::string`.
373 // The former doesn't take any properties[1], the latter is a name of an
374 // extension[2].
375 // [1] [6.0:319:1-2]
376 // [2] [6.0:319:36-37]
377 auto pp{propertyListParser(extParser)};
378 return OmpTraitSelector(std::move(*name), std::move(*pp.Parse(state)));
379 }
380
381 llvm_unreachable("Unhandled trait name?");
382 }
383
384private:
385 const Parser<OmpTraitSelectorName> np;
386};
387
388TYPE_PARSER(sourced(construct<OmpTraitSelector>(
389 sourced(TraitSelectorParser(Parser<OmpTraitSelectorName>{})))))
390
391TYPE_PARSER(construct<OmpTraitSetSelectorName::Value>(
392 "CONSTRUCT"_id >> pure(OmpTraitSetSelectorName::Value::Construct) ||
393 "DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Device) ||
394 "IMPLEMENTATION"_id >>
395 pure(OmpTraitSetSelectorName::Value::Implementation) ||
396 "TARGET_DEVICE"_id >> pure(OmpTraitSetSelectorName::Value::Target_Device) ||
397 "USER"_id >> pure(OmpTraitSetSelectorName::Value::User)))
398
399TYPE_PARSER(sourced(construct<OmpTraitSetSelectorName>(
400 Parser<OmpTraitSetSelectorName::Value>{})))
401
402TYPE_PARSER(sourced(construct<OmpTraitSetSelector>( //
403 Parser<OmpTraitSetSelectorName>{},
404 "=" >> braced(nonemptySeparated(Parser<OmpTraitSelector>{}, ",")))))
405
406TYPE_PARSER(sourced(construct<OmpContextSelectorSpecification>(
407 nonemptySeparated(Parser<OmpTraitSetSelector>{}, ","))))
408
409// Note: OmpContextSelector is a type alias.
410
411// --- Parsers for clause modifiers -----------------------------------
412
413TYPE_PARSER(construct<OmpAlignment>(scalarIntExpr))
414
415TYPE_PARSER(construct<OmpAlignModifier>( //
416 "ALIGN" >> parenthesized(scalarIntExpr)))
417
418TYPE_PARSER(construct<OmpAllocatorComplexModifier>(
419 "ALLOCATOR" >> parenthesized(scalarIntExpr)))
420
421TYPE_PARSER(construct<OmpAllocatorSimpleModifier>(scalarIntExpr))
422
423TYPE_PARSER(construct<OmpChunkModifier>( //
424 "SIMD" >> pure(OmpChunkModifier::Value::Simd)))
425
426TYPE_PARSER(construct<OmpDependenceType>(
427 "SINK" >> pure(OmpDependenceType::Value::Sink) ||
428 "SOURCE" >> pure(OmpDependenceType::Value::Source)))
429
430TYPE_PARSER(construct<OmpDeviceModifier>(
431 "ANCESTOR" >> pure(OmpDeviceModifier::Value::Ancestor) ||
432 "DEVICE_NUM" >> pure(OmpDeviceModifier::Value::Device_Num)))
433
434TYPE_PARSER(construct<OmpExpectation>( //
435 "PRESENT" >> pure(OmpExpectation::Value::Present)))
436
437TYPE_PARSER(construct<OmpInteropRuntimeIdentifier>(
438 construct<OmpInteropRuntimeIdentifier>(charLiteralConstant) ||
439 construct<OmpInteropRuntimeIdentifier>(scalarIntConstantExpr)))
440
441TYPE_PARSER(construct<OmpInteropPreference>(verbatim("PREFER_TYPE"_tok) >>
442 parenthesized(nonemptyList(Parser<OmpInteropRuntimeIdentifier>{}))))
443
444TYPE_PARSER(construct<OmpInteropType>(
445 "TARGETSYNC" >> pure(OmpInteropType::Value::TargetSync) ||
446 "TARGET" >> pure(OmpInteropType::Value::Target)))
447
448TYPE_PARSER(construct<OmpIteratorSpecifier>(
449 // Using Parser<TypeDeclarationStmt> or Parser<EntityDecl> has the problem
450 // that they will attempt to treat what follows the '=' as initialization.
451 // There are several issues with that,
452 // 1. integer :: i = 0:10 will be parsed as "integer :: i = 0", followed
453 // by triplet ":10".
454 // 2. integer :: j = i:10 will be flagged as an error because the
455 // initializer 'i' must be constant (in declarations). In an iterator
456 // specifier the 'j' is not an initializer and can be a variable.
457 (applyFunction<TypeDeclarationStmt>(makeIterSpecDecl,
458 Parser<DeclarationTypeSpec>{} / maybe("::"_tok),
459 nonemptyList(Parser<ObjectName>{}) / "="_tok) ||
460 applyFunction<TypeDeclarationStmt>(
461 makeIterSpecDecl, nonemptyList(Parser<ObjectName>{}) / "="_tok)),
462 subscriptTriplet))
463
464// [5.0] 2.1.6 iterator -> iterator-specifier-list
465TYPE_PARSER(construct<OmpIterator>( //
466 "ITERATOR" >>
467 parenthesized(nonemptyList(sourced(Parser<OmpIteratorSpecifier>{})))))
468
469TYPE_PARSER(construct<OmpLastprivateModifier>(
470 "CONDITIONAL" >> pure(OmpLastprivateModifier::Value::Conditional)))
471
472// 2.15.3.7 LINEAR (linear-list: linear-step)
473// linear-list -> list | modifier(list)
474// linear-modifier -> REF | VAL | UVAL
475TYPE_PARSER(construct<OmpLinearModifier>( //
476 "REF" >> pure(OmpLinearModifier::Value::Ref) ||
477 "VAL" >> pure(OmpLinearModifier::Value::Val) ||
478 "UVAL" >> pure(OmpLinearModifier::Value::Uval)))
479
480TYPE_PARSER(construct<OmpMapper>( //
481 "MAPPER"_tok >> parenthesized(Parser<ObjectName>{})))
482
483// map-type -> ALLOC | DELETE | FROM | RELEASE | TO | TOFROM
484TYPE_PARSER(construct<OmpMapType>( //
485 "ALLOC" >> pure(OmpMapType::Value::Alloc) ||
486 "DELETE" >> pure(OmpMapType::Value::Delete) ||
487 "FROM" >> pure(OmpMapType::Value::From) ||
488 "RELEASE" >> pure(OmpMapType::Value::Release) ||
489 "TO"_id >> pure(OmpMapType::Value::To) ||
490 "TOFROM" >> pure(OmpMapType::Value::Tofrom)))
491
492// map-type-modifier -> ALWAYS | CLOSE | OMPX_HOLD | PRESENT
493TYPE_PARSER(construct<OmpMapTypeModifier>(
494 "ALWAYS" >> pure(OmpMapTypeModifier::Value::Always) ||
495 "CLOSE" >> pure(OmpMapTypeModifier::Value::Close) ||
496 "OMPX_HOLD" >> pure(OmpMapTypeModifier::Value::Ompx_Hold) ||
497 "PRESENT" >> pure(OmpMapTypeModifier::Value::Present)))
498
499// 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list)
500TYPE_PARSER(construct<OmpReductionIdentifier>(Parser<DefinedOperator>{}) ||
501 construct<OmpReductionIdentifier>(Parser<ProcedureDesignator>{}))
502
503TYPE_PARSER(construct<OmpOrderModifier>(
504 "REPRODUCIBLE" >> pure(OmpOrderModifier::Value::Reproducible) ||
505 "UNCONSTRAINED" >> pure(OmpOrderModifier::Value::Unconstrained)))
506
507TYPE_PARSER(construct<OmpOrderingModifier>(
508 "MONOTONIC" >> pure(OmpOrderingModifier::Value::Monotonic) ||
509 "NONMONOTONIC" >> pure(OmpOrderingModifier::Value::Nonmonotonic) ||
510 "SIMD" >> pure(OmpOrderingModifier::Value::Simd)))
511
512TYPE_PARSER(construct<OmpPrescriptiveness>(
513 "STRICT" >> pure(OmpPrescriptiveness::Value::Strict)))
514
515TYPE_PARSER(construct<OmpReductionModifier>(
516 "INSCAN" >> pure(OmpReductionModifier::Value::Inscan) ||
517 "TASK" >> pure(OmpReductionModifier::Value::Task) ||
518 "DEFAULT" >> pure(OmpReductionModifier::Value::Default)))
519
520TYPE_PARSER(construct<OmpStepComplexModifier>( //
521 "STEP" >> parenthesized(scalarIntExpr)))
522
523TYPE_PARSER(construct<OmpStepSimpleModifier>(scalarIntExpr))
524
525TYPE_PARSER(construct<OmpTaskDependenceType>(
526 "DEPOBJ" >> pure(OmpTaskDependenceType::Value::Depobj) ||
527 "IN"_id >> pure(OmpTaskDependenceType::Value::In) ||
528 "INOUT"_id >> pure(OmpTaskDependenceType::Value::Inout) ||
529 "INOUTSET"_id >> pure(OmpTaskDependenceType::Value::Inoutset) ||
530 "MUTEXINOUTSET" >> pure(OmpTaskDependenceType::Value::Mutexinoutset) ||
531 "OUT" >> pure(OmpTaskDependenceType::Value::Out)))
532
533TYPE_PARSER(construct<OmpVariableCategory>(
534 "AGGREGATE" >> pure(OmpVariableCategory::Value::Aggregate) ||
535 "ALL"_id >> pure(OmpVariableCategory::Value::All) ||
536 "ALLOCATABLE" >> pure(OmpVariableCategory::Value::Allocatable) ||
537 "POINTER" >> pure(OmpVariableCategory::Value::Pointer) ||
538 "SCALAR" >> pure(OmpVariableCategory::Value::Scalar)))
539
540// This could be auto-generated.
541TYPE_PARSER(
542 sourced(construct<OmpAffinityClause::Modifier>(Parser<OmpIterator>{})))
543
544TYPE_PARSER(
545 sourced(construct<OmpAlignedClause::Modifier>(Parser<OmpAlignment>{})))
546
547TYPE_PARSER(sourced(construct<OmpAllocateClause::Modifier>(sourced(
548 construct<OmpAllocateClause::Modifier>(Parser<OmpAlignModifier>{}) ||
549 construct<OmpAllocateClause::Modifier>(
550 Parser<OmpAllocatorComplexModifier>{}) ||
551 construct<OmpAllocateClause::Modifier>(
552 Parser<OmpAllocatorSimpleModifier>{})))))
553
554TYPE_PARSER(sourced(
555 construct<OmpDefaultmapClause::Modifier>(Parser<OmpVariableCategory>{})))
556
557TYPE_PARSER(sourced(construct<OmpDependClause::TaskDep::Modifier>(sourced(
558 construct<OmpDependClause::TaskDep::Modifier>(Parser<OmpIterator>{}) ||
559 construct<OmpDependClause::TaskDep::Modifier>(
560 Parser<OmpTaskDependenceType>{})))))
561
562TYPE_PARSER(
563 sourced(construct<OmpDeviceClause::Modifier>(Parser<OmpDeviceModifier>{})))
564
565TYPE_PARSER(sourced(construct<OmpFromClause::Modifier>(
566 sourced(construct<OmpFromClause::Modifier>(Parser<OmpExpectation>{}) ||
567 construct<OmpFromClause::Modifier>(Parser<OmpMapper>{}) ||
568 construct<OmpFromClause::Modifier>(Parser<OmpIterator>{})))))
569
570TYPE_PARSER(sourced(
571 construct<OmpGrainsizeClause::Modifier>(Parser<OmpPrescriptiveness>{})))
572
573TYPE_PARSER(sourced(construct<OmpIfClause::Modifier>(OmpDirectiveNameParser{})))
574
575TYPE_PARSER(sourced(
576 construct<OmpInitClause::Modifier>(
577 construct<OmpInitClause::Modifier>(Parser<OmpInteropPreference>{})) ||
578 construct<OmpInitClause::Modifier>(Parser<OmpInteropType>{})))
579
580TYPE_PARSER(sourced(construct<OmpInReductionClause::Modifier>(
581 Parser<OmpReductionIdentifier>{})))
582
583TYPE_PARSER(sourced(construct<OmpLastprivateClause::Modifier>(
584 Parser<OmpLastprivateModifier>{})))
585
586TYPE_PARSER(sourced(
587 construct<OmpLinearClause::Modifier>(Parser<OmpLinearModifier>{}) ||
588 construct<OmpLinearClause::Modifier>(Parser<OmpStepComplexModifier>{}) ||
589 construct<OmpLinearClause::Modifier>(Parser<OmpStepSimpleModifier>{})))
590
591TYPE_PARSER(sourced(construct<OmpMapClause::Modifier>(
592 sourced(construct<OmpMapClause::Modifier>(Parser<OmpMapTypeModifier>{}) ||
593 construct<OmpMapClause::Modifier>(Parser<OmpMapper>{}) ||
594 construct<OmpMapClause::Modifier>(Parser<OmpIterator>{}) ||
595 construct<OmpMapClause::Modifier>(Parser<OmpMapType>{})))))
596
597TYPE_PARSER(
598 sourced(construct<OmpOrderClause::Modifier>(Parser<OmpOrderModifier>{})))
599
600TYPE_PARSER(sourced(
601 construct<OmpNumTasksClause::Modifier>(Parser<OmpPrescriptiveness>{})))
602
603TYPE_PARSER(sourced(construct<OmpReductionClause::Modifier>(sourced(
604 construct<OmpReductionClause::Modifier>(Parser<OmpReductionModifier>{}) ||
605 construct<OmpReductionClause::Modifier>(
606 Parser<OmpReductionIdentifier>{})))))
607
608TYPE_PARSER(sourced(construct<OmpScheduleClause::Modifier>(sourced(
609 construct<OmpScheduleClause::Modifier>(Parser<OmpChunkModifier>{}) ||
610 construct<OmpScheduleClause::Modifier>(Parser<OmpOrderingModifier>{})))))
611
612TYPE_PARSER(sourced(construct<OmpTaskReductionClause::Modifier>(
613 Parser<OmpReductionIdentifier>{})))
614
615TYPE_PARSER(sourced(construct<OmpToClause::Modifier>(
616 sourced(construct<OmpToClause::Modifier>(Parser<OmpExpectation>{}) ||
617 construct<OmpToClause::Modifier>(Parser<OmpMapper>{}) ||
618 construct<OmpToClause::Modifier>(Parser<OmpIterator>{})))))
619
620TYPE_PARSER(sourced(construct<OmpWhenClause::Modifier>( //
621 Parser<OmpContextSelector>{})))
622
623TYPE_PARSER(construct<OmpAppendArgsClause::OmpAppendOp>(
624 "INTEROP" >> parenthesized(nonemptyList(Parser<OmpInteropType>{}))))
625
626TYPE_PARSER(construct<OmpAdjustArgsClause::OmpAdjustOp>(
627 "NOTHING" >> pure(OmpAdjustArgsClause::OmpAdjustOp::Value::Nothing) ||
628 "NEED_DEVICE_PTR" >>
629 pure(OmpAdjustArgsClause::OmpAdjustOp::Value::Need_Device_Ptr)))
630
631// --- Parsers for clauses --------------------------------------------
632
633/// `MOBClause` is a clause that has a
634/// std::tuple<Modifiers, OmpObjectList, bool>.
635/// Helper function to create a typical modifiers-objects clause, where the
636/// commas separating individual modifiers are optional, and the clause
637/// contains a bool member to indicate whether it was fully comma-separated
638/// or not.
639template <bool CommaSeparated, typename MOBClause>
640static inline MOBClause makeMobClause(
641 std::list<typename MOBClause::Modifier> &&mods, OmpObjectList &&objs) {
642 if (!mods.empty()) {
643 return MOBClause{std::move(mods), std::move(objs), CommaSeparated};
644 } else {
645 using ListTy = std::list<typename MOBClause::Modifier>;
646 return MOBClause{std::optional<ListTy>{}, std::move(objs), CommaSeparated};
647 }
648}
649
650TYPE_PARSER(construct<OmpAdjustArgsClause>(
651 (Parser<OmpAdjustArgsClause::OmpAdjustOp>{} / ":"),
652 Parser<OmpObjectList>{}))
653
654// [5.0] 2.10.1 affinity([aff-modifier:] locator-list)
655// aff-modifier: interator-modifier
656TYPE_PARSER(construct<OmpAffinityClause>(
657 maybe(nonemptyList(Parser<OmpAffinityClause::Modifier>{}) / ":"),
658 Parser<OmpObjectList>{}))
659
660// 2.4 Requires construct [OpenMP 5.0]
661// atomic-default-mem-order-clause ->
662// acq_rel
663// acquire
664// relaxed
665// release
666// seq_cst
667TYPE_PARSER(construct<OmpAtomicDefaultMemOrderClause>(
668 "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) ||
669 "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) ||
670 "RELAXED" >> pure(common::OmpMemoryOrderType::Relaxed) ||
671 "RELEASE" >> pure(common::OmpMemoryOrderType::Release) ||
672 "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst)))
673
674TYPE_PARSER(construct<OmpCancellationConstructTypeClause>(
675 OmpDirectiveNameParser{}, maybe(parenthesized(scalarLogicalExpr))))
676
677TYPE_PARSER(construct<OmpAppendArgsClause>(
678 nonemptyList(Parser<OmpAppendArgsClause::OmpAppendOp>{})))
679
680// 2.15.3.1 DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE)
681TYPE_PARSER(construct<OmpDefaultClause::DataSharingAttribute>(
682 "PRIVATE" >> pure(OmpDefaultClause::DataSharingAttribute::Private) ||
683 "FIRSTPRIVATE" >>
684 pure(OmpDefaultClause::DataSharingAttribute::Firstprivate) ||
685 "SHARED" >> pure(OmpDefaultClause::DataSharingAttribute::Shared) ||
686 "NONE" >> pure(OmpDefaultClause::DataSharingAttribute::None)))
687
688TYPE_PARSER(construct<OmpDefaultClause>(
689 construct<OmpDefaultClause>(
690 Parser<OmpDefaultClause::DataSharingAttribute>{}) ||
691 construct<OmpDefaultClause>(indirect(Parser<OmpDirectiveSpecification>{}))))
692
693TYPE_PARSER(construct<OmpFailClause>(
694 "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) ||
695 "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) ||
696 "RELAXED" >> pure(common::OmpMemoryOrderType::Relaxed) ||
697 "RELEASE" >> pure(common::OmpMemoryOrderType::Release) ||
698 "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst)))
699
700// 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD)
701TYPE_PARSER(construct<OmpProcBindClause>(
702 "CLOSE" >> pure(OmpProcBindClause::AffinityPolicy::Close) ||
703 "MASTER" >> pure(OmpProcBindClause::AffinityPolicy::Master) ||
704 "PRIMARY" >> pure(OmpProcBindClause::AffinityPolicy::Primary) ||
705 "SPREAD" >> pure(OmpProcBindClause::AffinityPolicy::Spread)))
706
707TYPE_PARSER(construct<OmpMapClause>(
708 applyFunction<OmpMapClause>(makeMobClause<true>,
709 modifierList<OmpMapClause>(","_tok), Parser<OmpObjectList>{}) ||
710 applyFunction<OmpMapClause>(makeMobClause<false>,
711 modifierList<OmpMapClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
712
713// [OpenMP 5.0]
714// 2.19.7.2 defaultmap(implicit-behavior[:variable-category])
715// implicit-behavior -> ALLOC | TO | FROM | TOFROM | FIRSRTPRIVATE | NONE |
716// DEFAULT | PRESENT
717// variable-category -> ALL | SCALAR | AGGREGATE | ALLOCATABLE | POINTER
718TYPE_PARSER(construct<OmpDefaultmapClause>(
719 construct<OmpDefaultmapClause::ImplicitBehavior>(
720 "ALLOC" >> pure(OmpDefaultmapClause::ImplicitBehavior::Alloc) ||
721 "TO"_id >> pure(OmpDefaultmapClause::ImplicitBehavior::To) ||
722 "FROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::From) ||
723 "TOFROM" >> pure(OmpDefaultmapClause::ImplicitBehavior::Tofrom) ||
724 "FIRSTPRIVATE" >>
725 pure(OmpDefaultmapClause::ImplicitBehavior::Firstprivate) ||
726 "NONE" >> pure(OmpDefaultmapClause::ImplicitBehavior::None) ||
727 "DEFAULT" >> pure(OmpDefaultmapClause::ImplicitBehavior::Default) ||
728 "PRESENT" >> pure(OmpDefaultmapClause::ImplicitBehavior::Present)),
729 maybe(":" >> nonemptyList(Parser<OmpDefaultmapClause::Modifier>{}))))
730
731TYPE_PARSER(construct<OmpScheduleClause::Kind>(
732 "STATIC" >> pure(OmpScheduleClause::Kind::Static) ||
733 "DYNAMIC" >> pure(OmpScheduleClause::Kind::Dynamic) ||
734 "GUIDED" >> pure(OmpScheduleClause::Kind::Guided) ||
735 "AUTO" >> pure(OmpScheduleClause::Kind::Auto) ||
736 "RUNTIME" >> pure(OmpScheduleClause::Kind::Runtime)))
737
738TYPE_PARSER(construct<OmpScheduleClause>(
739 maybe(nonemptyList(Parser<OmpScheduleClause::Modifier>{}) / ":"),
740 Parser<OmpScheduleClause::Kind>{}, maybe("," >> scalarIntExpr)))
741
742// device([ device-modifier :] scalar-integer-expression)
743TYPE_PARSER(construct<OmpDeviceClause>(
744 maybe(nonemptyList(Parser<OmpDeviceClause::Modifier>{}) / ":"),
745 scalarIntExpr))
746
747// device_type(any | host | nohost)
748TYPE_PARSER(construct<OmpDeviceTypeClause>(
749 "ANY" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Any) ||
750 "HOST" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Host) ||
751 "NOHOST" >> pure(OmpDeviceTypeClause::DeviceTypeDescription::Nohost)))
752
753// 2.12 IF (directive-name-modifier: scalar-logical-expr)
754TYPE_PARSER(construct<OmpIfClause>(
755 maybe(nonemptyList(Parser<OmpIfClause::Modifier>{}) / ":"),
756 scalarLogicalExpr))
757
758TYPE_PARSER(construct<OmpReductionClause>(
759 maybe(nonemptyList(Parser<OmpReductionClause::Modifier>{}) / ":"),
760 Parser<OmpObjectList>{}))
761
762// OMP 5.0 2.19.5.6 IN_REDUCTION (reduction-identifier: variable-name-list)
763TYPE_PARSER(construct<OmpInReductionClause>(
764 maybe(nonemptyList(Parser<OmpInReductionClause::Modifier>{}) / ":"),
765 Parser<OmpObjectList>{}))
766
767TYPE_PARSER(construct<OmpTaskReductionClause>(
768 maybe(nonemptyList(Parser<OmpTaskReductionClause::Modifier>{}) / ":"),
769 Parser<OmpObjectList>{}))
770
771// OMP 5.0 2.11.4 allocate-clause -> ALLOCATE ([allocator:] variable-name-list)
772// OMP 5.2 2.13.4 allocate-clause -> ALLOCATE ([allocate-modifier
773// [, allocate-modifier] :]
774// variable-name-list)
775// allocate-modifier -> allocator | align
776TYPE_PARSER(construct<OmpAllocateClause>(
777 maybe(nonemptyList(Parser<OmpAllocateClause::Modifier>{}) / ":"),
778 Parser<OmpObjectList>{}))
779
780// iteration-offset -> +/- non-negative-constant-expr
781TYPE_PARSER(construct<OmpIterationOffset>(
782 Parser<DefinedOperator>{}, scalarIntConstantExpr))
783
784// iteration -> iteration-variable [+/- nonnegative-scalar-integer-constant]
785TYPE_PARSER(construct<OmpIteration>(name, maybe(Parser<OmpIterationOffset>{})))
786
787TYPE_PARSER(construct<OmpIterationVector>(nonemptyList(Parser<OmpIteration>{})))
788
789TYPE_PARSER(construct<OmpDoacross>(
790 construct<OmpDoacross>(construct<OmpDoacross::Sink>(
791 "SINK"_tok >> ":"_tok >> Parser<OmpIterationVector>{})) ||
792 construct<OmpDoacross>(construct<OmpDoacross::Source>("SOURCE"_tok))))
793
794TYPE_CONTEXT_PARSER("Omp Depend clause"_en_US,
795 construct<OmpDependClause>(
796 // Try to parse OmpDoacross first, because TaskDep will succeed on
797 // "sink: xxx", interpreting it to not have any modifiers, and "sink"
798 // being an OmpObject. Parsing of the TaskDep variant will stop right
799 // after the "sink", leaving the ": xxx" unvisited.
800 construct<OmpDependClause>(Parser<OmpDoacross>{}) ||
801 // Parse TaskDep after Doacross.
802 construct<OmpDependClause>(construct<OmpDependClause::TaskDep>(
803 maybe(nonemptyList(Parser<OmpDependClause::TaskDep::Modifier>{}) /
804 ": "),
805 Parser<OmpObjectList>{}))))
806
807TYPE_CONTEXT_PARSER("Omp Doacross clause"_en_US,
808 construct<OmpDoacrossClause>(Parser<OmpDoacross>{}))
809
810TYPE_PARSER(construct<OmpFromClause>(
811 applyFunction<OmpFromClause>(makeMobClause<true>,
812 modifierList<OmpFromClause>(","_tok), Parser<OmpObjectList>{}) ||
813 applyFunction<OmpFromClause>(makeMobClause<false>,
814 modifierList<OmpFromClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
815
816TYPE_PARSER(construct<OmpToClause>(
817 applyFunction<OmpToClause>(makeMobClause<true>,
818 modifierList<OmpToClause>(","_tok), Parser<OmpObjectList>{}) ||
819 applyFunction<OmpToClause>(makeMobClause<false>,
820 modifierList<OmpToClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
821
822OmpLinearClause makeLinearFromOldSyntax(OmpLinearClause::Modifier &&lm,
823 OmpObjectList &&objs, std::optional<OmpLinearClause::Modifier> &&ssm) {
824 std::list<OmpLinearClause::Modifier> mods;
825 mods.emplace_back(std::move(lm));
826 if (ssm) {
827 mods.emplace_back(std::move(*ssm));
828 }
829 return OmpLinearClause{std::move(objs),
830 mods.empty() ? decltype(mods){} : std::move(mods),
831 /*PostModified=*/false};
832}
833
834TYPE_PARSER(
835 // Parse the "modifier(x)" first, because syntacticaly it will match
836 // an array element (i.e. a list item).
837 // LINEAR(linear-modifier(list) [: step-simple-modifier])
838 construct<OmpLinearClause>( //
839 applyFunction<OmpLinearClause>(makeLinearFromOldSyntax,
840 SpecificModifierParser<OmpLinearModifier, OmpLinearClause>{},
841 parenthesized(Parser<OmpObjectList>{}),
842 maybe(":"_tok >> SpecificModifierParser<OmpStepSimpleModifier,
843 OmpLinearClause>{}))) ||
844 // LINEAR(list [: modifiers])
845 construct<OmpLinearClause>( //
846 Parser<OmpObjectList>{},
847 maybe(":"_tok >> nonemptyList(Parser<OmpLinearClause::Modifier>{})),
848 /*PostModified=*/pure(true)))
849
850// OpenMPv5.2 12.5.2 detach-clause -> DETACH (event-handle)
851TYPE_PARSER(construct<OmpDetachClause>(Parser<OmpObject>{}))
852
853TYPE_PARSER(construct<OmpHintClause>(scalarIntConstantExpr))
854
855// init clause
856TYPE_PARSER(construct<OmpInitClause>(
857 maybe(nonemptyList(Parser<OmpInitClause::Modifier>{}) / ":"),
858 Parser<OmpObject>{}))
859
860// 2.8.1 ALIGNED (list: alignment)
861TYPE_PARSER(construct<OmpAlignedClause>(Parser<OmpObjectList>{},
862 maybe(":" >> nonemptyList(Parser<OmpAlignedClause::Modifier>{}))))
863
864TYPE_PARSER( //
865 construct<OmpUpdateClause>(parenthesized(Parser<OmpDependenceType>{})) ||
866 construct<OmpUpdateClause>(parenthesized(Parser<OmpTaskDependenceType>{})))
867
868TYPE_PARSER(construct<OmpOrderClause>(
869 maybe(nonemptyList(Parser<OmpOrderClause::Modifier>{}) / ":"),
870 "CONCURRENT" >> pure(OmpOrderClause::Ordering::Concurrent)))
871
872TYPE_PARSER(construct<OmpMatchClause>(
873 Parser<traits::OmpContextSelectorSpecification>{}))
874
875TYPE_PARSER(construct<OmpOtherwiseClause>(
876 maybe(indirect(sourced(Parser<OmpDirectiveSpecification>{})))))
877
878TYPE_PARSER(construct<OmpWhenClause>(
879 maybe(nonemptyList(Parser<OmpWhenClause::Modifier>{}) / ":"),
880 maybe(indirect(sourced(Parser<OmpDirectiveSpecification>{})))))
881
882// OMP 5.2 12.6.1 grainsize([ prescriptiveness :] scalar-integer-expression)
883TYPE_PARSER(construct<OmpGrainsizeClause>(
884 maybe(nonemptyList(Parser<OmpGrainsizeClause::Modifier>{}) / ":"),
885 scalarIntExpr))
886
887// OMP 5.2 12.6.2 num_tasks([ prescriptiveness :] scalar-integer-expression)
888TYPE_PARSER(construct<OmpNumTasksClause>(
889 maybe(nonemptyList(Parser<OmpNumTasksClause::Modifier>{}) / ":"),
890 scalarIntExpr))
891
892TYPE_PARSER(
893 construct<OmpObject>(designator) || construct<OmpObject>("/" >> name / "/"))
894
895// OMP 5.0 2.19.4.5 LASTPRIVATE ([lastprivate-modifier :] list)
896TYPE_PARSER(construct<OmpLastprivateClause>(
897 maybe(nonemptyList(Parser<OmpLastprivateClause::Modifier>{}) / ":"),
898 Parser<OmpObjectList>{}))
899
900// OMP 5.2 11.7.1 BIND ( PARALLEL | TEAMS | THREAD )
901TYPE_PARSER(construct<OmpBindClause>(
902 "PARALLEL" >> pure(OmpBindClause::Binding::Parallel) ||
903 "TEAMS" >> pure(OmpBindClause::Binding::Teams) ||
904 "THREAD" >> pure(OmpBindClause::Binding::Thread)))
905
906TYPE_PARSER(construct<OmpAlignClause>(scalarIntExpr))
907
908TYPE_PARSER(construct<OmpAtClause>(
909 "EXECUTION" >> pure(OmpAtClause::ActionTime::Execution) ||
910 "COMPILATION" >> pure(OmpAtClause::ActionTime::Compilation)))
911
912TYPE_PARSER(construct<OmpSeverityClause>(
913 "FATAL" >> pure(OmpSeverityClause::Severity::Fatal) ||
914 "WARNING" >> pure(OmpSeverityClause::Severity::Warning)))
915
916TYPE_PARSER(construct<OmpMessageClause>(expr))
917
918TYPE_PARSER(construct<OmpHoldsClause>(indirect(expr)))
919TYPE_PARSER(construct<OmpAbsentClause>(many(maybe(","_tok) >>
920 construct<llvm::omp::Directive>(unwrap(OmpDirectiveNameParser{})))))
921TYPE_PARSER(construct<OmpContainsClause>(many(maybe(","_tok) >>
922 construct<llvm::omp::Directive>(unwrap(OmpDirectiveNameParser{})))))
923
924TYPE_PARSER( //
925 "ABSENT" >> construct<OmpClause>(construct<OmpClause::Absent>(
926 parenthesized(Parser<OmpAbsentClause>{}))) ||
927 "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
928 "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
929 "ADJUST_ARGS" >> construct<OmpClause>(construct<OmpClause::AdjustArgs>(
930 parenthesized(Parser<OmpAdjustArgsClause>{}))) ||
931 "AFFINITY" >> construct<OmpClause>(construct<OmpClause::Affinity>(
932 parenthesized(Parser<OmpAffinityClause>{}))) ||
933 "ALIGN" >> construct<OmpClause>(construct<OmpClause::Align>(
934 parenthesized(Parser<OmpAlignClause>{}))) ||
935 "ALIGNED" >> construct<OmpClause>(construct<OmpClause::Aligned>(
936 parenthesized(Parser<OmpAlignedClause>{}))) ||
937 "ALLOCATE" >> construct<OmpClause>(construct<OmpClause::Allocate>(
938 parenthesized(Parser<OmpAllocateClause>{}))) ||
939 "APPEND_ARGS" >> construct<OmpClause>(construct<OmpClause::AppendArgs>(
940 parenthesized(Parser<OmpAppendArgsClause>{}))) ||
941 "ALLOCATOR" >> construct<OmpClause>(construct<OmpClause::Allocator>(
942 parenthesized(scalarIntExpr))) ||
943 "AT" >> construct<OmpClause>(construct<OmpClause::At>(
944 parenthesized(Parser<OmpAtClause>{}))) ||
945 "ATOMIC_DEFAULT_MEM_ORDER" >>
946 construct<OmpClause>(construct<OmpClause::AtomicDefaultMemOrder>(
947 parenthesized(Parser<OmpAtomicDefaultMemOrderClause>{}))) ||
948 "BIND" >> construct<OmpClause>(construct<OmpClause::Bind>(
949 parenthesized(Parser<OmpBindClause>{}))) ||
950 "CAPTURE" >> construct<OmpClause>(construct<OmpClause::Capture>()) ||
951 "COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>(
952 parenthesized(scalarIntConstantExpr))) ||
953 "COMPARE" >> construct<OmpClause>(construct<OmpClause::Compare>()) ||
954 "CONTAINS" >> construct<OmpClause>(construct<OmpClause::Contains>(
955 parenthesized(Parser<OmpContainsClause>{}))) ||
956 "COPYIN" >> construct<OmpClause>(construct<OmpClause::Copyin>(
957 parenthesized(Parser<OmpObjectList>{}))) ||
958 "COPYPRIVATE" >> construct<OmpClause>(construct<OmpClause::Copyprivate>(
959 (parenthesized(Parser<OmpObjectList>{})))) ||
960 "DEFAULT"_id >> construct<OmpClause>(construct<OmpClause::Default>(
961 parenthesized(Parser<OmpDefaultClause>{}))) ||
962 "DEFAULTMAP" >> construct<OmpClause>(construct<OmpClause::Defaultmap>(
963 parenthesized(Parser<OmpDefaultmapClause>{}))) ||
964 "DEPEND" >> construct<OmpClause>(construct<OmpClause::Depend>(
965 parenthesized(Parser<OmpDependClause>{}))) ||
966 "DESTROY" >>
967 construct<OmpClause>(construct<OmpClause::Destroy>(maybe(parenthesized(
968 construct<OmpDestroyClause>(Parser<OmpObject>{}))))) ||
969 "DEVICE" >> construct<OmpClause>(construct<OmpClause::Device>(
970 parenthesized(Parser<OmpDeviceClause>{}))) ||
971 "DEVICE_TYPE" >> construct<OmpClause>(construct<OmpClause::DeviceType>(
972 parenthesized(Parser<OmpDeviceTypeClause>{}))) ||
973 "DIST_SCHEDULE" >>
974 construct<OmpClause>(construct<OmpClause::DistSchedule>(
975 parenthesized("STATIC" >> maybe("," >> scalarIntExpr)))) ||
976 "DOACROSS" >>
977 construct<OmpClause>(parenthesized(Parser<OmpDoacrossClause>{})) ||
978 "DYNAMIC_ALLOCATORS" >>
979 construct<OmpClause>(construct<OmpClause::DynamicAllocators>()) ||
980 "ENTER" >> construct<OmpClause>(construct<OmpClause::Enter>(
981 parenthesized(Parser<OmpObjectList>{}))) ||
982 "EXCLUSIVE" >> construct<OmpClause>(construct<OmpClause::Exclusive>(
983 parenthesized(Parser<OmpObjectList>{}))) ||
984 "FAIL" >> construct<OmpClause>(construct<OmpClause::Fail>(
985 parenthesized(Parser<OmpFailClause>{}))) ||
986 "FILTER" >> construct<OmpClause>(construct<OmpClause::Filter>(
987 parenthesized(scalarIntExpr))) ||
988 "FINAL" >> construct<OmpClause>(construct<OmpClause::Final>(
989 parenthesized(scalarLogicalExpr))) ||
990 "FIRSTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Firstprivate>(
991 parenthesized(Parser<OmpObjectList>{}))) ||
992 "FROM" >> construct<OmpClause>(construct<OmpClause::From>(
993 parenthesized(Parser<OmpFromClause>{}))) ||
994 "FULL" >> construct<OmpClause>(construct<OmpClause::Full>()) ||
995 "GRAINSIZE" >> construct<OmpClause>(construct<OmpClause::Grainsize>(
996 parenthesized(Parser<OmpGrainsizeClause>{}))) ||
997 "HAS_DEVICE_ADDR" >>
998 construct<OmpClause>(construct<OmpClause::HasDeviceAddr>(
999 parenthesized(Parser<OmpObjectList>{}))) ||
1000 "HINT" >> construct<OmpClause>(construct<OmpClause::Hint>(
1001 parenthesized(Parser<OmpHintClause>{}))) ||
1002 "HOLDS" >> construct<OmpClause>(construct<OmpClause::Holds>(
1003 parenthesized(Parser<OmpHoldsClause>{}))) ||
1004 "IF" >> construct<OmpClause>(construct<OmpClause::If>(
1005 parenthesized(Parser<OmpIfClause>{}))) ||
1006 "INBRANCH" >> construct<OmpClause>(construct<OmpClause::Inbranch>()) ||
1007 "INIT" >> construct<OmpClause>(construct<OmpClause::Init>(
1008 parenthesized(Parser<OmpInitClause>{}))) ||
1009 "INCLUSIVE" >> construct<OmpClause>(construct<OmpClause::Inclusive>(
1010 parenthesized(Parser<OmpObjectList>{}))) ||
1011 "INITIALIZER" >> construct<OmpClause>(construct<OmpClause::Initializer>(
1012 parenthesized(Parser<OmpInitializerClause>{}))) ||
1013 "IS_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::IsDevicePtr>(
1014 parenthesized(Parser<OmpObjectList>{}))) ||
1015 "LASTPRIVATE" >> construct<OmpClause>(construct<OmpClause::Lastprivate>(
1016 parenthesized(Parser<OmpLastprivateClause>{}))) ||
1017 "LINEAR" >> construct<OmpClause>(construct<OmpClause::Linear>(
1018 parenthesized(Parser<OmpLinearClause>{}))) ||
1019 "LINK" >> construct<OmpClause>(construct<OmpClause::Link>(
1020 parenthesized(Parser<OmpObjectList>{}))) ||
1021 "MAP" >> construct<OmpClause>(construct<OmpClause::Map>(
1022 parenthesized(Parser<OmpMapClause>{}))) ||
1023 "MATCH" >> construct<OmpClause>(construct<OmpClause::Match>(
1024 parenthesized(Parser<OmpMatchClause>{}))) ||
1025 "MERGEABLE" >> construct<OmpClause>(construct<OmpClause::Mergeable>()) ||
1026 "MESSAGE" >> construct<OmpClause>(construct<OmpClause::Message>(
1027 parenthesized(Parser<OmpMessageClause>{}))) ||
1028 "NOCONTEXT" >> construct<OmpClause>(construct<OmpClause::Nocontext>(
1029 parenthesized(scalarLogicalExpr))) ||
1030 "NOGROUP" >> construct<OmpClause>(construct<OmpClause::Nogroup>()) ||
1031 "NONTEMPORAL" >> construct<OmpClause>(construct<OmpClause::Nontemporal>(
1032 parenthesized(nonemptyList(name)))) ||
1033 "NOTINBRANCH" >>
1034 construct<OmpClause>(construct<OmpClause::Notinbranch>()) ||
1035 "NOVARIANTS" >> construct<OmpClause>(construct<OmpClause::Novariants>(
1036 parenthesized(scalarLogicalExpr))) ||
1037 "NOWAIT" >> construct<OmpClause>(construct<OmpClause::Nowait>()) ||
1038 "NO_OPENMP"_id >> construct<OmpClause>(construct<OmpClause::NoOpenmp>()) ||
1039 "NO_OPENMP_ROUTINES" >>
1040 construct<OmpClause>(construct<OmpClause::NoOpenmpRoutines>()) ||
1041 "NO_PARALLELISM" >>
1042 construct<OmpClause>(construct<OmpClause::NoParallelism>()) ||
1043 "NUM_TASKS" >> construct<OmpClause>(construct<OmpClause::NumTasks>(
1044 parenthesized(Parser<OmpNumTasksClause>{}))) ||
1045 "NUM_TEAMS" >> construct<OmpClause>(construct<OmpClause::NumTeams>(
1046 parenthesized(scalarIntExpr))) ||
1047 "NUM_THREADS" >> construct<OmpClause>(construct<OmpClause::NumThreads>(
1048 parenthesized(scalarIntExpr))) ||
1049 "OMPX_BARE" >> construct<OmpClause>(construct<OmpClause::OmpxBare>()) ||
1050 "ORDER" >> construct<OmpClause>(construct<OmpClause::Order>(
1051 parenthesized(Parser<OmpOrderClause>{}))) ||
1052 "ORDERED" >> construct<OmpClause>(construct<OmpClause::Ordered>(
1053 maybe(parenthesized(scalarIntConstantExpr)))) ||
1054 "OTHERWISE" >> construct<OmpClause>(construct<OmpClause::Otherwise>(
1055 maybe(parenthesized(Parser<OmpOtherwiseClause>{})))) ||
1056 "PARTIAL" >> construct<OmpClause>(construct<OmpClause::Partial>(
1057 maybe(parenthesized(scalarIntConstantExpr)))) ||
1058 "PRIORITY" >> construct<OmpClause>(construct<OmpClause::Priority>(
1059 parenthesized(scalarIntExpr))) ||
1060 "PRIVATE" >> construct<OmpClause>(construct<OmpClause::Private>(
1061 parenthesized(Parser<OmpObjectList>{}))) ||
1062 "PROC_BIND" >> construct<OmpClause>(construct<OmpClause::ProcBind>(
1063 parenthesized(Parser<OmpProcBindClause>{}))) ||
1064 "REDUCTION"_id >> construct<OmpClause>(construct<OmpClause::Reduction>(
1065 parenthesized(Parser<OmpReductionClause>{}))) ||
1066 "IN_REDUCTION" >> construct<OmpClause>(construct<OmpClause::InReduction>(
1067 parenthesized(Parser<OmpInReductionClause>{}))) ||
1068 "DETACH" >> construct<OmpClause>(construct<OmpClause::Detach>(
1069 parenthesized(Parser<OmpDetachClause>{}))) ||
1070 "TASK_REDUCTION" >>
1071 construct<OmpClause>(construct<OmpClause::TaskReduction>(
1072 parenthesized(Parser<OmpTaskReductionClause>{}))) ||
1073 "READ" >> construct<OmpClause>(construct<OmpClause::Read>()) ||
1074 "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>()) ||
1075 "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) ||
1076 "REVERSE_OFFLOAD" >>
1077 construct<OmpClause>(construct<OmpClause::ReverseOffload>()) ||
1078 "SAFELEN" >> construct<OmpClause>(construct<OmpClause::Safelen>(
1079 parenthesized(scalarIntConstantExpr))) ||
1080 "SCHEDULE" >> construct<OmpClause>(construct<OmpClause::Schedule>(
1081 parenthesized(Parser<OmpScheduleClause>{}))) ||
1082 "SEQ_CST" >> construct<OmpClause>(construct<OmpClause::SeqCst>()) ||
1083 "SEVERITY" >> construct<OmpClause>(construct<OmpClause::Severity>(
1084 parenthesized(Parser<OmpSeverityClause>{}))) ||
1085 "SHARED" >> construct<OmpClause>(construct<OmpClause::Shared>(
1086 parenthesized(Parser<OmpObjectList>{}))) ||
1087 "SIMD"_id >> construct<OmpClause>(construct<OmpClause::Simd>()) ||
1088 "SIMDLEN" >> construct<OmpClause>(construct<OmpClause::Simdlen>(
1089 parenthesized(scalarIntConstantExpr))) ||
1090 "SIZES" >> construct<OmpClause>(construct<OmpClause::Sizes>(
1091 parenthesized(nonemptyList(scalarIntExpr)))) ||
1092 "PERMUTATION" >> construct<OmpClause>(construct<OmpClause::Permutation>(
1093 parenthesized(nonemptyList(scalarIntExpr)))) ||
1094 "THREADS" >> construct<OmpClause>(construct<OmpClause::Threads>()) ||
1095 "THREAD_LIMIT" >> construct<OmpClause>(construct<OmpClause::ThreadLimit>(
1096 parenthesized(scalarIntExpr))) ||
1097 "TO" >> construct<OmpClause>(construct<OmpClause::To>(
1098 parenthesized(Parser<OmpToClause>{}))) ||
1099 "USE" >> construct<OmpClause>(construct<OmpClause::Use>(
1100 parenthesized(Parser<OmpObject>{}))) ||
1101 "USE_DEVICE_PTR" >> construct<OmpClause>(construct<OmpClause::UseDevicePtr>(
1102 parenthesized(Parser<OmpObjectList>{}))) ||
1103 "USE_DEVICE_ADDR" >>
1104 construct<OmpClause>(construct<OmpClause::UseDeviceAddr>(
1105 parenthesized(Parser<OmpObjectList>{}))) ||
1106 "UNIFIED_ADDRESS" >>
1107 construct<OmpClause>(construct<OmpClause::UnifiedAddress>()) ||
1108 "UNIFIED_SHARED_MEMORY" >>
1109 construct<OmpClause>(construct<OmpClause::UnifiedSharedMemory>()) ||
1110 "UNIFORM" >> construct<OmpClause>(construct<OmpClause::Uniform>(
1111 parenthesized(nonemptyList(name)))) ||
1112 "UNTIED" >> construct<OmpClause>(construct<OmpClause::Untied>()) ||
1113 "UPDATE" >> construct<OmpClause>(construct<OmpClause::Update>(
1114 maybe(Parser<OmpUpdateClause>{}))) ||
1115 "WHEN" >> construct<OmpClause>(construct<OmpClause::When>(
1116 parenthesized(Parser<OmpWhenClause>{}))) ||
1117 "WRITE" >> construct<OmpClause>(construct<OmpClause::Write>()) ||
1118 // Cancellable constructs
1119 construct<OmpClause>(construct<OmpClause::CancellationConstructType>(
1120 Parser<OmpCancellationConstructTypeClause>{})))
1121
1122// [Clause, [Clause], ...]
1123TYPE_PARSER(sourced(construct<OmpClauseList>(
1124 many(maybe(","_tok) >> sourced(Parser<OmpClause>{})))))
1125
1126// 2.1 (variable | /common-block/ | array-sections)
1127TYPE_PARSER(construct<OmpObjectList>(nonemptyList(Parser<OmpObject>{})))
1128
1129TYPE_PARSER(sourced(construct<OmpErrorDirective>(
1130 verbatim("ERROR"_tok), Parser<OmpClauseList>{})))
1131
1132// --- Parsers for directives and constructs --------------------------
1133
1134TYPE_PARSER(sourced(construct<OmpDirectiveName>(OmpDirectiveNameParser{})))
1135
1136OmpDirectiveSpecification static makeFlushFromOldSyntax(Verbatim &&text,
1137 std::optional<OmpClauseList> &&clauses,
1138 std::optional<OmpArgumentList> &&args,
1139 OmpDirectiveSpecification::Flags &&flags) {
1140 return OmpDirectiveSpecification{OmpDirectiveName(text), std::move(args),
1141 std::move(clauses), std::move(flags)};
1142}
1143
1144TYPE_PARSER(sourced(
1145 // Parse the old syntax: FLUSH [clauses] [(objects)]
1146 construct<OmpDirectiveSpecification>(
1147 // Force this old-syntax parser to fail for FLUSH followed by '('.
1148 // Otherwise it could succeed on the new syntax but have one of
1149 // lists absent in the parsed result.
1150 // E.g. for FLUSH(x) SEQ_CST it would find no clauses following
1151 // the directive name, parse the argument list "(x)" and stop.
1152 applyFunction<OmpDirectiveSpecification>(makeFlushFromOldSyntax,
1153 verbatim("FLUSH"_tok) / !lookAhead("("_tok),
1154 maybe(Parser<OmpClauseList>{}),
1155 maybe(parenthesized(Parser<OmpArgumentList>{})),
1156 pure(OmpDirectiveSpecification::Flags::DeprecatedSyntax))) ||
1157 // Parse the standard syntax: directive [(arguments)] [clauses]
1158 construct<OmpDirectiveSpecification>( //
1159 sourced(OmpDirectiveNameParser{}),
1160 maybe(parenthesized(Parser<OmpArgumentList>{})),
1161 maybe(Parser<OmpClauseList>{}),
1162 pure(OmpDirectiveSpecification::Flags::None))))
1163
1164TYPE_PARSER(sourced(construct<OmpNothingDirective>("NOTHING" >> ok)))
1165
1166TYPE_PARSER(sourced(construct<OpenMPUtilityConstruct>(
1167 sourced(construct<OpenMPUtilityConstruct>(
1168 sourced(Parser<OmpErrorDirective>{}))) ||
1169 sourced(construct<OpenMPUtilityConstruct>(
1170 sourced(Parser<OmpNothingDirective>{}))))))
1171
1172TYPE_PARSER(sourced(construct<OmpMetadirectiveDirective>(
1173 "METADIRECTIVE" >> Parser<OmpClauseList>{})))
1174
1175// Omp directives enclosing do loop
1176TYPE_PARSER(sourced(construct<OmpLoopDirective>(first(
1177 "DISTRIBUTE PARALLEL DO SIMD" >>
1178 pure(llvm::omp::Directive::OMPD_distribute_parallel_do_simd),
1179 "DISTRIBUTE PARALLEL DO" >>
1180 pure(llvm::omp::Directive::OMPD_distribute_parallel_do),
1181 "DISTRIBUTE SIMD" >> pure(llvm::omp::Directive::OMPD_distribute_simd),
1182 "DISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_distribute),
1183 "DO SIMD" >> pure(llvm::omp::Directive::OMPD_do_simd),
1184 "DO" >> pure(llvm::omp::Directive::OMPD_do),
1185 "LOOP" >> pure(llvm::omp::Directive::OMPD_loop),
1186 "MASKED TASKLOOP SIMD" >>
1187 pure(llvm::omp::Directive::OMPD_masked_taskloop_simd),
1188 "MASKED TASKLOOP" >> pure(llvm::omp::Directive::OMPD_masked_taskloop),
1189 "MASTER TASKLOOP SIMD" >>
1190 pure(llvm::omp::Directive::OMPD_master_taskloop_simd),
1191 "MASTER TASKLOOP" >> pure(llvm::omp::Directive::OMPD_master_taskloop),
1192 "PARALLEL DO SIMD" >> pure(llvm::omp::Directive::OMPD_parallel_do_simd),
1193 "PARALLEL DO" >> pure(llvm::omp::Directive::OMPD_parallel_do),
1194 "PARALLEL MASKED TASKLOOP SIMD" >>
1195 pure(llvm::omp::Directive::OMPD_parallel_masked_taskloop_simd),
1196 "PARALLEL MASKED TASKLOOP" >>
1197 pure(llvm::omp::Directive::OMPD_parallel_masked_taskloop),
1198 "PARALLEL MASTER TASKLOOP SIMD" >>
1199 pure(llvm::omp::Directive::OMPD_parallel_master_taskloop_simd),
1200 "PARALLEL MASTER TASKLOOP" >>
1201 pure(llvm::omp::Directive::OMPD_parallel_master_taskloop),
1202 "SIMD" >> pure(llvm::omp::Directive::OMPD_simd),
1203 "TARGET LOOP" >> pure(llvm::omp::Directive::OMPD_target_loop),
1204 "TARGET PARALLEL DO SIMD" >>
1205 pure(llvm::omp::Directive::OMPD_target_parallel_do_simd),
1206 "TARGET PARALLEL DO" >> pure(llvm::omp::Directive::OMPD_target_parallel_do),
1207 "TARGET PARALLEL LOOP" >>
1208 pure(llvm::omp::Directive::OMPD_target_parallel_loop),
1209 "TARGET SIMD" >> pure(llvm::omp::Directive::OMPD_target_simd),
1210 "TARGET TEAMS DISTRIBUTE PARALLEL DO SIMD" >>
1211 pure(llvm::omp::Directive::
1212 OMPD_target_teams_distribute_parallel_do_simd),
1213 "TARGET TEAMS DISTRIBUTE PARALLEL DO" >>
1214 pure(llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do),
1215 "TARGET TEAMS DISTRIBUTE SIMD" >>
1216 pure(llvm::omp::Directive::OMPD_target_teams_distribute_simd),
1217 "TARGET TEAMS DISTRIBUTE" >>
1218 pure(llvm::omp::Directive::OMPD_target_teams_distribute),
1219 "TARGET TEAMS LOOP" >> pure(llvm::omp::Directive::OMPD_target_teams_loop),
1220 "TASKLOOP SIMD" >> pure(llvm::omp::Directive::OMPD_taskloop_simd),
1221 "TASKLOOP" >> pure(llvm::omp::Directive::OMPD_taskloop),
1222 "TEAMS DISTRIBUTE PARALLEL DO SIMD" >>
1223 pure(llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd),
1224 "TEAMS DISTRIBUTE PARALLEL DO" >>
1225 pure(llvm::omp::Directive::OMPD_teams_distribute_parallel_do),
1226 "TEAMS DISTRIBUTE SIMD" >>
1227 pure(llvm::omp::Directive::OMPD_teams_distribute_simd),
1228 "TEAMS DISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_teams_distribute),
1229 "TEAMS LOOP" >> pure(llvm::omp::Directive::OMPD_teams_loop),
1230 "TILE" >> pure(llvm::omp::Directive::OMPD_tile),
1231 "UNROLL" >> pure(llvm::omp::Directive::OMPD_unroll)))))
1232
1233TYPE_PARSER(sourced(construct<OmpBeginLoopDirective>(
1234 sourced(Parser<OmpLoopDirective>{}), Parser<OmpClauseList>{})))
1235
1236struct OmpEndDirectiveParser {
1237 using resultType = OmpDirectiveSpecification;
1238
1239 constexpr OmpEndDirectiveParser(llvm::omp::Directive dir) : dir_(dir) {}
1240
1241 std::optional<resultType> Parse(ParseState &state) const {
1242 if ((startOmpLine >> "END"_sptok).Parse(state)) {
1243 auto &&dirSpec{Parser<OmpDirectiveSpecification>{}.Parse(state)};
1244 if (dirSpec && dirSpec->DirId() == dir_) {
1245 return std::move(dirSpec);
1246 }
1247 }
1248 return std::nullopt;
1249 }
1250
1251private:
1252 llvm::omp::Directive dir_;
1253};
1254
1255// Parser for an arbitrary OpenMP ATOMIC construct.
1256//
1257// Depending on circumstances, an ATOMIC construct applies to one or more
1258// following statements. In certain cases when a single statement is
1259// expected, the end-directive is optional. The specifics depend on both
1260// the clauses used, and the form of the executable statement. To emit
1261// more meaningful messages in case of errors, the exact analysis of the
1262// structure of the construct will be delayed until semantic checks.
1263//
1264// The parser will first try the case when the end-directive is present,
1265// and will parse at most "BodyLimit" (and potentially zero) constructs
1266// while looking for the end-directive before it gives up.
1267// Then it will assume that no end-directive is present, and will try to
1268// parse a single executable construct as the body of the construct.
1269//
1270// The limit on the number of constructs is there to reduce the amount of
1271// unnecessary parsing when the end-directive is absent. It's higher than
1272// the maximum number of statements in any valid construct to accept cases
1273// when extra statements are present by mistake.
1274// A problem can occur when atomic constructs without end-directive follow
1275// each other closely, e.g.
1276// !$omp atomic write
1277// x = v
1278// !$omp atomic update
1279// x = x + 1
1280// ...
1281// The speculative parsing will become "recursive", and has the potential
1282// to take a (practically) infinite amount of time given a sufficiently
1283// large number of such constructs in a row. Since atomic constructs cannot
1284// contain other OpenMP constructs, guarding against recursive calls to the
1285// atomic construct parser solves the problem.
1286struct OmpAtomicConstructParser {
1287 using resultType = OpenMPAtomicConstruct;
1288
1289 static constexpr size_t BodyLimit{5};
1290
1291 std::optional<resultType> Parse(ParseState &state) const {
1292 if (recursing_) {
1293 return std::nullopt;
1294 }
1295 recursing_ = true;
1296
1297 auto dirSpec{Parser<OmpDirectiveSpecification>{}.Parse(state)};
1298 if (!dirSpec || dirSpec->DirId() != llvm::omp::Directive::OMPD_atomic) {
1299 recursing_ = false;
1300 return std::nullopt;
1301 }
1302
1303 auto exec{Parser<ExecutionPartConstruct>{}};
1304 auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
1305 TailType tail;
1306
1307 if (ParseOne(exec, end, tail, state)) {
1308 if (!tail.first.empty()) {
1309 if (auto &&rest{attempt(LimitedTailParser(BodyLimit)).Parse(state)}) {
1310 for (auto &&s : rest->first) {
1311 tail.first.emplace_back(std::move(s));
1312 }
1313 assert(!tail.second);
1314 tail.second = std::move(rest->second);
1315 }
1316 }
1317 recursing_ = false;
1318 return OpenMPAtomicConstruct{
1319 std::move(*dirSpec), std::move(tail.first), std::move(tail.second)};
1320 }
1321
1322 recursing_ = false;
1323 return std::nullopt;
1324 }
1325
1326private:
1327 // Begin-directive + TailType = entire construct.
1328 using TailType = std::pair<Block, std::optional<OmpDirectiveSpecification>>;
1329
1330 // Parse either an ExecutionPartConstruct, or atomic end-directive. When
1331 // successful, record the result in the "tail" provided, otherwise fail.
1332 static std::optional<Success> ParseOne( //
1333 Parser<ExecutionPartConstruct> &exec, OmpEndDirectiveParser &end,
1334 TailType &tail, ParseState &state) {
1335 auto isRecovery{[](const ExecutionPartConstruct &e) {
1336 return std::holds_alternative<ErrorRecovery>(e.u);
1337 }};
1338 if (auto &&stmt{attempt(exec).Parse(state)}; stmt && !isRecovery(*stmt)) {
1339 tail.first.emplace_back(std::move(*stmt));
1340 } else if (auto &&dir{attempt(end).Parse(state)}) {
1341 tail.second = std::move(*dir);
1342 } else {
1343 return std::nullopt;
1344 }
1345 return Success{};
1346 }
1347
1348 struct LimitedTailParser {
1349 using resultType = TailType;
1350
1351 constexpr LimitedTailParser(size_t count) : count_(count) {}
1352
1353 std::optional<resultType> Parse(ParseState &state) const {
1354 auto exec{Parser<ExecutionPartConstruct>{}};
1355 auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
1356 TailType tail;
1357
1358 for (size_t i{0}; i != count_; ++i) {
1359 if (ParseOne(exec, end, tail, state)) {
1360 if (tail.second) {
1361 // Return when the end-directive was parsed.
1362 return std::move(tail);
1363 }
1364 } else {
1365 break;
1366 }
1367 }
1368 return std::nullopt;
1369 }
1370
1371 private:
1372 const size_t count_;
1373 };
1374
1375 // The recursion guard should become thread_local if parsing is ever
1376 // parallelized.
1377 static bool recursing_;
1378};
1379
1380bool OmpAtomicConstructParser::recursing_{false};
1381
1382TYPE_PARSER(sourced( //
1383 construct<OpenMPAtomicConstruct>(OmpAtomicConstructParser{})))
1384
1385// 2.17.7 Atomic construct/2.17.8 Flush construct [OpenMP 5.0]
1386// memory-order-clause ->
1387// acq_rel
1388// acquire
1389// relaxed
1390// release
1391// seq_cst
1392TYPE_PARSER(sourced(construct<OmpMemoryOrderClause>(
1393 sourced("ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
1394 "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
1395 "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>()) ||
1396 "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) ||
1397 "SEQ_CST" >> construct<OmpClause>(construct<OmpClause::SeqCst>())))))
1398
1399static bool IsSimpleStandalone(const OmpDirectiveName &name) {
1400 switch (name.v) {
1401 case llvm::omp::Directive::OMPD_barrier:
1402 case llvm::omp::Directive::OMPD_ordered:
1403 case llvm::omp::Directive::OMPD_scan:
1404 case llvm::omp::Directive::OMPD_target_enter_data:
1405 case llvm::omp::Directive::OMPD_target_exit_data:
1406 case llvm::omp::Directive::OMPD_target_update:
1407 case llvm::omp::Directive::OMPD_taskwait:
1408 case llvm::omp::Directive::OMPD_taskyield:
1409 return true;
1410 default:
1411 return false;
1412 }
1413}
1414
1415TYPE_PARSER(sourced( //
1416 construct<OpenMPSimpleStandaloneConstruct>(
1417 predicated(OmpDirectiveNameParser{}, IsSimpleStandalone) >=
1418 Parser<OmpDirectiveSpecification>{})))
1419
1420static inline constexpr auto IsDirective(llvm::omp::Directive dir) {
1421 return [dir](const OmpDirectiveName &name) -> bool { return dir == name.v; };
1422}
1423
1424TYPE_PARSER(sourced( //
1425 construct<OpenMPFlushConstruct>(
1426 predicated(OmpDirectiveNameParser{},
1427 IsDirective(llvm::omp::Directive::OMPD_flush)) >=
1428 Parser<OmpDirectiveSpecification>{})))
1429
1430// 2.14.2 Cancellation Point construct
1431TYPE_PARSER(sourced( //
1432 construct<OpenMPCancellationPointConstruct>(
1433 predicated(OmpDirectiveNameParser{},
1434 IsDirective(llvm::omp::Directive::OMPD_cancellation_point)) >=
1435 Parser<OmpDirectiveSpecification>{})))
1436
1437// 2.14.1 Cancel construct
1438TYPE_PARSER(sourced( //
1439 construct<OpenMPCancelConstruct>(
1440 predicated(OmpDirectiveNameParser{},
1441 IsDirective(llvm::omp::Directive::OMPD_cancel)) >=
1442 Parser<OmpDirectiveSpecification>{})))
1443
1444TYPE_PARSER(sourced( //
1445 construct<OpenMPDepobjConstruct>(
1446 predicated(OmpDirectiveNameParser{},
1447 IsDirective(llvm::omp::Directive::OMPD_depobj)) >=
1448 Parser<OmpDirectiveSpecification>{})))
1449
1450// OMP 5.2 14.1 Interop construct
1451TYPE_PARSER(sourced( //
1452 construct<OpenMPInteropConstruct>(
1453 predicated(OmpDirectiveNameParser{},
1454 IsDirective(llvm::omp::Directive::OMPD_interop)) >=
1455 Parser<OmpDirectiveSpecification>{})))
1456
1457// Standalone Constructs
1458TYPE_PARSER(
1459 sourced( //
1460 construct<OpenMPStandaloneConstruct>(
1461 Parser<OpenMPSimpleStandaloneConstruct>{}) ||
1462 construct<OpenMPStandaloneConstruct>(Parser<OpenMPFlushConstruct>{}) ||
1463 // Try CANCELLATION POINT before CANCEL.
1464 construct<OpenMPStandaloneConstruct>(
1465 Parser<OpenMPCancellationPointConstruct>{}) ||
1466 construct<OpenMPStandaloneConstruct>(Parser<OpenMPCancelConstruct>{}) ||
1467 construct<OpenMPStandaloneConstruct>(
1468 Parser<OmpMetadirectiveDirective>{}) ||
1469 construct<OpenMPStandaloneConstruct>(Parser<OpenMPDepobjConstruct>{}) ||
1470 construct<OpenMPStandaloneConstruct>(
1471 Parser<OpenMPInteropConstruct>{})) /
1472 endOfLine)
1473
1474// Directives enclosing structured-block
1475TYPE_PARSER(
1476 // In this context "TARGET UPDATE" can be parsed as a TARGET directive
1477 // followed by an UPDATE clause. This is the only combination at the
1478 // moment, exclude it explicitly.
1479 (!"TARGET UPDATE"_sptok) >=
1480 construct<OmpBlockDirective>(first(
1481 "MASKED" >> pure(llvm::omp::Directive::OMPD_masked),
1482 "MASTER" >> pure(llvm::omp::Directive::OMPD_master),
1483 "ORDERED" >> pure(llvm::omp::Directive::OMPD_ordered),
1484 "PARALLEL MASKED" >> pure(llvm::omp::Directive::OMPD_parallel_masked),
1485 "PARALLEL MASTER" >> pure(llvm::omp::Directive::OMPD_parallel_master),
1486 "PARALLEL WORKSHARE" >>
1487 pure(llvm::omp::Directive::OMPD_parallel_workshare),
1488 "PARALLEL" >> pure(llvm::omp::Directive::OMPD_parallel),
1489 "SCOPE" >> pure(llvm::omp::Directive::OMPD_scope),
1490 "SINGLE" >> pure(llvm::omp::Directive::OMPD_single),
1491 "TARGET DATA" >> pure(llvm::omp::Directive::OMPD_target_data),
1492 "TARGET PARALLEL" >> pure(llvm::omp::Directive::OMPD_target_parallel),
1493 "TARGET TEAMS" >> pure(llvm::omp::Directive::OMPD_target_teams),
1494 "TARGET" >> pure(llvm::omp::Directive::OMPD_target),
1495 "TASK"_id >> pure(llvm::omp::Directive::OMPD_task),
1496 "TASKGROUP" >> pure(llvm::omp::Directive::OMPD_taskgroup),
1497 "TEAMS" >> pure(llvm::omp::Directive::OMPD_teams),
1498 "WORKSHARE" >> pure(llvm::omp::Directive::OMPD_workshare))))
1499
1500TYPE_PARSER(sourced(construct<OmpBeginBlockDirective>(
1501 sourced(Parser<OmpBlockDirective>{}), Parser<OmpClauseList>{})))
1502
1503TYPE_PARSER(construct<OmpInitializerProc>(Parser<ProcedureDesignator>{},
1504 parenthesized(many(maybe(","_tok) >> Parser<ActualArgSpec>{}))))
1505
1506TYPE_PARSER(construct<OmpInitializerClause>(
1507 construct<OmpInitializerClause>(assignmentStmt) ||
1508 construct<OmpInitializerClause>(Parser<OmpInitializerProc>{})))
1509
1510// OpenMP 5.2: 7.5.4 Declare Variant directive
1511TYPE_PARSER(sourced(
1512 construct<OmpDeclareVariantDirective>(verbatim("DECLARE VARIANT"_tok),
1513 "(" >> maybe(name / ":"), name / ")", Parser<OmpClauseList>{})))
1514
1515// 2.16 Declare Reduction Construct
1516TYPE_PARSER(sourced(construct<OpenMPDeclareReductionConstruct>(
1517 verbatim("DECLARE REDUCTION"_tok),
1518 "(" >> indirect(Parser<OmpReductionSpecifier>{}) / ")",
1519 maybe(Parser<OmpClauseList>{}))))
1520
1521// declare-target with list
1522TYPE_PARSER(sourced(construct<OmpDeclareTargetWithList>(
1523 parenthesized(Parser<OmpObjectList>{}))))
1524
1525// declare-target with clause
1526TYPE_PARSER(
1527 sourced(construct<OmpDeclareTargetWithClause>(Parser<OmpClauseList>{})))
1528
1529// declare-target-specifier
1530TYPE_PARSER(
1531 construct<OmpDeclareTargetSpecifier>(Parser<OmpDeclareTargetWithList>{}) ||
1532 construct<OmpDeclareTargetSpecifier>(Parser<OmpDeclareTargetWithClause>{}))
1533
1534// 2.10.6 Declare Target Construct
1535TYPE_PARSER(sourced(construct<OpenMPDeclareTargetConstruct>(
1536 verbatim("DECLARE TARGET"_tok), Parser<OmpDeclareTargetSpecifier>{})))
1537
1538static OmpMapperSpecifier ConstructOmpMapperSpecifier(
1539 std::optional<Name> &&mapperName, TypeSpec &&typeSpec, Name &&varName) {
1540 // If a name is present, parse: name ":" typeSpec "::" name
1541 // This matches the syntax: <mapper-name> : <type-spec> :: <variable-name>
1542 if (mapperName.has_value() && mapperName->ToString() != "default") {
1543 return OmpMapperSpecifier{
1544 mapperName->ToString(), std::move(typeSpec), std::move(varName)};
1545 }
1546 // If the name is missing, use the DerivedTypeSpec name to construct the
1547 // default mapper name.
1548 // This matches the syntax: <type-spec> :: <variable-name>
1549 if (DerivedTypeSpec * derived{std::get_if<DerivedTypeSpec>(&typeSpec.u)}) {
1550 return OmpMapperSpecifier{
1551 std::get<Name>(derived->t).ToString() + llvm::omp::OmpDefaultMapperName,
1552 std::move(typeSpec), std::move(varName)};
1553 }
1554 return OmpMapperSpecifier{std::string("omp.default.mapper"),
1555 std::move(typeSpec), std::move(varName)};
1556}
1557
1558// mapper-specifier
1559TYPE_PARSER(applyFunction<OmpMapperSpecifier>(ConstructOmpMapperSpecifier,
1560 maybe(name / ":" / !":"_tok), typeSpec / "::", name))
1561
1562// OpenMP 5.2: 5.8.8 Declare Mapper Construct
1563TYPE_PARSER(sourced(
1564 construct<OpenMPDeclareMapperConstruct>(verbatim("DECLARE MAPPER"_tok),
1565 parenthesized(Parser<OmpMapperSpecifier>{}), Parser<OmpClauseList>{})))
1566
1567TYPE_PARSER(construct<OmpReductionCombiner>(Parser<AssignmentStmt>{}) ||
1568 construct<OmpReductionCombiner>(Parser<FunctionReference>{}))
1569
1570// 2.13.2 OMP CRITICAL
1571TYPE_PARSER(startOmpLine >>
1572 sourced(construct<OmpEndCriticalDirective>(
1573 verbatim("END CRITICAL"_tok), maybe(parenthesized(name)))) /
1574 endOmpLine)
1575TYPE_PARSER(sourced(construct<OmpCriticalDirective>(verbatim("CRITICAL"_tok),
1576 maybe(parenthesized(name)), Parser<OmpClauseList>{})) /
1577 endOmpLine)
1578
1579TYPE_PARSER(construct<OpenMPCriticalConstruct>(
1580 Parser<OmpCriticalDirective>{}, block, Parser<OmpEndCriticalDirective>{}))
1581
1582TYPE_PARSER(sourced(construct<OmpDispatchDirective>(
1583 verbatim("DISPATCH"_tok), Parser<OmpClauseList>{})))
1584
1585TYPE_PARSER(
1586 construct<OmpEndDispatchDirective>(startOmpLine >> "END DISPATCH"_tok))
1587
1588TYPE_PARSER(sourced(construct<OpenMPDispatchConstruct>(
1589 Parser<OmpDispatchDirective>{} / endOmpLine, block,
1590 maybe(Parser<OmpEndDispatchDirective>{} / endOmpLine))))
1591
1592// 2.11.3 Executable Allocate directive
1593TYPE_PARSER(
1594 sourced(construct<OpenMPExecutableAllocate>(verbatim("ALLOCATE"_tok),
1595 maybe(parenthesized(Parser<OmpObjectList>{})), Parser<OmpClauseList>{},
1596 maybe(nonemptyList(Parser<OpenMPDeclarativeAllocate>{})) / endOmpLine,
1597 statement(allocateStmt))))
1598
1599// 6.7 Allocators construct [OpenMP 5.2]
1600// allocators-construct -> ALLOCATORS [allocate-clause [,]]
1601// allocate-stmt
1602// [omp-end-allocators-construct]
1603TYPE_PARSER(sourced(construct<OpenMPAllocatorsConstruct>(
1604 verbatim("ALLOCATORS"_tok), Parser<OmpClauseList>{} / endOmpLine,
1605 statement(allocateStmt), maybe(Parser<OmpEndAllocators>{} / endOmpLine))))
1606
1607TYPE_PARSER(construct<OmpEndAllocators>(startOmpLine >> "END ALLOCATORS"_tok))
1608
1609// 2.8.2 Declare Simd construct
1610TYPE_PARSER(
1611 sourced(construct<OpenMPDeclareSimdConstruct>(verbatim("DECLARE SIMD"_tok),
1612 maybe(parenthesized(name)), Parser<OmpClauseList>{})))
1613
1614// 2.4 Requires construct
1615TYPE_PARSER(sourced(construct<OpenMPRequiresConstruct>(
1616 verbatim("REQUIRES"_tok), Parser<OmpClauseList>{})))
1617
1618// 2.15.2 Threadprivate directive
1619TYPE_PARSER(sourced(construct<OpenMPThreadprivate>(
1620 verbatim("THREADPRIVATE"_tok), parenthesized(Parser<OmpObjectList>{}))))
1621
1622// 2.11.3 Declarative Allocate directive
1623TYPE_PARSER(
1624 sourced(construct<OpenMPDeclarativeAllocate>(verbatim("ALLOCATE"_tok),
1625 parenthesized(Parser<OmpObjectList>{}), Parser<OmpClauseList>{})) /
1626 lookAhead(endOmpLine / !statement(allocateStmt)))
1627
1628// Assumes Construct
1629TYPE_PARSER(sourced(construct<OpenMPDeclarativeAssumes>(
1630 verbatim("ASSUMES"_tok), Parser<OmpClauseList>{})))
1631
1632// Declarative constructs
1633TYPE_PARSER(
1634 startOmpLine >> withMessage("expected OpenMP construct"_err_en_US,
1635 sourced(construct<OpenMPDeclarativeConstruct>(
1636 Parser<OpenMPDeclarativeAssumes>{}) ||
1637 construct<OpenMPDeclarativeConstruct>(
1638 Parser<OpenMPDeclareReductionConstruct>{}) ||
1639 construct<OpenMPDeclarativeConstruct>(
1640 Parser<OpenMPDeclareMapperConstruct>{}) ||
1641 construct<OpenMPDeclarativeConstruct>(
1642 Parser<OpenMPDeclareSimdConstruct>{}) ||
1643 construct<OpenMPDeclarativeConstruct>(
1644 Parser<OpenMPDeclareTargetConstruct>{}) ||
1645 construct<OpenMPDeclarativeConstruct>(
1646 Parser<OmpDeclareVariantDirective>{}) ||
1647 construct<OpenMPDeclarativeConstruct>(
1648 Parser<OpenMPDeclarativeAllocate>{}) ||
1649 construct<OpenMPDeclarativeConstruct>(
1650 Parser<OpenMPRequiresConstruct>{}) ||
1651 construct<OpenMPDeclarativeConstruct>(
1652 Parser<OpenMPThreadprivate>{}) ||
1653 construct<OpenMPDeclarativeConstruct>(
1654 Parser<OpenMPUtilityConstruct>{}) ||
1655 construct<OpenMPDeclarativeConstruct>(
1656 Parser<OmpMetadirectiveDirective>{})) /
1657 endOmpLine))
1658
1659// Assume Construct
1660TYPE_PARSER(sourced(construct<OmpAssumeDirective>(
1661 verbatim("ASSUME"_tok), Parser<OmpClauseList>{})))
1662
1663TYPE_PARSER(sourced(construct<OmpEndAssumeDirective>(
1664 verbatim(startOmpLine >> "END ASSUME"_tok))))
1665
1666TYPE_PARSER(sourced(
1667 construct<OpenMPAssumeConstruct>(Parser<OmpAssumeDirective>{} / endOmpLine,
1668 block, maybe(Parser<OmpEndAssumeDirective>{} / endOmpLine))))
1669
1670// Block Construct
1671TYPE_PARSER(construct<OpenMPBlockConstruct>(
1672 Parser<OmpBeginBlockDirective>{} / endOmpLine, block,
1673 Parser<OmpEndBlockDirective>{} / endOmpLine))
1674
1675// OMP SECTIONS Directive
1676TYPE_PARSER(construct<OmpSectionsDirective>(first(
1677 "SECTIONS" >> pure(llvm::omp::Directive::OMPD_sections),
1678 "PARALLEL SECTIONS" >> pure(llvm::omp::Directive::OMPD_parallel_sections))))
1679
1680// OMP BEGIN and END SECTIONS Directive
1681TYPE_PARSER(sourced(construct<OmpBeginSectionsDirective>(
1682 sourced(Parser<OmpSectionsDirective>{}), Parser<OmpClauseList>{})))
1683TYPE_PARSER(
1684 startOmpLine >> sourced(construct<OmpEndSectionsDirective>(
1685 sourced("END"_tok >> Parser<OmpSectionsDirective>{}),
1686 Parser<OmpClauseList>{})))
1687
1688// OMP SECTION-BLOCK
1689
1690TYPE_PARSER(construct<OpenMPSectionConstruct>(block))
1691
1692TYPE_PARSER(maybe(startOmpLine >> "SECTION"_tok / endOmpLine) >>
1693 construct<OmpSectionBlocks>(nonemptySeparated(
1694 construct<OpenMPConstruct>(sourced(Parser<OpenMPSectionConstruct>{})),
1695 startOmpLine >> "SECTION"_tok / endOmpLine)))
1696
1697// OMP SECTIONS (OpenMP 5.0 - 2.8.1), PARALLEL SECTIONS (OpenMP 5.0 - 2.13.3)
1698TYPE_PARSER(construct<OpenMPSectionsConstruct>(
1699 Parser<OmpBeginSectionsDirective>{} / endOmpLine,
1700 Parser<OmpSectionBlocks>{}, Parser<OmpEndSectionsDirective>{} / endOmpLine))
1701
1702TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,
1703 startOmpLine >>
1704 withMessage("expected OpenMP construct"_err_en_US,
1705 first(construct<OpenMPConstruct>(Parser<OpenMPSectionsConstruct>{}),
1706 construct<OpenMPConstruct>(Parser<OpenMPLoopConstruct>{}),
1707 construct<OpenMPConstruct>(Parser<OpenMPBlockConstruct>{}),
1708 // OpenMPBlockConstruct is attempted before
1709 // OpenMPStandaloneConstruct to resolve !$OMP ORDERED
1710 construct<OpenMPConstruct>(Parser<OpenMPStandaloneConstruct>{}),
1711 construct<OpenMPConstruct>(Parser<OpenMPAtomicConstruct>{}),
1712 construct<OpenMPConstruct>(Parser<OpenMPUtilityConstruct>{}),
1713 construct<OpenMPConstruct>(Parser<OpenMPDispatchConstruct>{}),
1714 construct<OpenMPConstruct>(Parser<OpenMPExecutableAllocate>{}),
1715 construct<OpenMPConstruct>(Parser<OpenMPAllocatorsConstruct>{}),
1716 construct<OpenMPConstruct>(Parser<OpenMPDeclarativeAllocate>{}),
1717 construct<OpenMPConstruct>(Parser<OpenMPAssumeConstruct>{}),
1718 construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}))))
1719
1720// END OMP Block directives
1721TYPE_PARSER(
1722 startOmpLine >> sourced(construct<OmpEndBlockDirective>(
1723 sourced("END"_tok >> Parser<OmpBlockDirective>{}),
1724 Parser<OmpClauseList>{})))
1725
1726// END OMP Loop directives
1727TYPE_PARSER(
1728 startOmpLine >> sourced(construct<OmpEndLoopDirective>(
1729 sourced("END"_tok >> Parser<OmpLoopDirective>{}),
1730 Parser<OmpClauseList>{})))
1731
1732TYPE_PARSER(construct<OpenMPLoopConstruct>(
1733 Parser<OmpBeginLoopDirective>{} / endOmpLine))
1734} // namespace Fortran::parser
1735

source code of flang/lib/Parser/openmp-parsers.cpp