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/semantics.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/StringRef.h"
18#include "llvm/Frontend/OpenMP/OMP.h"
19
20#include <cassert>
21#include <map>
22#include <memory>
23#include <optional>
24#include <variant>
25
26namespace Fortran::semantics {
27
28// Ref: [5.2:58]
29//
30// Syntactic properties for Clauses, Arguments and Modifiers
31//
32// Inverse properties:
33// not Required -> Optional
34// not Unique -> Repeatable
35// not Exclusive -> Compatible
36// not Ultimate -> Free
37//
38// Clause defaults: Optional, Repeatable, Compatible, Free
39// Argument defaults: Required, Unique, Compatible, Free
40// Modifier defaults: Optional, Unique, Compatible, Free
41//
42// ---
43// Each modifier is used as either pre-modifier (i.e. modifier: item),
44// or post-modifier (i.e. item: modifier). The default is pre-.
45// Add an additional property that reflects the type of modifier.
46
47ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post)
48using OmpProperties = common::EnumSet<OmpProperty, OmpProperty_enumSize>;
49using OmpClauses =
50 common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;
51
53 // Modifier name for use in diagnostic messages.
54 const OmpProperties &props(unsigned version) const;
55 const OmpClauses &clauses(unsigned version) const;
56 unsigned since(llvm::omp::Clause id) const;
57
58 const llvm::StringRef name;
59 // Version-dependent properties of the modifier.
60 const std::map<unsigned, OmpProperties> props_;
61 // Version-dependent set of clauses to which the modifier can apply.
62 const std::map<unsigned, OmpClauses> clauses_;
63};
64
65template <typename SpecificTy> const OmpModifierDescriptor &OmpGetDescriptor();
66
67#define DECLARE_DESCRIPTOR(name) \
68 template <> const OmpModifierDescriptor &OmpGetDescriptor<name>()
69
70DECLARE_DESCRIPTOR(parser::OmpAlignment);
71DECLARE_DESCRIPTOR(parser::OmpAlignModifier);
72DECLARE_DESCRIPTOR(parser::OmpAllocatorComplexModifier);
73DECLARE_DESCRIPTOR(parser::OmpAllocatorSimpleModifier);
74DECLARE_DESCRIPTOR(parser::OmpAlwaysModifier);
75DECLARE_DESCRIPTOR(parser::OmpAutomapModifier);
76DECLARE_DESCRIPTOR(parser::OmpChunkModifier);
77DECLARE_DESCRIPTOR(parser::OmpCloseModifier);
78DECLARE_DESCRIPTOR(parser::OmpContextSelector);
79DECLARE_DESCRIPTOR(parser::OmpDeleteModifier);
80DECLARE_DESCRIPTOR(parser::OmpDependenceType);
81DECLARE_DESCRIPTOR(parser::OmpDeviceModifier);
82DECLARE_DESCRIPTOR(parser::OmpDirectiveNameModifier);
83DECLARE_DESCRIPTOR(parser::OmpExpectation);
84DECLARE_DESCRIPTOR(parser::OmpInteropPreference);
85DECLARE_DESCRIPTOR(parser::OmpInteropType);
86DECLARE_DESCRIPTOR(parser::OmpIterator);
87DECLARE_DESCRIPTOR(parser::OmpLastprivateModifier);
88DECLARE_DESCRIPTOR(parser::OmpLinearModifier);
89DECLARE_DESCRIPTOR(parser::OmpMapper);
90DECLARE_DESCRIPTOR(parser::OmpMapType);
91DECLARE_DESCRIPTOR(parser::OmpMapTypeModifier);
92DECLARE_DESCRIPTOR(parser::OmpOrderModifier);
93DECLARE_DESCRIPTOR(parser::OmpOrderingModifier);
94DECLARE_DESCRIPTOR(parser::OmpPrescriptiveness);
95DECLARE_DESCRIPTOR(parser::OmpPresentModifier);
96DECLARE_DESCRIPTOR(parser::OmpReductionIdentifier);
97DECLARE_DESCRIPTOR(parser::OmpReductionModifier);
98DECLARE_DESCRIPTOR(parser::OmpRefModifier);
99DECLARE_DESCRIPTOR(parser::OmpSelfModifier);
100DECLARE_DESCRIPTOR(parser::OmpStepComplexModifier);
101DECLARE_DESCRIPTOR(parser::OmpStepSimpleModifier);
102DECLARE_DESCRIPTOR(parser::OmpTaskDependenceType);
103DECLARE_DESCRIPTOR(parser::OmpVariableCategory);
104DECLARE_DESCRIPTOR(parser::OmpxHoldModifier);
105
106#undef DECLARE_DESCRIPTOR
107
108// Explanation of terminology:
109//
110// A typical clause with modifier[s] looks like this (with parts that are
111// not relevant here removed):
112// struct OmpSomeClause {
113// struct Modifier {
114// using Variant = std::variant<Specific1, Specific2...>;
115// Variant u;
116// };
117// std::tuple<std::optional<std::list<Modifier>>, ...> t;
118// };
119//
120// The Specific1, etc. refer to parser classes that represent modifiers,
121// e.g. OmpIterator or OmpTaskDependenceType. The Variant type contains
122// all modifiers that are allowed for a given clause. The Modifier class
123// is there to wrap the variant into the form that the parse tree visitor
124// expects, i.e. with traits, member "u", etc.
125//
126// To avoid ambiguities with the word "modifier" (e.g. is it "any modifier",
127// or "this specific modifier"?), the following code uses different terms:
128//
129// - UnionTy: refers to the nested "Modifier" class, i.e.
130// "OmpSomeClause::Modifier" in the example above.
131// - SpecificTy: refers to any of the alternatives, i.e. "Specific1" or
132// "Specific2".
133
134template <typename UnionTy>
135const OmpModifierDescriptor &OmpGetDescriptor(const UnionTy &modifier) {
136 return common::visit(
137 [](auto &&m) -> decltype(auto) {
138 using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
139 return OmpGetDescriptor<SpecificTy>();
140 },
141 modifier.u);
142}
143
147template <typename ClauseTy>
148const std::optional<std::list<typename ClauseTy::Modifier>> &OmpGetModifiers(
149 const ClauseTy &clause) {
150 using UnionTy = typename ClauseTy::Modifier;
151 return std::get<std::optional<std::list<UnionTy>>>(clause.t);
152}
153
154namespace detail {
161template <typename SpecificTy, typename UnionTy>
162typename std::list<UnionTy>::const_iterator findInRange(
163 typename std::list<UnionTy>::const_iterator begin,
164 typename std::list<UnionTy>::const_iterator end) {
165 for (auto it{begin}; it != end; ++it) {
166 if (std::holds_alternative<SpecificTy>(it->u)) {
167 return it;
168 }
169 }
170 return end;
171}
172} // namespace detail
173
177template <typename SpecificTy, typename UnionTy>
178const SpecificTy *OmpGetUniqueModifier(
179 const std::optional<std::list<UnionTy>> &modifiers) {
180 const SpecificTy *found{nullptr};
181 if (modifiers) {
182 auto end{modifiers->cend()};
183 auto at{detail::findInRange<SpecificTy, UnionTy>(modifiers->cbegin(), end)};
184 if (at != end) {
185 found = &std::get<SpecificTy>(at->u);
186 }
187 }
188 return found;
189}
190
191template <typename SpecificTy> struct OmpSpecificModifierIterator {
192 using VectorTy = std::vector<const SpecificTy *>;
193 OmpSpecificModifierIterator(
194 std::shared_ptr<VectorTy> list, typename VectorTy::const_iterator where)
195 : specificList(list), at(where) {}
196
197 OmpSpecificModifierIterator &operator++() {
198 ++at;
199 return *this;
200 }
201 // OmpSpecificModifierIterator &operator++(int);
202 OmpSpecificModifierIterator &operator--() {
203 --at;
204 return *this;
205 }
206 // OmpSpecificModifierIterator &operator--(int);
207
208 const SpecificTy *operator*() const { return *at; }
209 bool operator==(const OmpSpecificModifierIterator &other) const {
210 assert(specificList.get() == other.specificList.get() &&
211 "comparing unrelated iterators");
212 return at == other.at;
213 }
214 bool operator!=(const OmpSpecificModifierIterator &other) const {
215 return !(*this == other);
216 }
217
218private:
219 std::shared_ptr<VectorTy> specificList;
220 typename VectorTy::const_iterator at;
221};
222
223template <typename SpecificTy, typename UnionTy>
224llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
225OmpGetRepeatableModifier(const std::optional<std::list<UnionTy>> &modifiers) {
226 using VectorTy = std::vector<const SpecificTy *>;
227 std::shared_ptr<VectorTy> items(new VectorTy);
228 if (modifiers) {
229 for (auto &m : *modifiers) {
230 if (auto *s = std::get_if<SpecificTy>(&m.u)) {
231 items->push_back(s);
232 }
233 }
234 }
235 return llvm::iterator_range(
236 OmpSpecificModifierIterator(items, items->begin()),
237 OmpSpecificModifierIterator(items, items->end()));
238}
239
240// Attempt to prevent creating a range based on an expiring modifier list.
241template <typename SpecificTy, typename UnionTy>
242llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
243OmpGetRepeatableModifier(std::optional<std::list<UnionTy>> &&) = delete;
244
245template <typename SpecificTy, typename UnionTy>
246Fortran::parser::CharBlock OmpGetModifierSource(
247 const std::optional<std::list<UnionTy>> &modifiers,
248 const SpecificTy *specific) {
249 if (!modifiers || !specific) {
250 return Fortran::parser::CharBlock{};
251 }
252 for (auto &m : *modifiers) {
253 if (std::get_if<SpecificTy>(&m.u) == specific) {
254 return m.source;
255 }
256 }
257 llvm_unreachable("`specific` must be a member of `modifiers`");
258}
259
260namespace detail {
261template <typename T> constexpr const T *make_nullptr() {
262 return static_cast<const T *>(nullptr);
263}
264
266template <typename UnionTy>
267bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
268 llvm::omp::Clause id, parser::CharBlock clauseSource,
269 SemanticsContext &semaCtx) {
270 if (!modifiers) {
271 return true;
272 }
273 unsigned version{semaCtx.langOptions().OpenMPVersion};
274 bool result{true};
275 for (auto &m : *modifiers) {
276 const OmpModifierDescriptor &desc{OmpGetDescriptor(m)};
277 unsigned since{desc.since(id)};
278 if (since == ~0u) {
279 // This shouldn't really happen, but have it just in case.
280 semaCtx.Say(m.source,
281 "'%s' modifier is not supported on %s clause"_err_en_US,
282 desc.name.str(),
283 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
284 } else if (version < since) {
285 semaCtx.Say(m.source,
286 "'%s' modifier is not supported in OpenMP v%d.%d, try -fopenmp-version=%d"_warn_en_US,
287 desc.name.str(), version / 10, version % 10, since);
288 result = false;
289 }
290 }
291 return result;
292}
293
298template <typename SpecificTy, typename UnionTy>
299bool verifyIfRequired(const SpecificTy *,
300 const std::optional<std::list<UnionTy>> &modifiers,
301 parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
302 unsigned version{semaCtx.langOptions().OpenMPVersion};
303 const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
304 if (!desc.props(version).test(OmpProperty::Required)) {
305 // If the modifier is not required, there is nothing to do.
306 return true;
307 }
308 bool present{modifiers.has_value()};
309 present = present && llvm::any_of(*modifiers, [](auto &&m) {
310 return std::holds_alternative<SpecificTy>(m.u);
311 });
312 if (!present) {
313 semaCtx.Say(
314 clauseSource, "'%s' modifier is required"_err_en_US, desc.name.str());
315 }
316 return present;
317}
318
322template <typename UnionTy, size_t... Idxs>
323bool verifyRequiredPack(const std::optional<std::list<UnionTy>> &modifiers,
324 parser::CharBlock clauseSource, SemanticsContext &semaCtx,
325 std::integer_sequence<size_t, Idxs...>) {
326 using VariantTy = typename UnionTy::Variant;
327 return (verifyIfRequired(
328 make_nullptr<std::variant_alternative_t<Idxs, VariantTy>>(),
329 modifiers, clauseSource, semaCtx) &&
330 ...);
331}
332
335template <typename UnionTy>
336bool verifyRequired(const std::optional<std::list<UnionTy>> &modifiers,
337 llvm::omp::Clause id, parser::CharBlock clauseSource,
338 SemanticsContext &semaCtx) {
339 using VariantTy = typename UnionTy::Variant;
340 return verifyRequiredPack(modifiers, clauseSource, semaCtx,
341 std::make_index_sequence<std::variant_size_v<VariantTy>>{});
342}
343
348template <typename UnionTy, typename SpecificTy>
349bool verifyIfUnique(const SpecificTy *,
350 typename std::list<UnionTy>::const_iterator specific,
351 typename std::list<UnionTy>::const_iterator end,
352 SemanticsContext &semaCtx) {
353 // `specific` is the location of the modifier of type SpecificTy.
354 assert(specific != end && "`specific` must be a valid location");
355
356 unsigned version{semaCtx.langOptions().OpenMPVersion};
357 const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
358 // Ultimate implies Unique.
359 if (!desc.props(version).test(OmpProperty::Unique) &&
360 !desc.props(version).test(OmpProperty::Ultimate)) {
361 return true;
362 }
363 if (std::next(specific) != end) {
364 auto next{
365 detail::findInRange<SpecificTy, UnionTy>(std::next(specific), end)};
366 if (next != end) {
367 semaCtx.Say(next->source,
368 "'%s' modifier cannot occur multiple times"_err_en_US,
369 desc.name.str());
370 }
371 }
372 return true;
373}
374
377template <typename UnionTy>
378bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
379 llvm::omp::Clause id, parser::CharBlock clauseSource,
380 SemanticsContext &semaCtx) {
381 if (!modifiers) {
382 return true;
383 }
384 bool result{true};
385 for (auto it{modifiers->cbegin()}, end{modifiers->cend()}; it != end; ++it) {
386 result = common::visit(
387 [&](auto &&m) {
388 return verifyIfUnique<UnionTy>(&m, it, end, semaCtx);
389 },
390 it->u) &&
391 result;
392 }
393 return result;
394}
395
398template <typename UnionTy>
399bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
400 llvm::omp::Clause id, parser::CharBlock clauseSource,
401 SemanticsContext &semaCtx) {
402 if (!modifiers || modifiers->size() <= 1) {
403 return true;
404 }
405 unsigned version{semaCtx.langOptions().OpenMPVersion};
406 bool result{true};
407 auto first{modifiers->cbegin()};
408 auto last{std::prev(modifiers->cend())};
409
410 // Any item that has the Ultimate property has to be either at the back
411 // or at the front of the list (depending on whether it's a pre- or a post-
412 // modifier).
413 // Walk over the list, and if a given item has the Ultimate property but is
414 // not at the right position, mark it as an error.
415 for (auto it{first}, end{modifiers->cend()}; it != end; ++it) {
416 result =
417 common::visit(
418 [&](auto &&m) {
419 using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
420 const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
421 auto &props{desc.props(version)};
422
423 if (props.test(OmpProperty::Ultimate)) {
424 bool isPre = !props.test(OmpProperty::Post);
425 if (it == (isPre ? last : first)) {
426 // Skip, since this is the correct place for this modifier.
427 return true;
428 }
429 llvm::StringRef where{isPre ? "last" : "first"};
430 semaCtx.Say(it->source,
431 "'%s' should be the %s modifier"_err_en_US, desc.name.str(),
432 where.str());
433 return false;
434 }
435 return true;
436 },
437 it->u) &&
438 result;
439 }
440 return result;
441}
442
445template <typename UnionTy>
446bool verifyExclusive(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 const UnionTy &front{modifiers->front()};
454 const OmpModifierDescriptor &frontDesc{OmpGetDescriptor(front)};
455
456 auto second{std::next(modifiers->cbegin())};
457 auto end{modifiers->end()};
458
459 auto emitErrorMessage{[&](const UnionTy &excl, const UnionTy &other) {
460 const OmpModifierDescriptor &descExcl{OmpGetDescriptor(excl)};
461 const OmpModifierDescriptor &descOther{OmpGetDescriptor(other)};
462 parser::MessageFormattedText txt(
463 "An exclusive '%s' modifier cannot be specified together with a modifier of a different type"_err_en_US,
464 descExcl.name.str());
465 parser::Message message(excl.source, txt);
466 message.Attach(
467 other.source, "'%s' provided here"_en_US, descOther.name.str());
468 semaCtx.Say(std::move(message));
469 }};
470
471 if (frontDesc.props(version).test(OmpProperty::Exclusive)) {
472 // If the first item has the Exclusive property, then check if there is
473 // another item in the rest of the list with a different SpecificTy as
474 // the alternative, and mark it as an error. This allows multiple Exclusive
475 // items to coexist as long as they hold the same SpecificTy.
476 bool result{true};
477 size_t frontIndex{front.u.index()};
478 for (auto it{second}; it != end; ++it) {
479 if (it->u.index() != frontIndex) {
480 emitErrorMessage(front, *it);
481 result = false;
482 break;
483 }
484 }
485 return result;
486 } else {
487 // If the first item does not have the Exclusive property, then check
488 // if there is an item in the rest of the list that is Exclusive, and
489 // mark it as an error if so.
490 bool result{true};
491 for (auto it{second}; it != end; ++it) {
492 const OmpModifierDescriptor &desc{OmpGetDescriptor(*it)};
493 if (desc.props(version).test(OmpProperty::Exclusive)) {
494 emitErrorMessage(*it, front);
495 result = false;
496 break;
497 }
498 }
499 return result;
500 }
501}
502} // namespace detail
503
504template <typename ClauseTy>
505bool OmpVerifyModifiers(const ClauseTy &clause, llvm::omp::Clause id,
506 parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
507 auto &modifiers{OmpGetModifiers(clause)};
508 bool results[]{//
509 detail::verifyVersions(modifiers, id, clauseSource, semaCtx),
510 detail::verifyRequired(modifiers, id, clauseSource, semaCtx),
511 detail::verifyUnique(modifiers, id, clauseSource, semaCtx),
512 detail::verifyUltimate(modifiers, id, clauseSource, semaCtx),
513 detail::verifyExclusive(modifiers, id, clauseSource, semaCtx)};
514 return llvm::all_of(results, [](bool x) { return x; });
515}
516} // namespace Fortran::semantics
517
518#endif // FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
Definition semantics.h:67
Definition parse-tree.h:3521
Definition parse-tree.h:3756
Definition parse-tree.h:3748
Definition parse-tree.h:3784
Definition parse-tree.h:3794
Definition parse-tree.h:3805
Definition parse-tree.h:3818
Definition parse-tree.h:3830
Definition parse-tree.h:3851
Definition parse-tree.h:3860
Definition parse-tree.h:3878
Definition parse-tree.h:3893
Definition parse-tree.h:3911
Definition parse-tree.h:3920
Definition parse-tree.h:3942
Definition parse-tree.h:3950
Definition parse-tree.h:3959
Definition parse-tree.h:3994
Definition parse-tree.h:3981
Definition parse-tree.h:3968
Definition parse-tree.h:4018
Definition parse-tree.h:4009
Definition parse-tree.h:4028
Definition parse-tree.h:4041
Definition parse-tree.h:4050
Definition parse-tree.h:4060
Definition parse-tree.h:4070
Definition parse-tree.h:4079
Definition parse-tree.h:4087
Definition parse-tree.h:4097
Definition parse-tree.h:4108
Definition parse-tree.h:4121
Definition openmp-modifiers.h:52
Definition openmp-modifiers.h:191