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