1 | //===-- Clauses.h -- OpenMP clause handling -------------------------------===// |
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 | #ifndef FORTRAN_LOWER_OPENMP_CLAUSES_H |
9 | #define FORTRAN_LOWER_OPENMP_CLAUSES_H |
10 | |
11 | #include "flang/Evaluate/expression.h" |
12 | #include "flang/Evaluate/type.h" |
13 | #include "flang/Parser/parse-tree.h" |
14 | #include "flang/Semantics/expression.h" |
15 | #include "flang/Semantics/semantics.h" |
16 | #include "flang/Semantics/symbol.h" |
17 | |
18 | #include "llvm/ADT/STLExtras.h" |
19 | #include "llvm/Frontend/OpenMP/ClauseT.h" |
20 | #include "llvm/Frontend/OpenMP/OMP.h.inc" |
21 | |
22 | #include <optional> |
23 | #include <type_traits> |
24 | #include <utility> |
25 | |
26 | namespace Fortran::semantics { |
27 | class Symbol; |
28 | } |
29 | |
30 | namespace Fortran::lower::omp { |
31 | using namespace Fortran; |
32 | using SomeExpr = semantics::SomeExpr; |
33 | using MaybeExpr = semantics::MaybeExpr; |
34 | using TypeTy = evaluate::DynamicType; |
35 | |
36 | template <typename ExprTy> |
37 | struct IdTyTemplate { |
38 | // "symbol" is always non-null for id's of actual objects. |
39 | Fortran::semantics::Symbol *symbol; |
40 | std::optional<ExprTy> designator; |
41 | |
42 | bool operator==(const IdTyTemplate &other) const { |
43 | // If symbols are different, then the objects are different. |
44 | if (symbol != other.symbol) |
45 | return false; |
46 | if (symbol == nullptr) |
47 | return true; |
48 | // Equal symbols don't necessarily indicate identical objects, |
49 | // for example, a derived object component may use a single symbol, |
50 | // which will refer to different objects for different designators, |
51 | // e.g. a%c and b%c. |
52 | return designator == other.designator; |
53 | } |
54 | |
55 | // Defining an "ordering" which allows types derived from this to be |
56 | // utilised in maps and other containers that require comparison |
57 | // operators for ordering |
58 | bool operator<(const IdTyTemplate &other) const { |
59 | return symbol < other.symbol; |
60 | } |
61 | |
62 | operator bool() const { return symbol != nullptr; } |
63 | }; |
64 | |
65 | using ExprTy = SomeExpr; |
66 | |
67 | template <typename T> |
68 | using List = tomp::ListT<T>; |
69 | } // namespace Fortran::lower::omp |
70 | |
71 | // Specialization of the ObjectT template |
72 | namespace tomp::type { |
73 | template <> |
74 | struct ObjectT<Fortran::lower::omp::IdTyTemplate<Fortran::lower::omp::ExprTy>, |
75 | Fortran::lower::omp::ExprTy> { |
76 | using IdTy = Fortran::lower::omp::IdTyTemplate<Fortran::lower::omp::ExprTy>; |
77 | using ExprTy = Fortran::lower::omp::ExprTy; |
78 | |
79 | IdTy id() const { return identity; } |
80 | Fortran::semantics::Symbol *sym() const { return identity.symbol; } |
81 | const std::optional<ExprTy> &ref() const { return identity.designator; } |
82 | |
83 | bool operator<(const ObjectT<IdTy, ExprTy> &other) const { |
84 | return identity < other.identity; |
85 | } |
86 | |
87 | IdTy identity; |
88 | }; |
89 | } // namespace tomp::type |
90 | |
91 | namespace Fortran::lower::omp { |
92 | using IdTy = IdTyTemplate<ExprTy>; |
93 | } |
94 | |
95 | namespace std { |
96 | template <> |
97 | struct hash<Fortran::lower::omp::IdTy> { |
98 | size_t operator()(const Fortran::lower::omp::IdTy &id) const { |
99 | return static_cast<size_t>(reinterpret_cast<uintptr_t>(id.symbol)); |
100 | } |
101 | }; |
102 | } // namespace std |
103 | |
104 | namespace Fortran::lower::omp { |
105 | using Object = tomp::ObjectT<IdTy, ExprTy>; |
106 | using ObjectList = tomp::ObjectListT<IdTy, ExprTy>; |
107 | |
108 | Object makeObject(const parser::OmpObject &object, |
109 | semantics::SemanticsContext &semaCtx); |
110 | Object makeObject(const parser::Name &name, |
111 | semantics::SemanticsContext &semaCtx); |
112 | Object makeObject(const parser::Designator &dsg, |
113 | semantics::SemanticsContext &semaCtx); |
114 | Object makeObject(const parser::StructureComponent &comp, |
115 | semantics::SemanticsContext &semaCtx); |
116 | |
117 | inline auto makeObjectFn(semantics::SemanticsContext &semaCtx) { |
118 | return [&](auto &&s) { return makeObject(s, semaCtx); }; |
119 | } |
120 | |
121 | template <typename T> |
122 | SomeExpr makeExpr(T &&pftExpr, semantics::SemanticsContext &semaCtx) { |
123 | auto maybeExpr = evaluate::ExpressionAnalyzer(semaCtx).Analyze(pftExpr); |
124 | assert(maybeExpr); |
125 | return std::move(*maybeExpr); |
126 | } |
127 | |
128 | inline auto makeExprFn(semantics::SemanticsContext &semaCtx) { |
129 | return [&](auto &&s) { return makeExpr(s, semaCtx); }; |
130 | } |
131 | |
132 | template < |
133 | typename ContainerTy, typename FunctionTy, |
134 | typename ElemTy = typename llvm::remove_cvref_t<ContainerTy>::value_type, |
135 | typename ResultTy = std::invoke_result_t<FunctionTy, ElemTy>> |
136 | List<ResultTy> makeList(ContainerTy &&container, FunctionTy &&func) { |
137 | List<ResultTy> v; |
138 | llvm::transform(container, std::back_inserter(v), func); |
139 | return v; |
140 | } |
141 | |
142 | inline ObjectList makeObjects(const parser::OmpObjectList &objects, |
143 | semantics::SemanticsContext &semaCtx) { |
144 | return makeList(objects.v, makeObjectFn(semaCtx)); |
145 | } |
146 | |
147 | ObjectList makeObjects(const parser::OmpArgumentList &objects, |
148 | semantics::SemanticsContext &semaCtx); |
149 | |
150 | template <typename FuncTy, // |
151 | typename ArgTy, // |
152 | typename ResultTy = std::invoke_result_t<FuncTy, ArgTy>> |
153 | std::optional<ResultTy> maybeApply(FuncTy &&func, |
154 | const std::optional<ArgTy> &arg) { |
155 | if (!arg) |
156 | return std::nullopt; |
157 | return func(*arg); |
158 | } |
159 | |
160 | template < // |
161 | typename FuncTy, // |
162 | typename ArgTy, // |
163 | typename ResultTy = |
164 | std::invoke_result_t<FuncTy, decltype(std::declval<ArgTy>().v)>> |
165 | std::optional<ResultTy> maybeApplyToV(FuncTy &&func, const ArgTy *arg) { |
166 | if (!arg) |
167 | return std::nullopt; |
168 | return func(arg->v); |
169 | } |
170 | |
171 | std::optional<Object> getBaseObject(const Object &object, |
172 | semantics::SemanticsContext &semaCtx); |
173 | |
174 | namespace clause { |
175 | using Range = tomp::type::RangeT<ExprTy>; |
176 | using Mapper = tomp::type::MapperT<IdTy, ExprTy>; |
177 | using Iterator = tomp::type::IteratorT<TypeTy, IdTy, ExprTy>; |
178 | using IteratorSpecifier = tomp::type::IteratorSpecifierT<TypeTy, IdTy, ExprTy>; |
179 | using DefinedOperator = tomp::type::DefinedOperatorT<IdTy, ExprTy>; |
180 | using ProcedureDesignator = tomp::type::ProcedureDesignatorT<IdTy, ExprTy>; |
181 | using ReductionOperator = tomp::type::ReductionIdentifierT<IdTy, ExprTy>; |
182 | using DependenceType = tomp::type::DependenceType; |
183 | using Prescriptiveness = tomp::type::Prescriptiveness; |
184 | |
185 | // "Requires" clauses are handled early on, and the aggregated information |
186 | // is stored in the Symbol details of modules, programs, and subprograms. |
187 | // These clauses are still handled here to cover all alternatives in the |
188 | // main clause variant. |
189 | |
190 | using Absent = tomp::clause::AbsentT<TypeTy, IdTy, ExprTy>; |
191 | using AcqRel = tomp::clause::AcqRelT<TypeTy, IdTy, ExprTy>; |
192 | using Acquire = tomp::clause::AcquireT<TypeTy, IdTy, ExprTy>; |
193 | using AdjustArgs = tomp::clause::AdjustArgsT<TypeTy, IdTy, ExprTy>; |
194 | using Affinity = tomp::clause::AffinityT<TypeTy, IdTy, ExprTy>; |
195 | using Aligned = tomp::clause::AlignedT<TypeTy, IdTy, ExprTy>; |
196 | using Align = tomp::clause::AlignT<TypeTy, IdTy, ExprTy>; |
197 | using Allocate = tomp::clause::AllocateT<TypeTy, IdTy, ExprTy>; |
198 | using Allocator = tomp::clause::AllocatorT<TypeTy, IdTy, ExprTy>; |
199 | using AppendArgs = tomp::clause::AppendArgsT<TypeTy, IdTy, ExprTy>; |
200 | using AtomicDefaultMemOrder = |
201 | tomp::clause::AtomicDefaultMemOrderT<TypeTy, IdTy, ExprTy>; |
202 | using At = tomp::clause::AtT<TypeTy, IdTy, ExprTy>; |
203 | using Bind = tomp::clause::BindT<TypeTy, IdTy, ExprTy>; |
204 | using Capture = tomp::clause::CaptureT<TypeTy, IdTy, ExprTy>; |
205 | using Collapse = tomp::clause::CollapseT<TypeTy, IdTy, ExprTy>; |
206 | using Compare = tomp::clause::CompareT<TypeTy, IdTy, ExprTy>; |
207 | using Contains = tomp::clause::ContainsT<TypeTy, IdTy, ExprTy>; |
208 | using Copyin = tomp::clause::CopyinT<TypeTy, IdTy, ExprTy>; |
209 | using Copyprivate = tomp::clause::CopyprivateT<TypeTy, IdTy, ExprTy>; |
210 | using Defaultmap = tomp::clause::DefaultmapT<TypeTy, IdTy, ExprTy>; |
211 | using Default = tomp::clause::DefaultT<TypeTy, IdTy, ExprTy>; |
212 | using Depend = tomp::clause::DependT<TypeTy, IdTy, ExprTy>; |
213 | using Destroy = tomp::clause::DestroyT<TypeTy, IdTy, ExprTy>; |
214 | using Detach = tomp::clause::DetachT<TypeTy, IdTy, ExprTy>; |
215 | using Device = tomp::clause::DeviceT<TypeTy, IdTy, ExprTy>; |
216 | using DeviceType = tomp::clause::DeviceTypeT<TypeTy, IdTy, ExprTy>; |
217 | using DistSchedule = tomp::clause::DistScheduleT<TypeTy, IdTy, ExprTy>; |
218 | using Doacross = tomp::clause::DoacrossT<TypeTy, IdTy, ExprTy>; |
219 | using DynamicAllocators = |
220 | tomp::clause::DynamicAllocatorsT<TypeTy, IdTy, ExprTy>; |
221 | using Enter = tomp::clause::EnterT<TypeTy, IdTy, ExprTy>; |
222 | using Exclusive = tomp::clause::ExclusiveT<TypeTy, IdTy, ExprTy>; |
223 | using Fail = tomp::clause::FailT<TypeTy, IdTy, ExprTy>; |
224 | using Filter = tomp::clause::FilterT<TypeTy, IdTy, ExprTy>; |
225 | using Final = tomp::clause::FinalT<TypeTy, IdTy, ExprTy>; |
226 | using Firstprivate = tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>; |
227 | using From = tomp::clause::FromT<TypeTy, IdTy, ExprTy>; |
228 | using Full = tomp::clause::FullT<TypeTy, IdTy, ExprTy>; |
229 | using Grainsize = tomp::clause::GrainsizeT<TypeTy, IdTy, ExprTy>; |
230 | using HasDeviceAddr = tomp::clause::HasDeviceAddrT<TypeTy, IdTy, ExprTy>; |
231 | using Hint = tomp::clause::HintT<TypeTy, IdTy, ExprTy>; |
232 | using Holds = tomp::clause::HoldsT<TypeTy, IdTy, ExprTy>; |
233 | using If = tomp::clause::IfT<TypeTy, IdTy, ExprTy>; |
234 | using Inbranch = tomp::clause::InbranchT<TypeTy, IdTy, ExprTy>; |
235 | using Inclusive = tomp::clause::InclusiveT<TypeTy, IdTy, ExprTy>; |
236 | using Indirect = tomp::clause::IndirectT<TypeTy, IdTy, ExprTy>; |
237 | using Init = tomp::clause::InitT<TypeTy, IdTy, ExprTy>; |
238 | using Initializer = tomp::clause::InitializerT<TypeTy, IdTy, ExprTy>; |
239 | using InReduction = tomp::clause::InReductionT<TypeTy, IdTy, ExprTy>; |
240 | using IsDevicePtr = tomp::clause::IsDevicePtrT<TypeTy, IdTy, ExprTy>; |
241 | using Lastprivate = tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>; |
242 | using Linear = tomp::clause::LinearT<TypeTy, IdTy, ExprTy>; |
243 | using Link = tomp::clause::LinkT<TypeTy, IdTy, ExprTy>; |
244 | using Map = tomp::clause::MapT<TypeTy, IdTy, ExprTy>; |
245 | using Match = tomp::clause::MatchT<TypeTy, IdTy, ExprTy>; |
246 | using Mergeable = tomp::clause::MergeableT<TypeTy, IdTy, ExprTy>; |
247 | using Message = tomp::clause::MessageT<TypeTy, IdTy, ExprTy>; |
248 | using NoOpenmp = tomp::clause::NoOpenmpT<TypeTy, IdTy, ExprTy>; |
249 | using NoOpenmpRoutines = tomp::clause::NoOpenmpRoutinesT<TypeTy, IdTy, ExprTy>; |
250 | using NoOpenmpConstructs = |
251 | tomp::clause::NoOpenmpConstructsT<TypeTy, IdTy, ExprTy>; |
252 | using NoParallelism = tomp::clause::NoParallelismT<TypeTy, IdTy, ExprTy>; |
253 | using Nocontext = tomp::clause::NocontextT<TypeTy, IdTy, ExprTy>; |
254 | using Nogroup = tomp::clause::NogroupT<TypeTy, IdTy, ExprTy>; |
255 | using Nontemporal = tomp::clause::NontemporalT<TypeTy, IdTy, ExprTy>; |
256 | using Notinbranch = tomp::clause::NotinbranchT<TypeTy, IdTy, ExprTy>; |
257 | using Novariants = tomp::clause::NovariantsT<TypeTy, IdTy, ExprTy>; |
258 | using Nowait = tomp::clause::NowaitT<TypeTy, IdTy, ExprTy>; |
259 | using NumTasks = tomp::clause::NumTasksT<TypeTy, IdTy, ExprTy>; |
260 | using NumTeams = tomp::clause::NumTeamsT<TypeTy, IdTy, ExprTy>; |
261 | using NumThreads = tomp::clause::NumThreadsT<TypeTy, IdTy, ExprTy>; |
262 | using OmpxAttribute = tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy>; |
263 | using OmpxBare = tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy>; |
264 | using OmpxDynCgroupMem = tomp::clause::OmpxDynCgroupMemT<TypeTy, IdTy, ExprTy>; |
265 | using Ordered = tomp::clause::OrderedT<TypeTy, IdTy, ExprTy>; |
266 | using Order = tomp::clause::OrderT<TypeTy, IdTy, ExprTy>; |
267 | using Otherwise = tomp::clause::OtherwiseT<TypeTy, IdTy, ExprTy>; |
268 | using Partial = tomp::clause::PartialT<TypeTy, IdTy, ExprTy>; |
269 | using Priority = tomp::clause::PriorityT<TypeTy, IdTy, ExprTy>; |
270 | using Private = tomp::clause::PrivateT<TypeTy, IdTy, ExprTy>; |
271 | using ProcBind = tomp::clause::ProcBindT<TypeTy, IdTy, ExprTy>; |
272 | using Read = tomp::clause::ReadT<TypeTy, IdTy, ExprTy>; |
273 | using Reduction = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>; |
274 | using Relaxed = tomp::clause::RelaxedT<TypeTy, IdTy, ExprTy>; |
275 | using Release = tomp::clause::ReleaseT<TypeTy, IdTy, ExprTy>; |
276 | using ReverseOffload = tomp::clause::ReverseOffloadT<TypeTy, IdTy, ExprTy>; |
277 | using Safelen = tomp::clause::SafelenT<TypeTy, IdTy, ExprTy>; |
278 | using Schedule = tomp::clause::ScheduleT<TypeTy, IdTy, ExprTy>; |
279 | using SeqCst = tomp::clause::SeqCstT<TypeTy, IdTy, ExprTy>; |
280 | using Severity = tomp::clause::SeverityT<TypeTy, IdTy, ExprTy>; |
281 | using Shared = tomp::clause::SharedT<TypeTy, IdTy, ExprTy>; |
282 | using Simdlen = tomp::clause::SimdlenT<TypeTy, IdTy, ExprTy>; |
283 | using Simd = tomp::clause::SimdT<TypeTy, IdTy, ExprTy>; |
284 | using Sizes = tomp::clause::SizesT<TypeTy, IdTy, ExprTy>; |
285 | using Permutation = tomp::clause::PermutationT<TypeTy, IdTy, ExprTy>; |
286 | using TaskReduction = tomp::clause::TaskReductionT<TypeTy, IdTy, ExprTy>; |
287 | using ThreadLimit = tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy>; |
288 | using Threads = tomp::clause::ThreadsT<TypeTy, IdTy, ExprTy>; |
289 | using To = tomp::clause::ToT<TypeTy, IdTy, ExprTy>; |
290 | using UnifiedAddress = tomp::clause::UnifiedAddressT<TypeTy, IdTy, ExprTy>; |
291 | using UnifiedSharedMemory = |
292 | tomp::clause::UnifiedSharedMemoryT<TypeTy, IdTy, ExprTy>; |
293 | using SelfMaps = tomp::clause::SelfMapsT<TypeTy, IdTy, ExprTy>; |
294 | using Uniform = tomp::clause::UniformT<TypeTy, IdTy, ExprTy>; |
295 | using Unknown = tomp::clause::UnknownT<TypeTy, IdTy, ExprTy>; |
296 | using Untied = tomp::clause::UntiedT<TypeTy, IdTy, ExprTy>; |
297 | using Update = tomp::clause::UpdateT<TypeTy, IdTy, ExprTy>; |
298 | using UseDeviceAddr = tomp::clause::UseDeviceAddrT<TypeTy, IdTy, ExprTy>; |
299 | using UseDevicePtr = tomp::clause::UseDevicePtrT<TypeTy, IdTy, ExprTy>; |
300 | using UsesAllocators = tomp::clause::UsesAllocatorsT<TypeTy, IdTy, ExprTy>; |
301 | using Use = tomp::clause::UseT<TypeTy, IdTy, ExprTy>; |
302 | using Weak = tomp::clause::WeakT<TypeTy, IdTy, ExprTy>; |
303 | using When = tomp::clause::WhenT<TypeTy, IdTy, ExprTy>; |
304 | using Write = tomp::clause::WriteT<TypeTy, IdTy, ExprTy>; |
305 | } // namespace clause |
306 | |
307 | using tomp::type::operator==; |
308 | |
309 | struct CancellationConstructType { |
310 | using WrapperTrait = std::true_type; |
311 | llvm::omp::CancellationConstructType v; |
312 | }; |
313 | struct Depobj { |
314 | using EmptyTrait = std::true_type; |
315 | }; |
316 | struct Flush { |
317 | using EmptyTrait = std::true_type; |
318 | }; |
319 | struct MemoryOrder { |
320 | using EmptyTrait = std::true_type; |
321 | }; |
322 | struct Threadprivate { |
323 | using EmptyTrait = std::true_type; |
324 | }; |
325 | |
326 | using ClauseBase = tomp::ClauseT<TypeTy, IdTy, ExprTy, |
327 | // Extras... |
328 | CancellationConstructType, Depobj, Flush, |
329 | MemoryOrder, Threadprivate>; |
330 | |
331 | struct Clause : public ClauseBase { |
332 | Clause(ClauseBase &&base, const parser::CharBlock source = {}) |
333 | : ClauseBase(std::move(base)), source(source) {} |
334 | // "source" will be ignored by tomp::type::operator==. |
335 | parser::CharBlock source; |
336 | }; |
337 | |
338 | template <typename Specific> |
339 | Clause makeClause(llvm::omp::Clause id, Specific &&specific, |
340 | parser::CharBlock source = {}) { |
341 | return Clause(typename Clause::BaseT{id, specific}, source); |
342 | } |
343 | |
344 | Clause makeClause(const parser::OmpClause &cls, |
345 | semantics::SemanticsContext &semaCtx); |
346 | |
347 | List<Clause> makeClauses(const parser::OmpClauseList &clauses, |
348 | semantics::SemanticsContext &semaCtx); |
349 | |
350 | bool transferLocations(const List<Clause> &from, List<Clause> &to); |
351 | } // namespace Fortran::lower::omp |
352 | |
353 | #endif // FORTRAN_LOWER_OPENMP_CLAUSES_H |
354 | |