FLANG
openmp-utils.h
1//===-- lib/Semantics/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 utilities used in OpenMP semantic checks.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef FORTRAN_SEMANTICS_OPENMP_UTILS_H
14#define FORTRAN_SEMANTICS_OPENMP_UTILS_H
15
16#include "flang/Common/indirection.h"
17#include "flang/Evaluate/type.h"
18#include "flang/Parser/char-block.h"
19#include "flang/Parser/message.h"
20#include "flang/Parser/openmp-utils.h"
21#include "flang/Parser/parse-tree.h"
22#include "flang/Parser/tools.h"
23#include "flang/Semantics/tools.h"
24
25#include "llvm/ADT/ArrayRef.h"
26
27#include <memory>
28#include <optional>
29#include <string>
30#include <tuple>
31#include <type_traits>
32#include <utility>
33#include <vector>
34
35namespace Fortran::semantics {
36class Scope;
38class Symbol;
39
40// Add this namespace to avoid potential conflicts
41namespace omp {
42using Fortran::parser::omp::BlockRange;
43using Fortran::parser::omp::ExecutionPartIterator;
44using Fortran::parser::omp::is_range_v;
45using Fortran::parser::omp::LoopNestIterator;
46using Fortran::parser::omp::LoopRange;
47
48template <typename T, typename U = std::remove_const_t<T>> U AsRvalue(T &t) {
49 return U(t);
50}
51
52template <typename T> T &&AsRvalue(T &&t) { return std::move(t); }
53
54const Scope &GetScopingUnit(const Scope &scope);
55const Scope &GetProgramUnit(const Scope &scope);
56
57template <typename T> struct WithSource {
58 template < //
59 typename U = std::remove_reference_t<T>,
60 typename = std::enable_if_t<std::is_default_constructible_v<U>>>
61 WithSource() : value(), source() {}
62 WithSource(const WithSource<T> &) = default;
63 WithSource(WithSource<T> &&) = default;
64 WithSource(const T &t, parser::CharBlock s) : value(t), source(s) {}
65 WithSource(T &&t, parser::CharBlock s) : value(std::move(t)), source(s) {}
66 WithSource &operator=(const WithSource<T> &) = default;
67 WithSource &operator=(WithSource<T> &&) = default;
68
69 using value_type = T;
70 T value;
71 parser::CharBlock source;
72};
73
74// There is no consistent way to get the source of an ActionStmt, but there
75// is "source" in Statement<T>. This structure keeps the ActionStmt with the
76// extracted source for further use.
77struct SourcedActionStmt : public WithSource<const parser::ActionStmt *> {
78 using WithSource<value_type>::WithSource;
79 value_type stmt() const { return value; }
80 operator bool() const { return stmt() != nullptr; }
81};
82
84SourcedActionStmt GetActionStmt(const parser::Block &block);
85
86std::string ThisVersion(unsigned version);
87std::string TryVersion(unsigned version);
88
89const Symbol *GetObjectSymbol(const parser::OmpObject &object);
90const Symbol *GetArgumentSymbol(const parser::OmpArgument &argument);
91
92bool IsCommonBlock(const Symbol &sym);
93bool IsExtendedListItem(const Symbol &sym);
94bool IsVariableListItem(const Symbol &sym);
95bool IsTypeParamInquiry(const Symbol &sym);
96bool IsStructureComponent(const Symbol &sym);
97bool IsPrivatizable(const Symbol &sym);
98bool IsVarOrFunctionRef(const MaybeExpr &expr);
99
100bool IsWholeAssumedSizeArray(const parser::OmpObject &object);
101
102const Symbol *GetHostSymbol(const Symbol &sym);
103
104bool IsMapEnteringType(parser::OmpMapType::Value type);
105bool IsMapExitingType(parser::OmpMapType::Value type);
106
107MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr);
108template <typename T> MaybeExpr GetEvaluateExpr(const T &inp) {
109 return GetEvaluateExpr(parser::UnwrapRef<parser::Expr>(inp));
110}
111
112std::optional<evaluate::DynamicType> GetDynamicType(
113 const parser::Expr &parserExpr);
114
115std::optional<bool> GetLogicalValue(const SomeExpr &expr);
116std::optional<int64_t> GetIntValueFromExpr(
117 const parser::Expr &parserExpr, SemanticsContext *semaCtx = nullptr);
118
119template <typename T>
120std::optional<int64_t> GetIntValueFromExpr(
121 const T &wrappedExpr, SemanticsContext *semaCtx = nullptr) {
122 if (auto *parserExpr{parser::Unwrap<parser::Expr>(wrappedExpr)}) {
123 return GetIntValueFromExpr(*parserExpr, semaCtx);
124 }
125 return std::nullopt;
126}
127
128std::optional<bool> IsContiguous(
129 SemanticsContext &semaCtx, const parser::OmpObject &object);
130
131std::vector<SomeExpr> GetTopLevelDesignators(const SomeExpr &expr);
132const SomeExpr *HasStorageOverlap(
133 const SomeExpr &base, llvm::ArrayRef<SomeExpr> exprs);
134
135bool IsAssignment(const parser::ActionStmt *x);
136bool IsPointerAssignment(const evaluate::Assignment &x);
137
138MaybeExpr MakeEvaluateExpr(const parser::OmpStylizedInstance &inp);
139
140bool IsLoopTransforming(llvm::omp::Directive dir);
141bool IsFullUnroll(const parser::OmpDirectiveSpecification &spec);
142
143inline bool IsDoConcurrentLegal(unsigned version) {
144 // DO CONCURRENT is allowed (as an alternative to a Canonical Loop Nest)
145 // in OpenMP 6.0+.
146 return version >= 60;
147}
148
149struct LoopControl {
150 LoopControl(LoopControl &&x) = default;
151 LoopControl(const LoopControl &x) = default;
152 LoopControl(const parser::LoopControl::Bounds &x);
153 LoopControl(const parser::ConcurrentControl &x);
154
155 parser::Name iv;
156 WithSource<MaybeExpr> lbound, ubound, step;
157
158private:
159 static WithSource<MaybeExpr> fromParserExpr(const parser::Expr &x);
160};
161
162std::vector<LoopControl> GetLoopControls(const parser::DoConstruct &x);
163
165struct Reason {
166 Reason() = default;
167 Reason(Reason &&) = default;
168 Reason(const Reason &);
169 Reason &operator=(Reason &&) = default;
170 Reason &operator=(const Reason &);
171
172 parser::Messages msgs;
173
174 template <typename... Ts> Reason &Say(Ts &&...args) {
175 msgs.Say(std::forward<Ts>(args)...);
176 return *this;
177 }
178 parser::Message &AttachTo(parser::Message &msg);
179 Reason &Append(const Reason &other) {
180 CopyFrom(other);
181 return *this;
182 }
183 operator bool() const { return !msgs.empty(); }
184
185private:
186 void CopyFrom(const Reason &other);
187};
188
189// A property with an explanation of its value. Both, the property and the
190// reason are optional (the reason can have no messages in it).
191template <typename T> struct WithReason {
192 std::optional<T> value;
193 Reason reason;
194
195 WithReason() = default;
196 WithReason(std::optional<T> v, const Reason &r = Reason())
197 : value(v), reason(r) {}
198 operator bool() const { return value.has_value(); }
199};
200
201WithReason<int64_t> GetArgumentValueWithReason(
202 const parser::OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId,
203 unsigned version, SemanticsContext *semaCtx = nullptr);
204WithReason<int64_t> GetNumArgumentsWithReason(
205 const parser::OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId,
206 unsigned version, SemanticsContext *semaCtx = nullptr);
207WithReason<int64_t> GetHeightWithReason(
208 const parser::OmpDirectiveSpecification &spec, unsigned version,
209 SemanticsContext *semaCtx = nullptr);
210
213std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
214 const parser::OmpDirectiveSpecification &spec, unsigned version,
215 SemanticsContext *semaCtx = nullptr);
218std::pair<WithReason<int64_t>, bool> GetGeneratedNestDepthWithReason(
219 const parser::OmpDirectiveSpecification &spec, unsigned version,
220 SemanticsContext *semaCtx = nullptr);
224WithReason<std::pair<int64_t, int64_t>> GetAffectedLoopRangeWithReason(
225 const parser::OmpDirectiveSpecification &spec, unsigned version,
226 SemanticsContext *semaCtx = nullptr);
228WithReason<int64_t> GetRectangularNestDepthWithReason(
229 const parser::OmpDirectiveSpecification &spec, unsigned version,
230 SemanticsContext *semaCtx = nullptr);
231
235std::optional<int64_t> GetMinimumSequenceCount(
236 std::optional<int64_t> first, std::optional<int64_t> count);
237std::optional<int64_t> GetMinimumSequenceCount(
238 std::optional<std::pair<int64_t, int64_t>> range);
239
245std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
246 const parser::OpenMPLoopConstruct &x, unsigned version,
247 SemanticsContext *semaCtx = nullptr);
248
249struct LoopSequence {
250 LoopSequence(const parser::ExecutionPartConstruct &root, unsigned version,
251 bool allowAllLoops = false, SemanticsContext *semaCtx = nullptr);
252
253 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
254 LoopSequence(const R &range, unsigned version, bool allowAllLoops = false,
255 SemanticsContext *semaCtx = nullptr)
256 : version_(version), allowAllLoops_(allowAllLoops), semaCtx_(semaCtx) {
257 entry_ = std::make_unique<Construct>(range, nullptr);
258 createChildrenFromRange(entry_->location);
259 precalculate();
260 }
261
262 struct Depth {
263 // If this sequence is a nest, the depth of the Canonical Loop Nest rooted
264 // at this sequence. Otherwise unspecified.
265 WithReason<int64_t> semantic;
266 // If this sequence is a nest, the depth of the perfect Canonical Loop Nest
267 // rooted at this sequence. Otherwise unspecified.
268 WithReason<int64_t> perfect;
269 };
270
271 bool isNest() const { return length_.value == 1; }
272 const WithReason<int64_t> &length() const { return length_; }
273 const WithReason<int64_t> &height() const { return height_; }
274 const Depth &depth() const { return depth_; }
275 const std::vector<LoopSequence> &children() const { return children_; }
276 const parser::ExecutionPartConstruct *owner() const { return entry_->owner; }
277
278 WithReason<bool> isWellFormedSequence() const;
279 WithReason<bool> isWellFormedNest() const;
280
283 const LoopSequence *getNestedDoConcurrent() const;
284
285 std::vector<LoopControl> getLoopControls() const;
286 // Check if this loop's bounds are invariant in each of the `outer`
287 // constructs.
288 WithReason<bool> isRectangular(
289 const std::vector<const LoopSequence *> &outer) const;
290
291private:
292 using Construct = ExecutionPartIterator::Construct;
293
294 LoopSequence(std::unique_ptr<Construct> entry, unsigned version,
295 bool allowAllLoops, SemanticsContext *semaCtx = nullptr);
296
297 template <typename R, typename = std::enable_if_t<is_range_v<R>>>
298 void createChildrenFromRange(const R &range) {
299 createChildrenFromRange(range.begin(), range.end());
300 }
301
302 std::unique_ptr<Construct> createConstructEntry(
303 const parser::ExecutionPartConstruct &code);
304
305 void createChildrenFromRange( //
306 ExecutionPartIterator::IteratorType begin,
307 ExecutionPartIterator::IteratorType end);
308
310 void precalculate();
311
312 WithReason<int64_t> calculateLength() const;
313 WithReason<int64_t> getNestedLength() const;
314 Depth calculateDepths() const;
315 Depth getNestedDepths() const;
316 WithReason<int64_t> calculateHeight() const;
317
321 const parser::ExecutionPartConstruct *invalidIC_{nullptr};
325 const parser::ExecutionPartConstruct *opaqueIC_{nullptr};
326
331 WithReason<int64_t> length_;
333 Depth depth_;
340 WithReason<int64_t> height_;
341
342 // The core structure of the class:
343 unsigned version_; // Needed for GetXyzWithReason
344 bool allowAllLoops_;
345 std::unique_ptr<Construct> entry_;
346 std::vector<LoopSequence> children_;
347 SemanticsContext *semaCtx_{nullptr};
348};
349} // namespace omp
350} // namespace Fortran::semantics
351
352#endif // FORTRAN_SEMANTICS_OPENMP_UTILS_H
Definition char-block.h:28
Definition message.h:200
Definition message.h:332
Definition scope.h:67
Definition semantics.h:67
Definition symbol.h:832
Definition parse-tree.h:2238
Definition parse-tree.h:2325
Definition parse-tree.h:556
Definition parse-tree.h:1695
Definition parse-tree.h:589
Definition parse-tree.h:5085
Definition parse-tree.h:3562
Definition parse-tree.h:5459
Definition parse-tree.h:3693
const LoopSequence * getNestedDoConcurrent() const
Definition openmp-utils.cpp:1388
A representation of a "because" message.
Definition openmp-utils.h:165
Definition openmp-utils.h:77
Definition openmp-utils.h:191
Definition openmp-utils.h:57