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