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/parse-tree.h"
18#include "llvm/ADT/iterator_range.h"
19#include "llvm/Frontend/OpenMP/OMP.h"
20
21#include <cassert>
22#include <iterator>
23#include <tuple>
24#include <type_traits>
25#include <utility>
26#include <variant>
27#include <vector>
28
29namespace Fortran::parser::omp {
30
31template <typename T> constexpr auto addr_if(std::optional<T> &x) {
32 return x ? &*x : nullptr;
33}
34template <typename T> constexpr auto addr_if(const std::optional<T> &x) {
35 return x ? &*x : nullptr;
36}
37
38const parser::Designator *GetDesignatorFromObj(const parser::OmpObject &object);
39const parser::DataRef *GetDataRefFromObj(const parser::OmpObject &object);
40const parser::OmpLocator *GetLocatorFromObj(const parser::OmpObject &object);
41const parser::Name *GetCommonBlockFromObj(const parser::OmpObject &object);
42
43const parser::ArrayElement *GetArrayElementFromObj(
44 const parser::OmpObject &object);
45std::optional<parser::CharBlock> GetObjectSource(
46 const parser::OmpObject &object);
47const parser::OmpObject *GetArgumentObject(const parser::OmpArgument &argument);
48
49const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
50 const OpenMPConstruct &x);
51const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
52 const OpenMPDeclarativeConstruct &x);
53
54namespace detail {
56 static OmpDirectiveName MakeName(CharBlock source = {},
57 llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown) {
59 name.source = source;
60 name.v = id;
61 return name;
62 }
63
64 static OmpDirectiveName GetOmpDirectiveName(const OmpDirectiveName &x) {
65 return x;
66 }
67
68 static OmpDirectiveName GetOmpDirectiveName(const OmpSectionDirective &x) {
69 if (auto &spec{std::get<std::optional<OmpDirectiveSpecification>>(x.t)}) {
70 return spec->DirName();
71 } else {
72 return MakeName({}, llvm::omp::Directive::OMPD_section);
73 }
74 }
75
76 static OmpDirectiveName GetOmpDirectiveName(
78 return x.DirName();
79 }
80
81 template <typename T>
82 static OmpDirectiveName GetOmpDirectiveName(const T &x) {
83 if constexpr (WrapperTrait<T>) {
84 return GetOmpDirectiveName(x.v);
85 } else if constexpr (TupleTrait<T>) {
86 if constexpr (std::is_base_of_v<OmpBlockConstruct, T>) {
87 return std::get<OmpBeginDirective>(x.t).DirName();
88 } else {
89 return GetFromTuple(
90 x.t, std::make_index_sequence<std::tuple_size_v<decltype(x.t)>>{});
91 }
92 } else if constexpr (UnionTrait<T>) {
93 return common::visit(
94 [](auto &&s) { return GetOmpDirectiveName(s); }, x.u);
95 } else {
96 return MakeName();
97 }
98 }
99
100 template <typename... Ts, size_t... Is>
101 static OmpDirectiveName GetFromTuple(
102 const std::tuple<Ts...> &t, std::index_sequence<Is...>) {
103 OmpDirectiveName name = MakeName();
104 auto accumulate = [&](const OmpDirectiveName &n) {
105 if (name.v == llvm::omp::Directive::OMPD_unknown) {
106 name = n;
107 } else {
108 assert(
109 n.v == llvm::omp::Directive::OMPD_unknown && "Conflicting names");
110 }
111 };
112 (accumulate(GetOmpDirectiveName(std::get<Is>(t))), ...);
113 return name;
114 }
115
116 template <typename T>
117 static OmpDirectiveName GetOmpDirectiveName(const common::Indirection<T> &x) {
118 return GetOmpDirectiveName(x.value());
119 }
120};
121} // namespace detail
122
123template <typename T> OmpDirectiveName GetOmpDirectiveName(const T &x) {
124 return detail::DirectiveNameScope::GetOmpDirectiveName(x);
125}
126
127std::string GetUpperName(llvm::omp::Clause id, unsigned version);
128std::string GetUpperName(llvm::omp::Directive id, unsigned version);
129
131const OpenMPConstruct *GetOmp(const ExecutionPartConstruct &x);
132
133const OpenMPLoopConstruct *GetOmpLoop(const ExecutionPartConstruct &x);
134const DoConstruct *GetDoConstruct(const ExecutionPartConstruct &x);
135
136namespace detail {
138 template <typename T> static const OmpObjectList *Get(const T &x) {
139 if constexpr (std::is_same_v<OmpObjectList, T>) {
140 return &x;
141 } else if constexpr (WrapperTrait<T>) {
142 return Get(x.v);
143 } else if constexpr (UnionTrait<T>) {
144 return std::visit([](auto &&s) { return Get(s); }, x.u);
145 } else if constexpr (TupleTrait<T>) {
146 return GetFromTuple(
147 x.t, std::make_index_sequence<std::tuple_size_v<decltype(x.t)>>{});
148 } else if constexpr (ConstraintTrait<T>) {
149 return Get(x.thing);
150 } else {
151 return nullptr;
152 }
153 }
154
155 template <typename T>
156 static const OmpObjectList *Get(const common::Indirection<T> &x) {
157 return Get(x.value());
158 }
159
160 template <typename... Ts, size_t... Is>
161 static const OmpObjectList *GetFromTuple(
162 const std::tuple<Ts...> &t, std::index_sequence<Is...>) {
163 const OmpObjectList *objects{nullptr};
164 ((objects = objects ? objects : Get(std::get<Is>(t))), ...);
165 return objects;
166 }
167};
168} // namespace detail
169
170template <typename T> const OmpObjectList *GetOmpObjectList(const T &clause) {
171 static_assert(std::is_class_v<T>, "Unexpected argument type");
172 return detail::OmpObjectListScope::Get(clause);
173}
174
175template <typename T>
176const T *GetFirstArgument(const OmpDirectiveSpecification &spec) {
177 for (const OmpArgument &arg : spec.Arguments().v) {
178 if (auto *t{std::get_if<T>(&arg.u)}) {
179 return t;
180 }
181 }
182 return nullptr;
183}
184
185const OmpClause *FindClause(
186 const OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId);
187
188const BlockConstruct *GetFortranBlockConstruct(
189 const ExecutionPartConstruct &epc);
190const Block &GetInnermostExecPart(const Block &block);
191bool IsStrictlyStructuredBlock(const Block &block);
192
193const OmpCombinerExpression *GetCombinerExpr(const OmpReductionSpecifier &x);
194const OmpCombinerExpression *GetCombinerExpr(const OmpClause &x);
195const OmpInitializerExpression *GetInitializerExpr(const OmpClause &x);
196
198 std::vector<const OmpAllocateDirective *> dirs;
199 const ExecutionPartConstruct *body{nullptr};
200};
201
202OmpAllocateInfo SplitOmpAllocate(const OmpAllocateDirective &x);
203
204template <typename R, typename = void, typename = void> struct is_range {
205 static constexpr bool value{false};
206};
207
208template <typename R>
209struct is_range<R, //
210 std::void_t<decltype(std::declval<R>().begin())>,
211 std::void_t<decltype(std::declval<R>().end())>> {
212 static constexpr bool value{true};
213};
214
215template <typename R> constexpr bool is_range_v = is_range<R>::value;
216
217// Iterate over a range of parser::Block::const_iterator's. When the end
218// of the range is reached, the iterator becomes invalid.
219// Treat BLOCK constructs as if they were transparent, i.e. as if the
220// BLOCK/ENDBLOCK statements, and the specification part contained within
221// were removed. The stepping determines whether the iterator steps "into"
222// DO loops and OpenMP loop constructs, or steps "over" them.
223//
224// Example: consecutive locations of the iterator:
225//
226// Step::Into Step::Over
227// block block
228// 1 => stmt1 1 => stmt1
229// block block
230// integer :: x integer :: x
231// 2 => stmt2 2 => stmt2
232// block block
233// end block end block
234// end block end block
235// 3 => do i = 1, n 3 => do i = 1, n
236// 4 => continue continue
237// end do end do
238// 5 => stmt3 4 => stmt3
239// end block end block
240//
241// 6 => <invalid> 5 => <invalid>
242//
243// The iterator is in a legal state (position) if it's at an
244// ExecutionPartConstruct that is not a BlockConstruct, or is invalid.
245struct ExecutionPartIterator {
246 enum class Step {
247 Into,
248 Over,
249 Default = Into,
250 };
251
252 using IteratorType = Block::const_iterator;
253 using IteratorRange = llvm::iterator_range<IteratorType>;
254
255 // An iterator range with a third iterator indicating a position inside
256 // the range.
257 struct IteratorGauge : public IteratorRange {
258 IteratorGauge(IteratorType b, IteratorType e)
259 : IteratorRange(b, e), at(b) {}
260 IteratorGauge(IteratorRange r) : IteratorRange(r), at(r.begin()) {}
261
262 bool atEnd() const { return at == end(); }
263 IteratorType at;
264 };
265
266 struct Construct {
267 Construct(IteratorType b, IteratorType e, const ExecutionPartConstruct *c)
268 : location(b, e), owner(c) {}
269 template <typename R>
270 Construct(const R &r, const ExecutionPartConstruct *c)
271 : location(r), owner(c) {}
272 Construct(const Construct &c) = default;
273 // The original range of the construct with the current position in it.
274 // The location.at is the construct currently being pointed at, or
275 // stepped into.
276 IteratorGauge location;
277 const ExecutionPartConstruct *owner;
278 };
279
280 ExecutionPartIterator() = default;
281
282 ExecutionPartIterator(IteratorType b, IteratorType e, Step s = Step::Default,
283 const ExecutionPartConstruct *c = nullptr)
284 : stepping_(s) {
285 stack_.emplace_back(b, e, c);
286 adjust();
287 }
288 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
289 ExecutionPartIterator(const R &range, Step stepping = Step::Default,
290 const ExecutionPartConstruct *construct = nullptr)
291 : ExecutionPartIterator(range.begin(), range.end(), stepping, construct) {
292 }
293
294 // Advance the iterator to the next legal position. If the current position
295 // is a DO-loop or a loop construct, step into the contained Block.
296 void step();
297
298 // Advance the iterator to the next legal position. If the current position
299 // is a DO-loop or a loop construct, step to the next legal position following
300 // the DO-loop or loop construct.
301 void next();
302
303 bool valid() const { return !stack_.empty(); }
304
305 const std::vector<Construct> &stack() const { return stack_; }
306 decltype(auto) operator*() const { return *at(); }
307 bool operator==(const ExecutionPartIterator &other) const {
308 if (valid() != other.valid()) {
309 return false;
310 }
311 // Invalid iterators are considered equal.
312 return !valid() ||
313 stack_.back().location.at == other.stack_.back().location.at;
314 }
315 bool operator!=(const ExecutionPartIterator &other) const {
316 return !(*this == other);
317 }
318
319 ExecutionPartIterator &operator++() {
320 if (stepping_ == Step::Into) {
321 step();
322 } else {
323 assert(stepping_ == Step::Over && "Unexpected stepping");
324 next();
325 }
326 return *this;
327 }
328
329 ExecutionPartIterator operator++(int) {
330 ExecutionPartIterator copy{*this};
331 operator++();
332 return copy;
333 }
334
335 using difference_type = IteratorType::difference_type;
336 using value_type = IteratorType::value_type;
337 using reference = IteratorType::reference;
338 using pointer = IteratorType::pointer;
339 using iterator_category = std::forward_iterator_tag;
340
341private:
342 IteratorType at() const { return stack_.back().location.at; };
343
344 // If the iterator is not at a legal location, keep advancing it until
345 // it lands at a legal location or becomes invalid.
346 void adjust();
347
348 const Step stepping_ = Step::Default;
349 std::vector<Construct> stack_;
350};
351
352template <typename Iterator = ExecutionPartIterator> struct ExecutionPartRange {
353 using Step = typename Iterator::Step;
354
355 ExecutionPartRange(Block::const_iterator begin, Block::const_iterator end,
356 Step stepping = Step::Default,
357 const ExecutionPartConstruct *owner = nullptr)
358 : begin_(begin, end, stepping, owner), end_() {}
359 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
360 ExecutionPartRange(const R &range, Step stepping = Step::Default,
361 const ExecutionPartConstruct *owner = nullptr)
362 : ExecutionPartRange(range.begin(), range.end(), stepping, owner) {}
363
364 Iterator begin() const { return begin_; }
365 Iterator end() const { return end_; }
366
367private:
368 Iterator begin_, end_;
369};
370
371struct LoopNestIterator : public ExecutionPartIterator {
372 LoopNestIterator() = default;
373
374 LoopNestIterator(IteratorType b, IteratorType e, Step s = Step::Default,
375 const ExecutionPartConstruct *c = nullptr)
376 : ExecutionPartIterator(b, e, s, c) {
377 adjust();
378 }
379 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
380 LoopNestIterator(const R &range, Step stepping = Step::Default,
381 const ExecutionPartConstruct *construct = nullptr)
382 : LoopNestIterator(range.begin(), range.end(), stepping, construct) {}
383
384 LoopNestIterator &operator++() {
385 ExecutionPartIterator::operator++();
386 adjust();
387 return *this;
388 }
389
390 LoopNestIterator operator++(int) {
391 LoopNestIterator copy{*this};
392 operator++();
393 return copy;
394 }
395
396private:
397 static bool isLoop(const ExecutionPartConstruct &c);
398
399 void adjust() {
400 while (valid() && !isLoop(**this)) {
401 ExecutionPartIterator::operator++();
402 }
403 }
404};
405
408
409} // namespace Fortran::parser::omp
410
411#endif // FORTRAN_PARSER_OPENMP_UTILS_H
Definition indirection.h:31
Definition char-block.h:26
Definition parse-tree.h:441
Definition parse-tree.h:2365
Definition parse-tree.h:558
Definition parse-tree.h:5397
Definition parse-tree.h:5261
Definition parse-tree.h:3577
Definition parse-tree.h:5162
Definition parse-tree.h:3632
Definition parse-tree.h:5275
Definition parse-tree.h:5549
Definition parse-tree.h:5535
Definition parse-tree.h:3742
Definition openmp-utils.h:245
Definition openmp-utils.h:352
Definition openmp-utils.h:197
Definition openmp-utils.h:204