FLANG
openmp-utils.h
1//===-- flang/Parser/openmp-utils.h ---------------------------------------===//
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// Common OpenMP utilities.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef FORTRAN_PARSER_OPENMP_UTILS_H
14#define FORTRAN_PARSER_OPENMP_UTILS_H
15
16#include "flang/Common/indirection.h"
17#include "flang/Parser/char-block.h"
18#include "flang/Parser/parse-tree.h"
19#include "llvm/ADT/iterator_range.h"
20#include "llvm/Frontend/OpenMP/OMP.h"
21#include "llvm/Frontend/OpenMP/OMPDescriptors.h"
22
23#include <cassert>
24#include <iterator>
25#include <limits>
26#include <tuple>
27#include <type_traits>
28#include <utility>
29#include <variant>
30#include <vector>
31
32namespace Fortran::parser::omp {
33
34template <typename T> constexpr auto addr_if(std::optional<T> &x) {
35 return x ? &*x : nullptr;
36}
37template <typename T> constexpr auto addr_if(const std::optional<T> &x) {
38 return x ? &*x : nullptr;
39}
40
41namespace descriptor {
42// Generic interfaces for entities that are "containers":
43// * Clause:
44// ├─ Elements: Modifiers
45// ╰─ Sets: ModifierSets
46// * Directive
47// ├─ Elements: Clauses
48// ╰─ Sets: ClauseSets
49// * ClauseSet:
50// ╰─ Elements: Clauses
51// * ModifierSet:
52// ╰─ Elements: Modifiers
53llvm::omp::Modifiers GetElements(
54 const llvm::omp::descriptor::Clause &desc, llvm::omp::Version version);
55llvm::omp::ModifierSets GetSets(
56 const llvm::omp::descriptor::Clause &desc, llvm::omp::Version version);
57llvm::omp::Clauses GetElements(
58 const llvm::omp::descriptor::Directive &desc, llvm::omp::Version version);
59llvm::omp::ClauseSets GetSets(
60 const llvm::omp::descriptor::Directive &desc, llvm::omp::Version version);
61llvm::omp::Clauses GetElements(
62 const llvm::omp::descriptor::ClauseSet &desc, llvm::omp::Version version);
63llvm::omp::Modifiers GetElements(
64 const llvm::omp::descriptor::ModifierSet &desc, llvm::omp::Version version);
65
66// The set of "elements" allowed on a given "container" is the union of all
67// elements listed explicitly, and elements of all sets. E.g. the set of all
68// clauses allowed on a given directive is the set of clauses listed in the
69// descriptor, plus the union of all clause sets.
70template <typename DescriptorTy>
71auto GetAllowedElements(const DescriptorTy &desc, llvm::omp::Version version) {
72 auto allowed{GetElements(desc, version)};
73 for (auto s : GetSets(desc, version)) {
74 allowed |= GetElements(llvm::omp::getDescriptor(s), version);
75 }
76 return allowed;
77}
78
79template <typename ElemTy, typename OwnerTy>
80llvm::directive::VersionRange GetVersionRangeForElement(
81 ElemTy elemId, OwnerTy ownerId) {
82 llvm::omp::Version minVer{llvm::omp::Version::Max()};
83 llvm::omp::Version maxVer{0};
84 const auto &desc{llvm::omp::getDescriptor(ownerId)};
85 for (llvm::omp::Version v : desc.getVersions()) {
86 if (GetAllowedElements(desc, v).test(elemId)) {
87 minVer = std::min(minVer, v);
88 maxVer = std::max(maxVer, v);
89 }
90 }
91 return llvm::directive::VersionRange{minVer, maxVer};
92}
93
94llvm::directive::VersionRange GetVersionRangeForDirective(
95 llvm::omp::Directive dirId);
96} // namespace descriptor
97
98const parser::Designator *GetDesignatorFromObj(const parser::OmpObject &object);
99const parser::DataRef *GetDataRefFromObj(const parser::OmpObject &object);
100const parser::OmpLocator *GetLocatorFromObj(const parser::OmpObject &object);
101const parser::Name *GetCommonBlockFromObj(const parser::OmpObject &object);
102
103const parser::ArrayElement *GetArrayElementFromObj(
104 const parser::OmpObject &object);
105std::optional<parser::CharBlock> GetObjectSource(
106 const parser::OmpObject &object);
107const parser::OmpObject *GetArgumentObject(const parser::OmpArgument &argument);
108
109const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
110 const OpenMPConstruct &x);
111const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
112 const OpenMPDeclarativeConstruct &x);
113
114template <typename T> struct WithSource {
115 template < //
116 typename U = std::remove_reference_t<T>,
117 typename = std::enable_if_t<std::is_default_constructible_v<U>>>
118 WithSource() : value(), source() {}
119 WithSource(const WithSource<T> &) = default;
120 WithSource(WithSource<T> &&) = default;
121 WithSource(const T &t, parser::CharBlock s) : value(t), source(s) {}
122 WithSource(T &&t, parser::CharBlock s) : value(std::move(t)), source(s) {}
123 WithSource &operator=(const WithSource<T> &) = default;
124 WithSource &operator=(WithSource<T> &&) = default;
125
126 using value_type = T;
127 T value;
128 parser::CharBlock source;
129};
130
131namespace detail {
133 static OmpDirectiveName MakeName(CharBlock source = {},
134 llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown) {
135 OmpDirectiveName name;
136 name.source = source;
137 name.v = id;
138 return name;
139 }
140
141 static OmpDirectiveName GetOmpDirectiveName(const OmpDirectiveName &x) {
142 return x;
143 }
144
145 static OmpDirectiveName GetOmpDirectiveName(const OmpSectionDirective &x) {
146 if (auto &spec{std::get<std::optional<OmpDirectiveSpecification>>(x.t)}) {
147 return spec->DirName();
148 } else {
149 return MakeName({}, llvm::omp::Directive::OMPD_section);
150 }
151 }
152
153 static OmpDirectiveName GetOmpDirectiveName(
154 const OmpBeginSectionsDirective &x) {
155 return x.DirName();
156 }
157
158 template <typename T>
159 static OmpDirectiveName GetOmpDirectiveName(const T &x) {
160 if constexpr (WrapperTrait<T>) {
161 return GetOmpDirectiveName(x.v);
162 } else if constexpr (TupleTrait<T>) {
163 if constexpr (std::is_base_of_v<OmpBlockConstruct, T>) {
164 return std::get<OmpBeginDirective>(x.t).DirName();
165 } else {
166 return GetFromTuple(
167 x.t, std::make_index_sequence<std::tuple_size_v<decltype(x.t)>>{});
168 }
169 } else if constexpr (UnionTrait<T>) {
170 return common::visit(
171 [](auto &&s) { return GetOmpDirectiveName(s); }, x.u);
172 } else {
173 return MakeName();
174 }
175 }
176
177 template <typename... Ts, size_t... Is>
178 static OmpDirectiveName GetFromTuple(
179 const std::tuple<Ts...> &t, std::index_sequence<Is...>) {
180 OmpDirectiveName name = MakeName();
181 auto accumulate = [&](const OmpDirectiveName &n) {
182 if (name.v == llvm::omp::Directive::OMPD_unknown) {
183 name = n;
184 } else {
185 assert(
186 n.v == llvm::omp::Directive::OMPD_unknown && "Conflicting names");
187 }
188 };
189 (accumulate(GetOmpDirectiveName(std::get<Is>(t))), ...);
190 return name;
191 }
192
193 template <typename T>
194 static OmpDirectiveName GetOmpDirectiveName(const common::Indirection<T> &x) {
195 return GetOmpDirectiveName(x.value());
196 }
197};
198} // namespace detail
199
200template <typename T> OmpDirectiveName GetOmpDirectiveName(const T &x) {
201 return detail::DirectiveNameScope::GetOmpDirectiveName(x);
202}
203
204std::string GetUpperName(
205 llvm::omp::Clause id, llvm::omp::Version version, bool annotate = true);
206std::string GetUpperName(
207 llvm::omp::Directive id, llvm::omp::Version version, bool annotate = true);
208
210const OpenMPConstruct *GetOmp(const ExecutionPartConstruct &x);
211
212const OpenMPLoopConstruct *GetOmpLoop(const ExecutionPartConstruct &x);
213const DoConstruct *GetDoConstruct(const ExecutionPartConstruct &x);
214
215namespace detail {
217 template <typename T> static const OmpObjectList *Get(const T &x) {
218 if constexpr (std::is_same_v<OmpObjectList, T>) {
219 return &x;
220 } else if constexpr (WrapperTrait<T>) {
221 return Get(x.v);
222 } else if constexpr (UnionTrait<T>) {
223 return std::visit([](auto &&s) { return Get(s); }, x.u);
224 } else if constexpr (TupleTrait<T>) {
225 return GetFromTuple(
226 x.t, std::make_index_sequence<std::tuple_size_v<decltype(x.t)>>{});
227 } else if constexpr (ConstraintTrait<T>) {
228 return Get(x.thing);
229 } else {
230 return nullptr;
231 }
232 }
233
234 template <typename T>
235 static const OmpObjectList *Get(const common::Indirection<T> &x) {
236 return Get(x.value());
237 }
238
239 template <typename... Ts, size_t... Is>
240 static const OmpObjectList *GetFromTuple(
241 const std::tuple<Ts...> &t, std::index_sequence<Is...>) {
242 const OmpObjectList *objects{nullptr};
243 ((objects = objects ? objects : Get(std::get<Is>(t))), ...);
244 return objects;
245 }
246};
247} // namespace detail
248
249template <typename T> const OmpObjectList *GetOmpObjectList(const T &clause) {
250 static_assert(std::is_class_v<T>, "Unexpected argument type");
251 return detail::OmpObjectListScope::Get(clause);
252}
253
254template <typename T>
255const T *GetFirstArgument(const OmpDirectiveSpecification &spec) {
256 for (const OmpArgument &arg : spec.Arguments().v) {
257 if (auto *t{std::get_if<T>(&arg.u)}) {
258 return t;
259 }
260 }
261 return nullptr;
262}
263
264const OmpClause *FindClause(
265 const OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId);
266
267const BlockConstruct *GetFortranBlockConstruct(
268 const ExecutionPartConstruct &epc);
269const Block &GetInnermostExecPart(const Block &block);
270bool IsStrictlyStructuredBlock(const Block &block);
271
272const OmpCombinerExpression *GetCombinerExpr(const OmpReductionSpecifier &x);
273const OmpCombinerExpression *GetCombinerExpr(const OmpClause &x);
274const OmpInitializerExpression *GetInitializerExpr(const OmpClause &x);
275
277 std::vector<const OmpAllocateDirective *> dirs;
278 const ExecutionPartConstruct *body{nullptr};
279};
280
281OmpAllocateInfo SplitOmpAllocate(const OmpAllocateDirective &x);
282
283namespace detail {
284template <typename ClauseTy, typename VoidTy = void> struct HasModifierImpl {
285 static constexpr bool value{false};
286};
287template <typename ClauseTy>
288struct HasModifierImpl<ClauseTy, std::void_t<typename ClauseTy::Modifier>> {
289 static constexpr bool value{true};
290};
291} // namespace detail
292template <typename ClauseTy>
293static constexpr bool HasModifier = detail::HasModifierImpl<ClauseTy>::value;
294
295template <typename R, typename = void, typename = void> struct is_range {
296 static constexpr bool value{false};
297};
298
299template <typename R>
300struct is_range<R, //
301 std::void_t<decltype(std::declval<R>().begin())>,
302 std::void_t<decltype(std::declval<R>().end())>> {
303 static constexpr bool value{true};
304};
305
306template <typename R> constexpr bool is_range_v = is_range<R>::value;
307
308// Iterate over a range of parser::Block::const_iterator's. When the end
309// of the range is reached, the iterator becomes invalid.
310// Treat BLOCK constructs as if they were transparent, i.e. as if the
311// BLOCK/ENDBLOCK statements, and the specification part contained within
312// were removed. The stepping determines whether the iterator steps "into"
313// DO loops and OpenMP loop constructs, or steps "over" them.
314//
315// Example: consecutive locations of the iterator:
316//
317// Step::Into Step::Over
318// block block
319// 1 => stmt1 1 => stmt1
320// block block
321// integer :: x integer :: x
322// 2 => stmt2 2 => stmt2
323// block block
324// end block end block
325// end block end block
326// 3 => do i = 1, n 3 => do i = 1, n
327// 4 => continue continue
328// end do end do
329// 5 => stmt3 4 => stmt3
330// end block end block
331//
332// 6 => <invalid> 5 => <invalid>
333//
334// The iterator is in a legal state (position) if it's at an
335// ExecutionPartConstruct that is not a BlockConstruct, or is invalid.
336struct ExecutionPartIterator {
337 enum class Step {
338 Into,
339 Over,
340 Default = Into,
341 };
342
343 using IteratorType = Block::const_iterator;
344 using IteratorRange = llvm::iterator_range<IteratorType>;
345
346 // An iterator range with a third iterator indicating a position inside
347 // the range.
348 struct IteratorGauge : public IteratorRange {
349 IteratorGauge(IteratorType b, IteratorType e)
350 : IteratorRange(b, e), at(b) {}
351 IteratorGauge(IteratorRange r) : IteratorRange(r), at(r.begin()) {}
352
353 bool atEnd() const { return at == end(); }
354 IteratorType at;
355 };
356
357 struct Construct {
358 Construct(IteratorType b, IteratorType e, const ExecutionPartConstruct *c)
359 : location(b, e), owner(c) {}
360 template <typename R>
361 Construct(const R &r, const ExecutionPartConstruct *c)
362 : location(r), owner(c) {}
363 Construct(const Construct &c) = default;
364 // The original range of the construct with the current position in it.
365 // The location.at is the construct currently being pointed at, or
366 // stepped into.
367 IteratorGauge location;
368 const ExecutionPartConstruct *owner;
369 };
370
371 ExecutionPartIterator() = default;
372
373 ExecutionPartIterator(IteratorType b, IteratorType e, Step s = Step::Default,
374 const ExecutionPartConstruct *c = nullptr)
375 : stepping_(s) {
376 stack_.emplace_back(b, e, c);
377 adjust();
378 }
379 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
380 ExecutionPartIterator(const R &range, Step stepping = Step::Default,
381 const ExecutionPartConstruct *construct = nullptr)
382 : ExecutionPartIterator(range.begin(), range.end(), stepping, construct) {
383 }
384
385 // Advance the iterator to the next legal position. If the current position
386 // is a DO-loop or a loop construct, step into the contained Block.
387 void step();
388
389 // Advance the iterator to the next legal position. If the current position
390 // is a DO-loop or a loop construct, step to the next legal position following
391 // the DO-loop or loop construct.
392 void next();
393
394 bool valid() const { return !stack_.empty(); }
395
396 const std::vector<Construct> &stack() const { return stack_; }
397 decltype(auto) operator*() const { return *at(); }
398 bool operator==(const ExecutionPartIterator &other) const {
399 if (valid() != other.valid()) {
400 return false;
401 }
402 // Invalid iterators are considered equal.
403 return !valid() ||
404 stack_.back().location.at == other.stack_.back().location.at;
405 }
406 bool operator!=(const ExecutionPartIterator &other) const {
407 return !(*this == other);
408 }
409
410 ExecutionPartIterator &operator++() {
411 if (stepping_ == Step::Into) {
412 step();
413 } else {
414 assert(stepping_ == Step::Over && "Unexpected stepping");
415 next();
416 }
417 return *this;
418 }
419
420 ExecutionPartIterator operator++(int) {
421 ExecutionPartIterator copy{*this};
422 operator++();
423 return copy;
424 }
425
426 using difference_type = IteratorType::difference_type;
427 using value_type = IteratorType::value_type;
428 using reference = IteratorType::reference;
429 using pointer = IteratorType::pointer;
430 using iterator_category = std::forward_iterator_tag;
431
432private:
433 IteratorType at() const { return stack_.back().location.at; };
434
435 // If the iterator is not at a legal location, keep advancing it until
436 // it lands at a legal location or becomes invalid.
437 void adjust();
438
439 const Step stepping_ = Step::Default;
440 std::vector<Construct> stack_;
441};
442
443template <typename Iterator = ExecutionPartIterator> struct ExecutionPartRange {
444 using Step = typename Iterator::Step;
445
446 ExecutionPartRange(Block::const_iterator begin, Block::const_iterator end,
447 Step stepping = Step::Default,
448 const ExecutionPartConstruct *owner = nullptr)
449 : begin_(begin, end, stepping, owner), end_() {}
450 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
451 ExecutionPartRange(const R &range, Step stepping = Step::Default,
452 const ExecutionPartConstruct *owner = nullptr)
453 : ExecutionPartRange(range.begin(), range.end(), stepping, owner) {}
454
455 Iterator begin() const { return begin_; }
456 Iterator end() const { return end_; }
457
458private:
459 Iterator begin_, end_;
460};
461
462struct LoopNestIterator : public ExecutionPartIterator {
463 LoopNestIterator() = default;
464
465 LoopNestIterator(IteratorType b, IteratorType e, Step s = Step::Default,
466 const ExecutionPartConstruct *c = nullptr)
467 : ExecutionPartIterator(b, e, s, c) {
468 adjust();
469 }
470 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
471 LoopNestIterator(const R &range, Step stepping = Step::Default,
472 const ExecutionPartConstruct *construct = nullptr)
473 : LoopNestIterator(range.begin(), range.end(), stepping, construct) {}
474
475 LoopNestIterator &operator++() {
476 ExecutionPartIterator::operator++();
477 adjust();
478 return *this;
479 }
480
481 LoopNestIterator operator++(int) {
482 LoopNestIterator copy{*this};
483 operator++();
484 return copy;
485 }
486
487private:
488 static bool isLoop(const ExecutionPartConstruct &c);
489
490 void adjust() {
491 while (valid() && !isLoop(**this)) {
492 ExecutionPartIterator::operator++();
493 }
494 }
495};
496
499
500} // namespace Fortran::parser::omp
501
502#endif // FORTRAN_PARSER_OPENMP_UTILS_H
Definition indirection.h:31
Definition char-block.h:27
Definition parse-tree.h:442
Definition parse-tree.h:2379
Definition parse-tree.h:559
Definition parse-tree.h:5530
Definition parse-tree.h:5394
Definition parse-tree.h:3591
Definition parse-tree.h:5295
Definition parse-tree.h:3646
Definition parse-tree.h:5408
Definition parse-tree.h:5682
Definition parse-tree.h:5668
Definition parse-tree.h:3757
Definition openmp-utils.h:336
Definition openmp-utils.h:443
Definition openmp-utils.h:276
Definition openmp-utils.h:295