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::OmpAccessGroup);
71DECLARE_DESCRIPTOR(parser::OmpAlignment);
72DECLARE_DESCRIPTOR(parser::OmpAlignModifier);
73DECLARE_DESCRIPTOR(parser::OmpAllocatorComplexModifier);
74DECLARE_DESCRIPTOR(parser::OmpAllocatorSimpleModifier);
75DECLARE_DESCRIPTOR(parser::OmpAlwaysModifier);
76DECLARE_DESCRIPTOR(parser::OmpAttachModifier);
77DECLARE_DESCRIPTOR(parser::OmpAutomapModifier);
78DECLARE_DESCRIPTOR(parser::OmpChunkModifier);
79DECLARE_DESCRIPTOR(parser::OmpCloseModifier);
80DECLARE_DESCRIPTOR(parser::OmpContextSelector);
81DECLARE_DESCRIPTOR(parser::OmpDeleteModifier);
82DECLARE_DESCRIPTOR(parser::OmpDependenceType);
83DECLARE_DESCRIPTOR(parser::OmpDepinfoModifier);
84DECLARE_DESCRIPTOR(parser::OmpDeviceModifier);
85DECLARE_DESCRIPTOR(parser::OmpDimsModifier);
86DECLARE_DESCRIPTOR(parser::OmpDirectiveNameModifier);
87DECLARE_DESCRIPTOR(parser::OmpExpectation);
88DECLARE_DESCRIPTOR(parser::OmpFallbackModifier);
89DECLARE_DESCRIPTOR(parser::OmpInteropType);
90DECLARE_DESCRIPTOR(parser::OmpIterator);
91DECLARE_DESCRIPTOR(parser::OmpLastprivateModifier);
92DECLARE_DESCRIPTOR(parser::OmpLinearModifier);
93DECLARE_DESCRIPTOR(parser::OmpLowerBound);
94DECLARE_DESCRIPTOR(parser::OmpMapper);
95DECLARE_DESCRIPTOR(parser::OmpMapType);
96DECLARE_DESCRIPTOR(parser::OmpMapTypeModifier);
97DECLARE_DESCRIPTOR(parser::OmpOrderModifier);
98DECLARE_DESCRIPTOR(parser::OmpOrderingModifier);
99DECLARE_DESCRIPTOR(parser::OmpPreferType);
100DECLARE_DESCRIPTOR(parser::OmpPrescriptiveness);
101DECLARE_DESCRIPTOR(parser::OmpPresentModifier);
102DECLARE_DESCRIPTOR(parser::OmpReductionIdentifier);
103DECLARE_DESCRIPTOR(parser::OmpReductionModifier);
104DECLARE_DESCRIPTOR(parser::OmpRefModifier);
105DECLARE_DESCRIPTOR(parser::OmpSelfModifier);
106DECLARE_DESCRIPTOR(parser::OmpStepComplexModifier);
107DECLARE_DESCRIPTOR(parser::OmpStepSimpleModifier);
108DECLARE_DESCRIPTOR(parser::OmpTaskDependenceType);
109DECLARE_DESCRIPTOR(parser::OmpVariableCategory);
110DECLARE_DESCRIPTOR(parser::OmpxHoldModifier);
111
112#undef DECLARE_DESCRIPTOR
113
114// Explanation of terminology:
115//
116// A typical clause with modifier[s] looks like this (with parts that are
117// not relevant here removed):
118// struct OmpSomeClause {
119// struct Modifier {
120// using Variant = std::variant<Specific1, Specific2...>;
121// Variant u;
122// };
123// std::tuple<std::optional<std::list<Modifier>>, ...> t;
124// };
125//
126// The Specific1, etc. refer to parser classes that represent modifiers,
127// e.g. OmpIterator or OmpTaskDependenceType. The Variant type contains
128// all modifiers that are allowed for a given clause. The Modifier class
129// is there to wrap the variant into the form that the parse tree visitor
130// expects, i.e. with traits, member "u", etc.
131//
132// To avoid ambiguities with the word "modifier" (e.g. is it "any modifier",
133// or "this specific modifier"?), the following code uses different terms:
134//
135// - UnionTy: refers to the nested "Modifier" class, i.e.
136// "OmpSomeClause::Modifier" in the example above.
137// - SpecificTy: refers to any of the alternatives, i.e. "Specific1" or
138// "Specific2".
139
140template <typename UnionTy>
141const OmpModifierDescriptor &OmpGetDescriptor(const UnionTy &modifier) {
142 return common::visit(
143 [](auto &&m) -> decltype(auto) {
144 using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
145 return OmpGetDescriptor<SpecificTy>();
146 },
147 modifier.u);
148}
149
153template <typename ClauseTy>
154const std::optional<std::list<typename ClauseTy::Modifier>> &OmpGetModifiers(
155 const ClauseTy &clause) {
156 using UnionTy = typename ClauseTy::Modifier;
157 return std::get<std::optional<std::list<UnionTy>>>(clause.t);
158}
159
160namespace detail {
167template <typename SpecificTy, typename UnionTy>
168typename std::list<UnionTy>::const_iterator findInRange(
169 typename std::list<UnionTy>::const_iterator begin,
170 typename std::list<UnionTy>::const_iterator end) {
171 for (auto it{begin}; it != end; ++it) {
172 if (std::holds_alternative<SpecificTy>(it->u)) {
173 return it;
174 }
175 }
176 return end;
177}
178} // namespace detail
179
183template <typename SpecificTy, typename UnionTy>
184const SpecificTy *OmpGetUniqueModifier(
185 const std::optional<std::list<UnionTy>> &modifiers) {
186 const SpecificTy *found{nullptr};
187 if (modifiers) {
188 auto end{modifiers->cend()};
189 auto at{detail::findInRange<SpecificTy, UnionTy>(modifiers->cbegin(), end)};
190 if (at != end) {
191 found = &std::get<SpecificTy>(at->u);
192 }
193 }
194 return found;
195}
196
197template <typename SpecificTy> struct OmpSpecificModifierIterator {
198 using VectorTy = std::vector<const SpecificTy *>;
199 OmpSpecificModifierIterator(
200 std::shared_ptr<VectorTy> list, typename VectorTy::const_iterator where)
201 : specificList(list), at(where) {}
202
203 OmpSpecificModifierIterator &operator++() {
204 ++at;
205 return *this;
206 }
207 // OmpSpecificModifierIterator &operator++(int);
208 OmpSpecificModifierIterator &operator--() {
209 --at;
210 return *this;
211 }
212 // OmpSpecificModifierIterator &operator--(int);
213
214 const SpecificTy *operator*() const { return *at; }
215 bool operator==(const OmpSpecificModifierIterator &other) const {
216 assert(specificList.get() == other.specificList.get() &&
217 "comparing unrelated iterators");
218 return at == other.at;
219 }
220 bool operator!=(const OmpSpecificModifierIterator &other) const {
221 return !(*this == other);
222 }
223
224private:
225 std::shared_ptr<VectorTy> specificList;
226 typename VectorTy::const_iterator at;
227};
228
229template <typename SpecificTy, typename UnionTy>
230llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
231OmpGetRepeatableModifier(const std::optional<std::list<UnionTy>> &modifiers) {
232 using VectorTy = std::vector<const SpecificTy *>;
233 std::shared_ptr<VectorTy> items(new VectorTy);
234 if (modifiers) {
235 for (auto &m : *modifiers) {
236 if (auto *s = std::get_if<SpecificTy>(&m.u)) {
237 items->push_back(s);
238 }
239 }
240 }
241 return llvm::iterator_range(
242 OmpSpecificModifierIterator(items, items->begin()),
243 OmpSpecificModifierIterator(items, items->end()));
244}
245
246// Attempt to prevent creating a range based on an expiring modifier list.
247template <typename SpecificTy, typename UnionTy>
248llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
249OmpGetRepeatableModifier(std::optional<std::list<UnionTy>> &&) = delete;
250
251template <typename SpecificTy, typename UnionTy>
252Fortran::parser::CharBlock OmpGetModifierSource(
253 const std::optional<std::list<UnionTy>> &modifiers,
254 const SpecificTy *specific) {
255 if (!modifiers || !specific) {
256 return Fortran::parser::CharBlock{};
257 }
258 for (auto &m : *modifiers) {
259 if (std::get_if<SpecificTy>(&m.u) == specific) {
260 return m.source;
261 }
262 }
263 llvm_unreachable("`specific` must be a member of `modifiers`");
264}
265
266namespace detail {
267template <typename T> constexpr const T *make_nullptr() {
268 return static_cast<const T *>(nullptr);
269}
270
272template <typename UnionTy>
273bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
274 llvm::omp::Clause id, parser::CharBlock clauseSource,
275 SemanticsContext &semaCtx) {
276 if (!modifiers) {
277 return true;
278 }
279 unsigned version{semaCtx.langOptions().OpenMPVersion};
280 bool result{true};
281 for (auto &m : *modifiers) {
282 const OmpModifierDescriptor &desc{OmpGetDescriptor(m)};
283 unsigned since{desc.since(id)};
284 if (since == ~0u) {
285 // This shouldn't really happen, but have it just in case.
286 semaCtx.Say(m.source,
287 "'%s' modifier is not supported on %s clause"_err_en_US,
288 desc.name.str(),
289 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
290 } else if (version < since) {
291 semaCtx.Say(m.source,
292 "'%s' modifier is not supported in OpenMP v%d.%d, try -fopenmp-version=%d"_warn_en_US,
293 desc.name.str(), version / 10, version % 10, since);
294 result = false;
295 }
296 }
297 return result;
298}
299
304template <typename SpecificTy, typename UnionTy>
305bool verifyIfRequired(const SpecificTy *,
306 const std::optional<std::list<UnionTy>> &modifiers,
307 parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
308 unsigned version{semaCtx.langOptions().OpenMPVersion};
309 const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
310 if (!desc.props(version).test(OmpProperty::Required)) {
311 // If the modifier is not required, there is nothing to do.
312 return true;
313 }
314 bool present{modifiers.has_value()};
315 present = present && llvm::any_of(*modifiers, [](auto &&m) {
316 return std::holds_alternative<SpecificTy>(m.u);
317 });
318 if (!present) {
319 semaCtx.Say(
320 clauseSource, "'%s' modifier is required"_err_en_US, desc.name.str());
321 }
322 return present;
323}
324
328template <typename UnionTy, size_t... Idxs>
329bool verifyRequiredPack(const std::optional<std::list<UnionTy>> &modifiers,
330 parser::CharBlock clauseSource, SemanticsContext &semaCtx,
331 std::integer_sequence<size_t, Idxs...>) {
332 using VariantTy = typename UnionTy::Variant;
333 return (verifyIfRequired(
334 make_nullptr<std::variant_alternative_t<Idxs, VariantTy>>(),
335 modifiers, clauseSource, semaCtx) &&
336 ...);
337}
338
341template <typename UnionTy>
342bool verifyRequired(const std::optional<std::list<UnionTy>> &modifiers,
343 llvm::omp::Clause id, parser::CharBlock clauseSource,
344 SemanticsContext &semaCtx) {
345 using VariantTy = typename UnionTy::Variant;
346 return verifyRequiredPack(modifiers, clauseSource, semaCtx,
347 std::make_index_sequence<std::variant_size_v<VariantTy>>{});
348}
349
354template <typename UnionTy, typename SpecificTy>
355bool verifyIfUnique(const SpecificTy *,
356 typename std::list<UnionTy>::const_iterator specific,
357 typename std::list<UnionTy>::const_iterator end,
358 SemanticsContext &semaCtx) {
359 // `specific` is the location of the modifier of type SpecificTy.
360 assert(specific != end && "`specific` must be a valid location");
361
362 unsigned version{semaCtx.langOptions().OpenMPVersion};
363 const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
364 // Ultimate implies Unique.
365 if (!desc.props(version).test(OmpProperty::Unique) &&
366 !desc.props(version).test(OmpProperty::Ultimate)) {
367 return true;
368 }
369 if (std::next(specific) != end) {
370 auto next{
371 detail::findInRange<SpecificTy, UnionTy>(std::next(specific), end)};
372 if (next != end) {
373 semaCtx.Say(next->source,
374 "'%s' modifier cannot occur multiple times"_err_en_US,
375 desc.name.str());
376 }
377 }
378 return true;
379}
380
383template <typename UnionTy>
384bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
385 llvm::omp::Clause id, parser::CharBlock clauseSource,
386 SemanticsContext &semaCtx) {
387 if (!modifiers) {
388 return true;
389 }
390 bool result{true};
391 for (auto it{modifiers->cbegin()}, end{modifiers->cend()}; it != end; ++it) {
392 result = common::visit(
393 [&](auto &&m) {
394 return verifyIfUnique<UnionTy>(&m, it, end, semaCtx);
395 },
396 it->u) &&
397 result;
398 }
399 return result;
400}
401
404template <typename UnionTy>
405bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
406 llvm::omp::Clause id, parser::CharBlock clauseSource,
407 SemanticsContext &semaCtx) {
408 if (!modifiers || modifiers->size() <= 1) {
409 return true;
410 }
411 unsigned version{semaCtx.langOptions().OpenMPVersion};
412 bool result{true};
413 auto first{modifiers->cbegin()};
414 auto last{std::prev(modifiers->cend())};
415
416 // Any item that has the Ultimate property has to be either at the back
417 // or at the front of the list (depending on whether it's a pre- or a post-
418 // modifier).
419 // Walk over the list, and if a given item has the Ultimate property but is
420 // not at the right position, mark it as an error.
421 for (auto it{first}, end{modifiers->cend()}; it != end; ++it) {
422 result =
423 common::visit(
424 [&](auto &&m) {
425 using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
426 const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
427 auto &props{desc.props(version)};
428
429 if (props.test(OmpProperty::Ultimate)) {
430 bool isPre = !props.test(OmpProperty::Post);
431 if (it == (isPre ? last : first)) {
432 // Skip, since this is the correct place for this modifier.
433 return true;
434 }
435 llvm::StringRef where{isPre ? "last" : "first"};
436 semaCtx.Say(it->source,
437 "'%s' should be the %s modifier"_err_en_US, desc.name.str(),
438 where.str());
439 return false;
440 }
441 return true;
442 },
443 it->u) &&
444 result;
445 }
446 return result;
447}
448
451template <typename UnionTy>
452bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
453 llvm::omp::Clause id, parser::CharBlock clauseSource,
454 SemanticsContext &semaCtx) {
455 if (!modifiers || modifiers->size() <= 1) {
456 return true;
457 }
458 unsigned version{semaCtx.langOptions().OpenMPVersion};
459 const UnionTy &front{modifiers->front()};
460 const OmpModifierDescriptor &frontDesc{OmpGetDescriptor(front)};
461
462 auto second{std::next(modifiers->cbegin())};
463 auto end{modifiers->end()};
464
465 auto emitErrorMessage{[&](const UnionTy &excl, const UnionTy &other) {
466 const OmpModifierDescriptor &descExcl{OmpGetDescriptor(excl)};
467 const OmpModifierDescriptor &descOther{OmpGetDescriptor(other)};
468 parser::MessageFormattedText txt(
469 "An exclusive '%s' modifier cannot be specified together with a modifier of a different type"_err_en_US,
470 descExcl.name.str());
471 parser::Message message(excl.source, txt);
472 message.Attach(
473 other.source, "'%s' provided here"_en_US, descOther.name.str());
474 semaCtx.Say(std::move(message));
475 }};
476
477 if (frontDesc.props(version).test(OmpProperty::Exclusive)) {
478 // If the first item has the Exclusive property, then check if there is
479 // another item in the rest of the list with a different SpecificTy as
480 // the alternative, and mark it as an error. This allows multiple Exclusive
481 // items to coexist as long as they hold the same SpecificTy.
482 bool result{true};
483 size_t frontIndex{front.u.index()};
484 for (auto it{second}; it != end; ++it) {
485 if (it->u.index() != frontIndex) {
486 emitErrorMessage(front, *it);
487 result = false;
488 break;
489 }
490 }
491 return result;
492 } else {
493 // If the first item does not have the Exclusive property, then check
494 // if there is an item in the rest of the list that is Exclusive, and
495 // mark it as an error if so.
496 bool result{true};
497 for (auto it{second}; it != end; ++it) {
498 const OmpModifierDescriptor &desc{OmpGetDescriptor(*it)};
499 if (desc.props(version).test(OmpProperty::Exclusive)) {
500 emitErrorMessage(*it, front);
501 result = false;
502 break;
503 }
504 }
505 return result;
506 }
507}
508} // namespace detail
509
510template <typename ClauseTy>
511bool OmpVerifyModifiers(const ClauseTy &clause, llvm::omp::Clause id,
512 parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
513 auto &modifiers{OmpGetModifiers(clause)};
514 bool results[]{//
515 detail::verifyVersions(modifiers, id, clauseSource, semaCtx),
516 detail::verifyRequired(modifiers, id, clauseSource, semaCtx),
517 detail::verifyUnique(modifiers, id, clauseSource, semaCtx),
518 detail::verifyUltimate(modifiers, id, clauseSource, semaCtx),
519 detail::verifyExclusive(modifiers, id, clauseSource, semaCtx)};
520 return llvm::all_of(results, [](bool x) { return x; });
521}
522} // namespace Fortran::semantics
523
524#endif // FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
Definition semantics.h:67
Definition parse-tree.h:3582
Definition parse-tree.h:3821
Definition parse-tree.h:3838
Definition parse-tree.h:3830
Definition parse-tree.h:3866
Definition parse-tree.h:3878
Definition parse-tree.h:3888
Definition parse-tree.h:3899
Definition parse-tree.h:3912
Definition parse-tree.h:3924
Definition parse-tree.h:3945
Definition parse-tree.h:3956
Definition parse-tree.h:3966
Definition parse-tree.h:3975
Definition parse-tree.h:3992
Definition parse-tree.h:4007
Definition parse-tree.h:4018
Definition parse-tree.h:4029
Definition parse-tree.h:4051
Definition parse-tree.h:4059
Definition parse-tree.h:4068
Definition parse-tree.h:4077
Definition parse-tree.h:4111
Definition parse-tree.h:4098
Definition parse-tree.h:4085
Definition parse-tree.h:4135
Definition parse-tree.h:4126
Definition parse-tree.h:4169
Definition parse-tree.h:4178
Definition parse-tree.h:4191
Definition parse-tree.h:4200
Definition parse-tree.h:4210
Definition parse-tree.h:4220
Definition parse-tree.h:4229
Definition parse-tree.h:4237
Definition parse-tree.h:4247
Definition parse-tree.h:4258
Definition parse-tree.h:4271
Definition openmp-modifiers.h:52
Definition openmp-modifiers.h:197