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