FLANG
openmp-modifiers.h
1//===-- flang/lib/Semantics/openmp-modifiers.h ------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
10#define FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
11
12#include "flang/Common/enum-set.h"
13#include "flang/Parser/characters.h"
14#include "flang/Parser/parse-tree.h"
15#include "flang/Semantics/openmp-utils.h"
16#include "flang/Semantics/semantics.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/ADT/StringRef.h"
19#include "llvm/Frontend/OpenMP/OMP.h"
20#include "llvm/Frontend/OpenMP/OMPDescriptors.h"
21
22#include <cassert>
23#include <map>
24#include <memory>
25#include <optional>
26#include <variant>
27
28namespace Fortran::semantics {
29
30// Ref: [5.2:58]
31//
32// Syntactic properties for Clauses, Arguments and Modifiers
33//
34// Inverse properties:
35// not Required -> Optional
36// not Unique -> Repeatable
37// not Exclusive -> Compatible
38// not Ultimate -> Free
39//
40// Clause defaults: Optional, Repeatable, Compatible, Free
41// Argument defaults: Required, Unique, Compatible, Free
42// Modifier defaults: Optional, Unique, Compatible, Free
43//
44template <typename SpecificTy> llvm::omp::Modifier OmpGetModifierId();
45template <typename SpecificTy>
46const llvm::omp::descriptor::Modifier &OmpGetDescriptor();
47
48#define DECLARE_DESCRIPTOR(name, id) \
49 template <> inline llvm::omp::Modifier OmpGetModifierId<name>() { \
50 return id; \
51 } \
52 template <> \
53 inline const llvm::omp::descriptor::Modifier &OmpGetDescriptor<name>() { \
54 return llvm::omp::getDescriptor(OmpGetModifierId<name>()); \
55 }
56
57DECLARE_DESCRIPTOR(parser::OmpAccessGroup, llvm::omp::Modifier::AccessGroup)
58DECLARE_DESCRIPTOR(parser::OmpAlignment, llvm::omp::Modifier::Alignment)
59DECLARE_DESCRIPTOR(parser::OmpAlignModifier, llvm::omp::Modifier::AlignModifier)
60DECLARE_DESCRIPTOR(parser::OmpAllocatorComplexModifier,
61 llvm::omp::Modifier::AllocatorComplexModifier)
62DECLARE_DESCRIPTOR(parser::OmpAllocatorSimpleModifier,
63 llvm::omp::Modifier::AllocatorSimpleModifier)
64DECLARE_DESCRIPTOR(
65 parser::OmpAlwaysModifier, llvm::omp::Modifier::AlwaysModifier)
66DECLARE_DESCRIPTOR(
67 parser::OmpAttachModifier, llvm::omp::Modifier::AttachModifier)
68DECLARE_DESCRIPTOR(
69 parser::OmpAutomapModifier, llvm::omp::Modifier::AutomapModifier)
70DECLARE_DESCRIPTOR(parser::OmpChunkModifier, llvm::omp::Modifier::ChunkModifier)
71DECLARE_DESCRIPTOR(parser::OmpCloseModifier, llvm::omp::Modifier::CloseModifier)
72DECLARE_DESCRIPTOR(
73 parser::OmpContextSelector, llvm::omp::Modifier::ContextSelector)
74DECLARE_DESCRIPTOR(
75 parser::OmpDeleteModifier, llvm::omp::Modifier::DeleteModifier)
76DECLARE_DESCRIPTOR(
77 parser::OmpDependenceType, llvm::omp::Modifier::DependenceType)
78DECLARE_DESCRIPTOR(
79 parser::OmpDepinfoModifier, llvm::omp::Modifier::DepinfoModifier)
80DECLARE_DESCRIPTOR(
81 parser::OmpDeviceModifier, llvm::omp::Modifier::DeviceModifier)
82DECLARE_DESCRIPTOR(parser::OmpDimsModifier, llvm::omp::Modifier::DimsModifier)
83DECLARE_DESCRIPTOR(parser::OmpDirectiveNameModifier,
84 llvm::omp::Modifier::DirectiveNameModifier)
85DECLARE_DESCRIPTOR(parser::OmpExpectation, llvm::omp::Modifier::Expectation)
86DECLARE_DESCRIPTOR(
87 parser::OmpFallbackModifier, llvm::omp::Modifier::FallbackModifier)
88DECLARE_DESCRIPTOR(parser::OmpInteropType, llvm::omp::Modifier::InteropType)
89DECLARE_DESCRIPTOR(parser::OmpIterator, llvm::omp::Modifier::Iterator)
90DECLARE_DESCRIPTOR(
91 parser::OmpLastprivateModifier, llvm::omp::Modifier::LastprivateModifier)
92DECLARE_DESCRIPTOR(
93 parser::OmpLinearModifier, llvm::omp::Modifier::LinearModifier)
94DECLARE_DESCRIPTOR(parser::OmpLinearStep, llvm::omp::Modifier::LinearStep)
95DECLARE_DESCRIPTOR(parser::OmpLoopModifier, llvm::omp::Modifier::LoopModifier)
96DECLARE_DESCRIPTOR(parser::OmpLowerBound, llvm::omp::Modifier::LowerBound)
97DECLARE_DESCRIPTOR(parser::OmpMapper, llvm::omp::Modifier::Mapper)
98DECLARE_DESCRIPTOR(parser::OmpMapType, llvm::omp::Modifier::MapType)
99DECLARE_DESCRIPTOR(
100 parser::OmpMapTypeModifier, llvm::omp::Modifier::MapTypeModifier)
101DECLARE_DESCRIPTOR(parser::OmpMemSpace, llvm::omp::Modifier::MemSpace)
102DECLARE_DESCRIPTOR(
103 parser::OmpMotionModifier, llvm::omp::Modifier::MotionModifier)
104DECLARE_DESCRIPTOR(parser::OmpOrderModifier, llvm::omp::Modifier::OrderModifier)
105DECLARE_DESCRIPTOR(
106 parser::OmpOrderingModifier, llvm::omp::Modifier::OrderingModifier)
107DECLARE_DESCRIPTOR(parser::OmpPreferType, llvm::omp::Modifier::PreferType)
108DECLARE_DESCRIPTOR(
109 parser::OmpPrescriptiveness, llvm::omp::Modifier::Prescriptiveness)
110DECLARE_DESCRIPTOR(
111 parser::OmpPresentModifier, llvm::omp::Modifier::PresentModifier)
112DECLARE_DESCRIPTOR(
113 parser::OmpReductionIdentifier, llvm::omp::Modifier::ReductionIdentifier)
114DECLARE_DESCRIPTOR(
115 parser::OmpReductionModifier, llvm::omp::Modifier::ReductionModifier)
116DECLARE_DESCRIPTOR(parser::OmpRefModifier, llvm::omp::Modifier::RefModifier)
117DECLARE_DESCRIPTOR(parser::OmpSelfModifier, llvm::omp::Modifier::SelfModifier)
118DECLARE_DESCRIPTOR(
119 parser::OmpStepComplexModifier, llvm::omp::Modifier::StepComplexModifier)
120DECLARE_DESCRIPTOR(
121 parser::OmpStepSimpleModifier, llvm::omp::Modifier::StepSimpleModifier)
122DECLARE_DESCRIPTOR(
123 parser::OmpTaskDependenceType, llvm::omp::Modifier::TaskDependenceType)
124DECLARE_DESCRIPTOR(parser::OmpTraitsArray, llvm::omp::Modifier::TraitsArray)
125DECLARE_DESCRIPTOR(
126 parser::OmpVariableCategory, llvm::omp::Modifier::VariableCategory)
127DECLARE_DESCRIPTOR(
128 parser::OmpxHoldModifier, llvm::omp::Modifier::OmpxHoldModifier)
129
130#undef DECLARE_DESCRIPTOR
131
132// Explanation of terminology:
133//
134// A typical clause with modifier[s] looks like this (with parts that are
135// not relevant here removed):
136// struct OmpSomeClause {
137// struct Modifier {
138// using Variant = std::variant<Specific1, Specific2...>;
139// Variant u;
140// };
141// std::tuple<std::optional<std::list<Modifier>>, ...> t;
142// };
143//
144// The Specific1, etc. refer to parser classes that represent modifiers,
145// e.g. OmpIterator or OmpTaskDependenceType. The Variant type contains
146// all modifiers that are allowed for a given clause. The Modifier class
147// is there to wrap the variant into the form that the parse tree visitor
148// expects, i.e. with traits, member "u", etc.
149//
150// To avoid ambiguities with the word "modifier" (e.g. is it "any modifier",
151// or "this specific modifier"?), the following code uses different terms:
152//
153// - UnionTy: refers to the nested "Modifier" class, i.e.
154// "OmpSomeClause::Modifier" in the example above.
155// - SpecificTy: refers to any of the alternatives, i.e. "Specific1" or
156// "Specific2".
157
158template <typename UnionTy>
159const llvm::omp::descriptor::Modifier &OmpGetDescriptor(
160 const UnionTy &modifier) {
161 return common::visit(
162 [](auto &&m) -> decltype(auto) {
163 using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
164 return OmpGetDescriptor<SpecificTy>();
165 },
166 modifier.u);
167}
168
172template <typename ClauseTy>
173const std::optional<std::list<typename ClauseTy::Modifier>> &OmpGetModifiers(
174 const ClauseTy &clause) {
175 using UnionTy = typename ClauseTy::Modifier;
176 return std::get<std::optional<std::list<UnionTy>>>(clause.t);
177}
178
179namespace detail {
186template <typename SpecificTy, typename UnionTy>
187typename std::list<UnionTy>::const_iterator findInRange(
188 typename std::list<UnionTy>::const_iterator begin,
189 typename std::list<UnionTy>::const_iterator end) {
190 for (auto it{begin}; it != end; ++it) {
191 if (std::holds_alternative<SpecificTy>(it->u)) {
192 return it;
193 }
194 }
195 return end;
196}
197} // namespace detail
198
202template <typename SpecificTy, typename UnionTy>
203const SpecificTy *OmpGetUniqueModifier(
204 const std::optional<std::list<UnionTy>> &modifiers) {
205 const SpecificTy *found{nullptr};
206 if (modifiers) {
207 auto end{modifiers->cend()};
208 auto at{detail::findInRange<SpecificTy, UnionTy>(modifiers->cbegin(), end)};
209 if (at != end) {
210 found = &std::get<SpecificTy>(at->u);
211 }
212 }
213 return found;
214}
215
216template <typename SpecificTy> struct OmpSpecificModifierIterator {
217 using VectorTy = std::vector<const SpecificTy *>;
218 OmpSpecificModifierIterator(
219 std::shared_ptr<VectorTy> list, typename VectorTy::const_iterator where)
220 : specificList(list), at(where) {}
221
222 OmpSpecificModifierIterator &operator++() {
223 ++at;
224 return *this;
225 }
226 // OmpSpecificModifierIterator &operator++(int);
227 OmpSpecificModifierIterator &operator--() {
228 --at;
229 return *this;
230 }
231 // OmpSpecificModifierIterator &operator--(int);
232
233 const SpecificTy *operator*() const { return *at; }
234 bool operator==(const OmpSpecificModifierIterator &other) const {
235 assert(specificList.get() == other.specificList.get() &&
236 "comparing unrelated iterators");
237 return at == other.at;
238 }
239 bool operator!=(const OmpSpecificModifierIterator &other) const {
240 return !(*this == other);
241 }
242
243private:
244 std::shared_ptr<VectorTy> specificList;
245 typename VectorTy::const_iterator at;
246};
247
248template <typename SpecificTy, typename UnionTy>
249llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
250OmpGetRepeatableModifier(const std::optional<std::list<UnionTy>> &modifiers) {
251 using VectorTy = std::vector<const SpecificTy *>;
252 std::shared_ptr<VectorTy> items(new VectorTy);
253 if (modifiers) {
254 for (auto &m : *modifiers) {
255 if (auto *s = std::get_if<SpecificTy>(&m.u)) {
256 items->push_back(s);
257 }
258 }
259 }
260 return llvm::iterator_range(
261 OmpSpecificModifierIterator(items, items->begin()),
262 OmpSpecificModifierIterator(items, items->end()));
263}
264
265// Attempt to prevent creating a range based on an expiring modifier list.
266template <typename SpecificTy, typename UnionTy>
267llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
268OmpGetRepeatableModifier(std::optional<std::list<UnionTy>> &&) = delete;
269
270template <typename SpecificTy, typename UnionTy>
271Fortran::parser::CharBlock OmpGetModifierSource(
272 const std::optional<std::list<UnionTy>> &modifiers,
273 const SpecificTy *specific) {
274 if (!modifiers || !specific) {
275 return Fortran::parser::CharBlock{};
276 }
277 for (auto &m : *modifiers) {
278 if (std::get_if<SpecificTy>(&m.u) == specific) {
279 return m.source;
280 }
281 }
282 llvm_unreachable("`specific` must be a member of `modifiers`");
283}
284
285namespace detail {
286template <typename T> constexpr const T *make_nullptr() {
287 return static_cast<const T *>(nullptr);
288}
289
291template <typename UnionTy>
292bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
293 llvm::omp::Clause id, parser::CharBlock clauseSource,
294 SemanticsContext &semaCtx) {
295 if (!modifiers) {
296 return true;
297 }
298 unsigned version{semaCtx.langOptions().OpenMPVersion};
299 bool result{true};
300 for (auto &m : *modifiers) {
301 const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor(m)};
302 if (desc.getClauses(version).test(id)) {
303 continue;
304 }
305 // Find the next higher version that allows this modifier on this clause.
306 const auto &versions{desc.getVersions()};
307 unsigned since{~0u}, until{0u};
308 for (unsigned v : versions) {
309 if (desc.getClauses(v).test(id)) {
310 if (v < version) {
311 until = std::max(until, v);
312 } else if (v > version) {
313 since = std::min(since, v);
314 }
315 }
316 }
317 if (since == ~0u && until == 0u) {
318 // This shouldn't really happen, but have it just in case.
319 semaCtx.Say(m.source,
320 "'%s' modifier is not supported on %s clause"_err_en_US,
321 desc.getName().str(),
322 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
323 } else if (since != ~0u && version < since) {
324 semaCtx.Say(m.source,
325 "'%s' modifier is not supported in %s on %s clause, %s"_warn_en_US,
326 desc.getName().str(), omp::ThisVersion(version),
327 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)),
328 omp::TryVersion(since));
329 result = false;
330 } else if (until != 0u && version > until) {
331 semaCtx.Say(m.source,
332 "'%s' modifier is no longer supported in %s on %s clause"_warn_en_US,
333 desc.getName().str(), omp::ThisVersion(version),
334 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
335 result = false;
336 }
337 }
338 return result;
339}
340
345template <typename SpecificTy, typename UnionTy>
346bool verifyIfRequired(const SpecificTy *,
347 const std::optional<std::list<UnionTy>> &modifiers,
348 parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
349 unsigned version{semaCtx.langOptions().OpenMPVersion};
350 const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
351 if (!desc.getProperties(version).test(llvm::omp::Property::Required)) {
352 // If the modifier is not required, there is nothing to do.
353 return true;
354 }
355 bool present{modifiers.has_value()};
356 present = present && llvm::any_of(*modifiers, [](auto &&m) {
357 return std::holds_alternative<SpecificTy>(m.u);
358 });
359 if (!present) {
360 semaCtx.Say(clauseSource, "'%s' modifier is required"_err_en_US,
361 desc.getName().str());
362 }
363 return present;
364}
365
369template <typename UnionTy, size_t... Idxs>
370bool verifyRequiredPack(const std::optional<std::list<UnionTy>> &modifiers,
371 parser::CharBlock clauseSource, SemanticsContext &semaCtx,
372 std::integer_sequence<size_t, Idxs...>) {
373 using VariantTy = typename UnionTy::Variant;
374 return (verifyIfRequired(
375 make_nullptr<std::variant_alternative_t<Idxs, VariantTy>>(),
376 modifiers, clauseSource, semaCtx) &&
377 ...);
378}
379
382template <typename UnionTy>
383bool verifyRequired(const std::optional<std::list<UnionTy>> &modifiers,
384 llvm::omp::Clause id, parser::CharBlock clauseSource,
385 SemanticsContext &semaCtx) {
386 using VariantTy = typename UnionTy::Variant;
387 return verifyRequiredPack(modifiers, clauseSource, semaCtx,
388 std::make_index_sequence<std::variant_size_v<VariantTy>>{});
389}
390
395template <typename UnionTy, typename SpecificTy>
396bool verifyIfUnique(const SpecificTy *,
397 typename std::list<UnionTy>::const_iterator specific,
398 typename std::list<UnionTy>::const_iterator end,
399 SemanticsContext &semaCtx) {
400 // `specific` is the location of the modifier of type SpecificTy.
401 assert(specific != end && "`specific` must be a valid location");
402
403 unsigned version{semaCtx.langOptions().OpenMPVersion};
404 const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
405 // Ultimate implies Unique.
406 if (!desc.getProperties(version).test(llvm::omp::Property::Unique) &&
407 !desc.getProperties(version).test(llvm::omp::Property::Ultimate)) {
408 return true;
409 }
410 if (std::next(specific) != end) {
411 auto next{
412 detail::findInRange<SpecificTy, UnionTy>(std::next(specific), end)};
413 if (next != end) {
414 semaCtx.Say(next->source,
415 "'%s' modifier cannot occur multiple times"_err_en_US,
416 desc.getName().str());
417 }
418 }
419 return true;
420}
421
424template <typename UnionTy>
425bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
426 llvm::omp::Clause id, parser::CharBlock clauseSource,
427 SemanticsContext &semaCtx) {
428 if (!modifiers) {
429 return true;
430 }
431 bool result{true};
432 for (auto it{modifiers->cbegin()}, end{modifiers->cend()}; it != end; ++it) {
433 result = common::visit(
434 [&](auto &&m) {
435 return verifyIfUnique<UnionTy>(&m, it, end, semaCtx);
436 },
437 it->u) &&
438 result;
439 }
440 return result;
441}
442
445template <typename UnionTy>
446bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
447 llvm::omp::Clause id, parser::CharBlock clauseSource,
448 SemanticsContext &semaCtx) {
449 if (!modifiers || modifiers->size() <= 1) {
450 return true;
451 }
452 unsigned version{semaCtx.langOptions().OpenMPVersion};
453 bool result{true};
454 auto first{modifiers->cbegin()};
455 auto last{std::prev(modifiers->cend())};
456
457 // Any item that has the Ultimate property has to be either at the back
458 // or at the front of the list (depending on whether it's a pre- or a post-
459 // modifier).
460 // Walk over the list, and if a given item has the Ultimate property but is
461 // not at the right position, mark it as an error.
462 for (auto it{first}, end{modifiers->cend()}; it != end; ++it) {
463 result = common::visit(
464 [&](auto &&m) {
465 using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
466 const llvm::omp::descriptor::Modifier &desc{
467 OmpGetDescriptor<SpecificTy>()};
468 const auto &props{desc.getProperties(version)};
469
470 if (props.test(llvm::omp::Property::Ultimate)) {
471 bool isPre = !llvm::omp::getProperties(id, version)
472 .test(llvm::omp::Property::PostModified);
473 if (it == (isPre ? last : first)) {
474 // Skip, since this is the correct place for this
475 // modifier.
476 return true;
477 }
478 llvm::StringRef where{isPre ? "last" : "first"};
479 semaCtx.Say(it->source,
480 "'%s' should be the %s modifier"_err_en_US,
481 desc.getName().str(), where.str());
482 return false;
483 }
484 return true;
485 },
486 it->u) &&
487 result;
488 }
489 return result;
490}
491
494template <typename UnionTy>
495bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
496 llvm::omp::Clause id, parser::CharBlock clauseSource,
497 SemanticsContext &semaCtx) {
498 if (!modifiers || modifiers->size() <= 1) {
499 return true;
500 }
501 unsigned version{semaCtx.langOptions().OpenMPVersion};
502 const UnionTy &front{modifiers->front()};
503 const llvm::omp::descriptor::Modifier &frontDesc{OmpGetDescriptor(front)};
504
505 auto second{std::next(modifiers->cbegin())};
506 auto end{modifiers->end()};
507
508 auto emitErrorMessage{[&](const UnionTy &excl, const UnionTy &other) {
509 const llvm::omp::descriptor::Modifier &descExcl{OmpGetDescriptor(excl)};
510 const llvm::omp::descriptor::Modifier &descOther{OmpGetDescriptor(other)};
511 parser::MessageFormattedText txt(
512 "An exclusive '%s' modifier cannot be specified together with a modifier of a different type"_err_en_US,
513 descExcl.getName().str());
514 parser::Message message(excl.source, txt);
515 message.Attach(
516 other.source, "'%s' provided here"_en_US, descOther.getName().str());
517 semaCtx.Say(std::move(message));
518 }};
519
520 if (frontDesc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
521 // If the first item has the Exclusive property, then check if there is
522 // another item in the rest of the list with a different SpecificTy as
523 // the alternative, and mark it as an error. This allows multiple Exclusive
524 // items to coexist as long as they hold the same SpecificTy.
525 bool result{true};
526 size_t frontIndex{front.u.index()};
527 for (auto it{second}; it != end; ++it) {
528 if (it->u.index() != frontIndex) {
529 emitErrorMessage(front, *it);
530 result = false;
531 break;
532 }
533 }
534 return result;
535 } else {
536 // If the first item does not have the Exclusive property, then check
537 // if there is an item in the rest of the list that is Exclusive, and
538 // mark it as an error if so.
539 bool result{true};
540 for (auto it{second}; it != end; ++it) {
541 const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor(*it)};
542 if (desc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
543 emitErrorMessage(*it, front);
544 result = false;
545 break;
546 }
547 }
548 return result;
549 }
550}
551} // namespace detail
552
553template <typename ClauseTy>
554bool OmpVerifyModifiers(const ClauseTy &clause, llvm::omp::Clause id,
555 parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
556 auto &modifiers{OmpGetModifiers(clause)};
557 bool results[]{//
558 detail::verifyVersions(modifiers, id, clauseSource, semaCtx),
559 detail::verifyRequired(modifiers, id, clauseSource, semaCtx),
560 detail::verifyUnique(modifiers, id, clauseSource, semaCtx),
561 detail::verifyUltimate(modifiers, id, clauseSource, semaCtx),
562 detail::verifyExclusive(modifiers, id, clauseSource, semaCtx)};
563 return llvm::all_of(results, [](bool x) { return x; });
564}
565} // namespace Fortran::semantics
566
567#endif // FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
Definition semantics.h:67
Definition openmp-modifiers.h:216