1 //===- ConstructDecompositionT.h -- Decomposing compound constructs -------===//
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 // Given a compound construct with a set of clauses, generate the list of
9 // constituent leaf constructs, each with a list of clauses that apply to it.
10 //
11 // Note: Clauses that are not originally present, but that are implied by the
12 // OpenMP spec are materialized, and are present in the output.
13 //
14 // Note: Composite constructs will also be broken up into leaf constructs.
15 // If composite constructs require processing as a whole, the lists of clauses
16 // for each leaf constituent should be merged.
17 //===----------------------------------------------------------------------===//
18 #ifndef LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
19 #define LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
20
21 #include "llvm/ADT/ArrayRef.h"
22 #include "llvm/ADT/STLExtras.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/ADT/iterator_range.h"
25 #include "llvm/Frontend/OpenMP/ClauseT.h"
26 #include "llvm/Frontend/OpenMP/OMP.h"
27
28 #include <iterator>
29 #include <list>
30 #include <optional>
31 #include <tuple>
32 #include <type_traits>
33 #include <unordered_map>
34 #include <unordered_set>
35 #include <utility>
36 #include <variant>
37
getWorksharing()38 static inline llvm::ArrayRef<llvm::omp::Directive> getWorksharing() {
39 static llvm::omp::Directive worksharing[] = {
40 llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_for,
41 llvm::omp::Directive::OMPD_scope, llvm::omp::Directive::OMPD_sections,
42 llvm::omp::Directive::OMPD_single, llvm::omp::Directive::OMPD_workshare,
43 };
44 return worksharing;
45 }
46
getWorksharingLoop()47 static inline llvm::ArrayRef<llvm::omp::Directive> getWorksharingLoop() {
48 static llvm::omp::Directive worksharingLoop[] = {
49 llvm::omp::Directive::OMPD_do,
50 llvm::omp::Directive::OMPD_for,
51 };
52 return worksharingLoop;
53 }
54
55 namespace detail {
56 template <typename Container, typename Predicate>
57 typename std::remove_reference_t<Container>::iterator
find_unique(Container && container,Predicate && pred)58 find_unique(Container &&container, Predicate &&pred) {
59 auto first = llvm::find_if(container, pred);
60 if (first == container.end())
61 return first;
62 auto second = std::find_if(std::next(first), container.end(), pred);
63 if (second == container.end())
64 return first;
65 return container.end();
66 }
67 } // namespace detail
68
69 namespace tomp {
70
71 // ClauseType - Either instance of ClauseT, or a type derived from ClauseT.
72 //
73 // This is the clause representation in the code using this infrastructure.
74 //
75 // HelperType - A class that implements two member functions:
76 //
77 // // Return the base object of the given object, if any.
78 // std::optional<Object> getBaseObject(const Object &object) const
79 // // Return the iteration variable of the outermost loop associated
80 // // with the construct being worked on, if any.
81 // std::optional<Object> getLoopIterVar() const
82 template <typename ClauseType, typename HelperType>
83 struct ConstructDecompositionT {
84 using ClauseTy = ClauseType;
85
86 using TypeTy = typename ClauseTy::TypeTy;
87 using IdTy = typename ClauseTy::IdTy;
88 using ExprTy = typename ClauseTy::ExprTy;
89 using HelperTy = HelperType;
90 using ObjectTy = tomp::ObjectT<IdTy, ExprTy>;
91
92 using ClauseSet = std::unordered_set<const ClauseTy *>;
93
ConstructDecompositionTConstructDecompositionT94 ConstructDecompositionT(uint32_t ver, HelperType &helper,
95 llvm::omp::Directive dir,
96 llvm::ArrayRef<ClauseTy> clauses)
97 : version(ver), construct(dir), helper(helper) {
98 for (const ClauseTy &clause : clauses)
99 nodes.push_back(&clause);
100
101 bool success = split();
102 if (!success)
103 return;
104
105 // Copy the individual leaf directives with their clauses to the
106 // output list. Copy by value, since we don't own the storage
107 // with the input clauses, and the internal representation uses
108 // clause addresses.
109 for (auto &leaf : leafs) {
110 output.push_back({leaf.id, {}});
111 auto &out = output.back();
112 for (const ClauseTy *c : leaf.clauses)
113 out.clauses.push_back(*c);
114 }
115 }
116
117 tomp::ListT<DirectiveWithClauses<ClauseType>> output;
118
119 private:
120 bool split();
121
122 struct LeafReprInternal {
123 llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown;
124 tomp::type::ListT<const ClauseTy *> clauses;
125 };
126
findDirectiveConstructDecompositionT127 LeafReprInternal *findDirective(llvm::omp::Directive dirId) {
128 auto found = llvm::find_if(
129 leafs, [&](const LeafReprInternal &leaf) { return leaf.id == dirId; });
130 return found != leafs.end() ? &*found : nullptr;
131 }
132
findClausesWithConstructDecompositionT133 ClauseSet *findClausesWith(const ObjectTy &object) {
134 if (auto found = syms.find(object.id()); found != syms.end())
135 return &found->second;
136 return nullptr;
137 }
138
139 template <typename S>
makeClauseConstructDecompositionT140 ClauseTy *makeClause(llvm::omp::Clause clauseId, S &&specific) {
141 implicit.push_back(typename ClauseTy::BaseT{clauseId, std::move(specific)});
142 return &implicit.back();
143 }
144
145 void addClauseSymsToMap(const ObjectTy &object, const ClauseTy *);
146 void addClauseSymsToMap(const tomp::ObjectListT<IdTy, ExprTy> &objects,
147 const ClauseTy *);
148 void addClauseSymsToMap(const TypeTy &item, const ClauseTy *);
149 void addClauseSymsToMap(const ExprTy &item, const ClauseTy *);
150 void addClauseSymsToMap(const tomp::clause::MapT<TypeTy, IdTy, ExprTy> &item,
151 const ClauseTy *);
152
153 template <typename U>
154 void addClauseSymsToMap(const std::optional<U> &item, const ClauseTy *);
155 template <typename U>
156 void addClauseSymsToMap(const tomp::ListT<U> &item, const ClauseTy *);
157 template <typename... U, size_t... Is>
158 void addClauseSymsToMap(const std::tuple<U...> &item, const ClauseTy *,
159 std::index_sequence<Is...> = {});
160 template <typename U>
161 std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, void>
162 addClauseSymsToMap(U &&item, const ClauseTy *);
163
164 template <typename U>
165 std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, void>
166 addClauseSymsToMap(U &&item, const ClauseTy *);
167
168 template <typename U>
169 std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, void>
170 addClauseSymsToMap(U &&item, const ClauseTy *);
171
172 template <typename U>
173 std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, void>
174 addClauseSymsToMap(U &&item, const ClauseTy *);
175
176 template <typename U>
177 std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, void>
178 addClauseSymsToMap(U &&item, const ClauseTy *);
179
180 template <typename U>
181 std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, void>
182 addClauseSymsToMap(U &&item, const ClauseTy *);
183
184 // Apply a clause to the only directive that allows it. If there are no
185 // directives that allow it, or if there is more that one, do not apply
186 // anything and return false, otherwise return true.
187 bool applyToUnique(const ClauseTy *node);
188
189 // Apply a clause to the first directive in given range that allows it.
190 // If such a directive does not exist, return false, otherwise return true.
191 template <typename Iterator>
192 bool applyToFirst(const ClauseTy *node, llvm::iterator_range<Iterator> range);
193
194 // Apply a clause to the innermost directive that allows it. If such a
195 // directive does not exist, return false, otherwise return true.
196 bool applyToInnermost(const ClauseTy *node);
197
198 // Apply a clause to the outermost directive that allows it. If such a
199 // directive does not exist, return false, otherwise return true.
200 bool applyToOutermost(const ClauseTy *node);
201
202 template <typename Predicate>
203 bool applyIf(const ClauseTy *node, Predicate shouldApply);
204
205 bool applyToAll(const ClauseTy *node);
206
207 template <typename Clause>
208 bool applyClause(Clause &&clause, const ClauseTy *node);
209
210 bool applyClause(const tomp::clause::CollapseT<TypeTy, IdTy, ExprTy> &clause,
211 const ClauseTy *);
212 bool applyClause(const tomp::clause::PrivateT<TypeTy, IdTy, ExprTy> &clause,
213 const ClauseTy *);
214 bool
215 applyClause(const tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy> &clause,
216 const ClauseTy *);
217 bool
218 applyClause(const tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy> &clause,
219 const ClauseTy *);
220 bool applyClause(const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
221 const ClauseTy *);
222 bool applyClause(const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
223 const ClauseTy *);
224 bool
225 applyClause(const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
226 const ClauseTy *);
227 bool applyClause(const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
228 const ClauseTy *);
229 bool applyClause(const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
230 const ClauseTy *);
231 bool applyClause(const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
232 const ClauseTy *);
233 bool applyClause(const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
234 const ClauseTy *);
235 bool applyClause(const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
236 const ClauseTy *);
237 bool applyClause(const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
238 const ClauseTy *);
239 bool
240 applyClause(const tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy> &clause,
241 const ClauseTy *);
242 bool applyClause(const tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy> &clause,
243 const ClauseTy *);
244
245 uint32_t version;
246 llvm::omp::Directive construct;
247 HelperType &helper;
248 ListT<LeafReprInternal> leafs;
249 tomp::ListT<const ClauseTy *> nodes;
250 std::list<ClauseTy> implicit; // Container for materialized implicit clauses.
251 // Inserting must preserve element addresses.
252 std::unordered_map<IdTy, ClauseSet> syms;
253 std::unordered_set<IdTy> mapBases;
254 };
255
256 // Deduction guide
257 template <typename ClauseType, typename HelperType>
258 ConstructDecompositionT(uint32_t, HelperType &, llvm::omp::Directive,
259 llvm::ArrayRef<ClauseType>)
260 -> ConstructDecompositionT<ClauseType, HelperType>;
261
262 template <typename C, typename H>
addClauseSymsToMap(const ObjectTy & object,const ClauseTy * node)263 void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ObjectTy &object,
264 const ClauseTy *node) {
265 syms[object.id()].insert(node);
266 }
267
268 template <typename C, typename H>
addClauseSymsToMap(const tomp::ObjectListT<IdTy,ExprTy> & objects,const ClauseTy * node)269 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
270 const tomp::ObjectListT<IdTy, ExprTy> &objects, const ClauseTy *node) {
271 for (auto &object : objects)
272 syms[object.id()].insert(node);
273 }
274
275 template <typename C, typename H>
addClauseSymsToMap(const TypeTy & item,const ClauseTy * node)276 void ConstructDecompositionT<C, H>::addClauseSymsToMap(const TypeTy &item,
277 const ClauseTy *node) {
278 // Nothing to do for types.
279 }
280
281 template <typename C, typename H>
addClauseSymsToMap(const ExprTy & item,const ClauseTy * node)282 void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ExprTy &item,
283 const ClauseTy *node) {
284 // Nothing to do for expressions.
285 }
286
287 template <typename C, typename H>
addClauseSymsToMap(const tomp::clause::MapT<TypeTy,IdTy,ExprTy> & item,const ClauseTy * node)288 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
289 const tomp::clause::MapT<TypeTy, IdTy, ExprTy> &item,
290 const ClauseTy *node) {
291 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(item.t);
292 addClauseSymsToMap(objects, node);
293 for (auto &object : objects) {
294 if (auto base = helper.getBaseObject(object))
295 mapBases.insert(base->id());
296 }
297 }
298
299 template <typename C, typename H>
300 template <typename U>
addClauseSymsToMap(const std::optional<U> & item,const ClauseTy * node)301 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
302 const std::optional<U> &item, const ClauseTy *node) {
303 if (item)
304 addClauseSymsToMap(*item, node);
305 }
306
307 template <typename C, typename H>
308 template <typename U>
addClauseSymsToMap(const tomp::ListT<U> & item,const ClauseTy * node)309 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
310 const tomp::ListT<U> &item, const ClauseTy *node) {
311 for (auto &s : item)
312 addClauseSymsToMap(s, node);
313 }
314
315 template <typename C, typename H>
316 template <typename... U, size_t... Is>
addClauseSymsToMap(const std::tuple<U...> & item,const ClauseTy * node,std::index_sequence<Is...>)317 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
318 const std::tuple<U...> &item, const ClauseTy *node,
319 std::index_sequence<Is...>) {
320 (void)node; // Silence strange warning from GCC.
321 (addClauseSymsToMap(std::get<Is>(item), node), ...);
322 }
323
324 template <typename C, typename H>
325 template <typename U>
326 std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, void>
addClauseSymsToMap(U && item,const ClauseTy * node)327 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
328 const ClauseTy *node) {
329 // Nothing to do for enums.
330 }
331
332 template <typename C, typename H>
333 template <typename U>
334 std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, void>
addClauseSymsToMap(U && item,const ClauseTy * node)335 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
336 const ClauseTy *node) {
337 // Nothing to do for an empty class.
338 }
339
340 template <typename C, typename H>
341 template <typename U>
342 std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, void>
addClauseSymsToMap(U && item,const ClauseTy * node)343 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
344 const ClauseTy *node) {
345 // Nothing to do for an incomplete class (they're empty).
346 }
347
348 template <typename C, typename H>
349 template <typename U>
350 std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, void>
addClauseSymsToMap(U && item,const ClauseTy * node)351 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
352 const ClauseTy *node) {
353 addClauseSymsToMap(item.v, node);
354 }
355
356 template <typename C, typename H>
357 template <typename U>
358 std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, void>
addClauseSymsToMap(U && item,const ClauseTy * node)359 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
360 const ClauseTy *node) {
361 constexpr size_t tuple_size =
362 std::tuple_size_v<llvm::remove_cvref_t<decltype(item.t)>>;
363 addClauseSymsToMap(item.t, node, std::make_index_sequence<tuple_size>{});
364 }
365
366 template <typename C, typename H>
367 template <typename U>
368 std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, void>
addClauseSymsToMap(U && item,const ClauseTy * node)369 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
370 const ClauseTy *node) {
371 std::visit([&](auto &&s) { addClauseSymsToMap(s, node); }, item.u);
372 }
373
374 // Apply a clause to the only directive that allows it. If there are no
375 // directives that allow it, or if there is more that one, do not apply
376 // anything and return false, otherwise return true.
377 template <typename C, typename H>
applyToUnique(const ClauseTy * node)378 bool ConstructDecompositionT<C, H>::applyToUnique(const ClauseTy *node) {
379 auto unique = detail::find_unique(leafs, [=](const auto &leaf) {
380 return llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version);
381 });
382
383 if (unique != leafs.end()) {
384 unique->clauses.push_back(node);
385 return true;
386 }
387 return false;
388 }
389
390 // Apply a clause to the first directive in given range that allows it.
391 // If such a directive does not exist, return false, otherwise return true.
392 template <typename C, typename H>
393 template <typename Iterator>
applyToFirst(const ClauseTy * node,llvm::iterator_range<Iterator> range)394 bool ConstructDecompositionT<C, H>::applyToFirst(
395 const ClauseTy *node, llvm::iterator_range<Iterator> range) {
396 if (range.empty())
397 return false;
398
399 for (auto &leaf : range) {
400 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
401 continue;
402 leaf.clauses.push_back(node);
403 return true;
404 }
405 return false;
406 }
407
408 // Apply a clause to the innermost directive that allows it. If such a
409 // directive does not exist, return false, otherwise return true.
410 template <typename C, typename H>
applyToInnermost(const ClauseTy * node)411 bool ConstructDecompositionT<C, H>::applyToInnermost(const ClauseTy *node) {
412 return applyToFirst(node, llvm::reverse(leafs));
413 }
414
415 // Apply a clause to the outermost directive that allows it. If such a
416 // directive does not exist, return false, otherwise return true.
417 template <typename C, typename H>
applyToOutermost(const ClauseTy * node)418 bool ConstructDecompositionT<C, H>::applyToOutermost(const ClauseTy *node) {
419 return applyToFirst(node, llvm::iterator_range(leafs));
420 }
421
422 template <typename C, typename H>
423 template <typename Predicate>
applyIf(const ClauseTy * node,Predicate shouldApply)424 bool ConstructDecompositionT<C, H>::applyIf(const ClauseTy *node,
425 Predicate shouldApply) {
426 bool applied = false;
427 for (auto &leaf : leafs) {
428 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
429 continue;
430 if (!shouldApply(leaf))
431 continue;
432 leaf.clauses.push_back(node);
433 applied = true;
434 }
435
436 return applied;
437 }
438
439 template <typename C, typename H>
applyToAll(const ClauseTy * node)440 bool ConstructDecompositionT<C, H>::applyToAll(const ClauseTy *node) {
441 return applyIf(node, [](auto) { return true; });
442 }
443
444 template <typename C, typename H>
445 template <typename Specific>
applyClause(Specific && specific,const ClauseTy * node)446 bool ConstructDecompositionT<C, H>::applyClause(Specific &&specific,
447 const ClauseTy *node) {
448 // The default behavior is to find the unique directive to which the
449 // given clause may be applied. If there are no such directives, or
450 // if there are multiple ones, flag an error.
451 // From "OpenMP Application Programming Interface", Version 5.2:
452 // S Some clauses are permitted only on a single leaf construct of the
453 // S combined or composite construct, in which case the effect is as if
454 // S the clause is applied to that specific construct. (p339, 31-33)
455 if (applyToUnique(node))
456 return true;
457
458 return false;
459 }
460
461 // COLLAPSE
462 // [5.2:93:20-21]
463 // Directives: distribute, do, for, loop, simd, taskloop
464 //
465 // [5.2:339:35]
466 // (35) The collapse clause is applied once to the combined or composite
467 // construct.
468 template <typename C, typename H>
applyClause(const tomp::clause::CollapseT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)469 bool ConstructDecompositionT<C, H>::applyClause(
470 const tomp::clause::CollapseT<TypeTy, IdTy, ExprTy> &clause,
471 const ClauseTy *node) {
472 // Apply "collapse" to the innermost directive. If it's not one that
473 // allows it flag an error.
474 if (!leafs.empty()) {
475 auto &last = leafs.back();
476
477 if (llvm::omp::isAllowedClauseForDirective(last.id, node->id, version)) {
478 last.clauses.push_back(node);
479 return true;
480 }
481 }
482
483 return false;
484 }
485
486 // PRIVATE
487 // [5.2:111:5-7]
488 // Directives: distribute, do, for, loop, parallel, scope, sections, simd,
489 // single, target, task, taskloop, teams
490 //
491 // [5.2:340:1-2]
492 // (1) The effect of the 1 private clause is as if it is applied only to the
493 // innermost leaf construct that permits it.
494 template <typename C, typename H>
applyClause(const tomp::clause::PrivateT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)495 bool ConstructDecompositionT<C, H>::applyClause(
496 const tomp::clause::PrivateT<TypeTy, IdTy, ExprTy> &clause,
497 const ClauseTy *node) {
498 return applyToInnermost(node);
499 }
500
501 // FIRSTPRIVATE
502 // [5.2:112:5-7]
503 // Directives: distribute, do, for, parallel, scope, sections, single, target,
504 // task, taskloop, teams
505 //
506 // [5.2:340:3-20]
507 // (3) The effect of the firstprivate clause is as if it is applied to one or
508 // more leaf constructs as follows:
509 // (5) To the distribute construct if it is among the constituent constructs;
510 // (6) To the teams construct if it is among the constituent constructs and the
511 // distribute construct is not;
512 // (8) To a worksharing construct that accepts the clause if one is among the
513 // constituent constructs;
514 // (9) To the taskloop construct if it is among the constituent constructs;
515 // (10) To the parallel construct if it is among the constituent constructs and
516 // neither a taskloop construct nor a worksharing construct that accepts
517 // the clause is among them;
518 // (12) To the target construct if it is among the constituent constructs and
519 // the same list item neither appears in a lastprivate clause nor is the
520 // base variable or base pointer of a list item that appears in a map
521 // clause.
522 //
523 // (15) If the parallel construct is among the constituent constructs and the
524 // effect is not as if the firstprivate clause is applied to it by the above
525 // rules, then the effect is as if the shared clause with the same list item is
526 // applied to the parallel construct.
527 // (17) If the teams construct is among the constituent constructs and the
528 // effect is not as if the firstprivate clause is applied to it by the above
529 // rules, then the effect is as if the shared clause with the same list item is
530 // applied to the teams construct.
531 template <typename C, typename H>
applyClause(const tomp::clause::FirstprivateT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)532 bool ConstructDecompositionT<C, H>::applyClause(
533 const tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy> &clause,
534 const ClauseTy *node) {
535 bool applied = false;
536
537 // [5.2:340:3-6]
538 auto dirDistribute = findDirective(llvm::omp::OMPD_distribute);
539 auto dirTeams = findDirective(llvm::omp::OMPD_teams);
540 if (dirDistribute != nullptr) {
541 dirDistribute->clauses.push_back(node);
542 applied = true;
543 // [5.2:340:17]
544 if (dirTeams != nullptr) {
545 auto *shared = makeClause(
546 llvm::omp::Clause::OMPC_shared,
547 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/clause.v});
548 dirTeams->clauses.push_back(shared);
549 }
550 } else if (dirTeams != nullptr) {
551 dirTeams->clauses.push_back(node);
552 applied = true;
553 }
554
555 // [5.2:340:8]
556 auto findWorksharing = [&]() {
557 auto worksharing = getWorksharing();
558 for (auto &leaf : leafs) {
559 auto found = llvm::find(worksharing, leaf.id);
560 if (found != std::end(worksharing))
561 return &leaf;
562 }
563 return static_cast<typename decltype(leafs)::value_type *>(nullptr);
564 };
565
566 auto dirWorksharing = findWorksharing();
567 if (dirWorksharing != nullptr) {
568 dirWorksharing->clauses.push_back(node);
569 applied = true;
570 }
571
572 // [5.2:340:9]
573 auto dirTaskloop = findDirective(llvm::omp::OMPD_taskloop);
574 if (dirTaskloop != nullptr) {
575 dirTaskloop->clauses.push_back(node);
576 applied = true;
577 }
578
579 // [5.2:340:10]
580 auto dirParallel = findDirective(llvm::omp::OMPD_parallel);
581 if (dirParallel != nullptr) {
582 if (dirTaskloop == nullptr && dirWorksharing == nullptr) {
583 dirParallel->clauses.push_back(node);
584 applied = true;
585 } else {
586 // [5.2:340:15]
587 auto *shared = makeClause(
588 llvm::omp::Clause::OMPC_shared,
589 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/clause.v});
590 dirParallel->clauses.push_back(shared);
591 }
592 }
593
594 // [5.2:340:12]
595 auto inLastprivate = [&](const ObjectTy &object) {
596 if (ClauseSet *set = findClausesWith(object)) {
597 return llvm::find_if(*set, [](const ClauseTy *c) {
598 return c->id == llvm::omp::Clause::OMPC_lastprivate;
599 }) != set->end();
600 }
601 return false;
602 };
603
604 auto dirTarget = findDirective(llvm::omp::OMPD_target);
605 if (dirTarget != nullptr) {
606 tomp::ObjectListT<IdTy, ExprTy> objects;
607 llvm::copy_if(
608 clause.v, std::back_inserter(objects), [&](const ObjectTy &object) {
609 return !inLastprivate(object) && !mapBases.count(object.id());
610 });
611 if (!objects.empty()) {
612 auto *firstp = makeClause(
613 llvm::omp::Clause::OMPC_firstprivate,
614 tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/objects});
615 dirTarget->clauses.push_back(firstp);
616 applied = true;
617 }
618 }
619
620 // "task" is not handled by any of the cases above.
621 if (auto dirTask = findDirective(llvm::omp::OMPD_task)) {
622 dirTask->clauses.push_back(node);
623 applied = true;
624 }
625
626 return applied;
627 }
628
629 // LASTPRIVATE
630 // [5.2:115:7-8]
631 // Directives: distribute, do, for, loop, sections, simd, taskloop
632 //
633 // [5.2:340:21-30]
634 // (21) The effect of the lastprivate clause is as if it is applied to all leaf
635 // constructs that permit the clause.
636 // (22) If the parallel construct is among the constituent constructs and the
637 // list item is not also specified in the firstprivate clause, then the effect
638 // of the lastprivate clause is as if the shared clause with the same list item
639 // is applied to the parallel construct.
640 // (24) If the teams construct is among the constituent constructs and the list
641 // item is not also specified in the firstprivate clause, then the effect of the
642 // lastprivate clause is as if the shared clause with the same list item is
643 // applied to the teams construct.
644 // (27) If the target construct is among the constituent constructs and the list
645 // item is not the base variable or base pointer of a list item that appears in
646 // a map clause, the effect of the lastprivate clause is as if the same list
647 // item appears in a map clause with a map-type of tofrom.
648 template <typename C, typename H>
applyClause(const tomp::clause::LastprivateT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)649 bool ConstructDecompositionT<C, H>::applyClause(
650 const tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy> &clause,
651 const ClauseTy *node) {
652 bool applied = false;
653
654 // [5.2:340:21]
655 applied = applyToAll(node);
656 if (!applied)
657 return false;
658
659 auto inFirstprivate = [&](const ObjectTy &object) {
660 if (ClauseSet *set = findClausesWith(object)) {
661 return llvm::find_if(*set, [](const ClauseTy *c) {
662 return c->id == llvm::omp::Clause::OMPC_firstprivate;
663 }) != set->end();
664 }
665 return false;
666 };
667
668 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
669
670 // Prepare list of objects that could end up in a "shared" clause.
671 tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
672 llvm::copy_if(
673 objects, std::back_inserter(sharedObjects),
674 [&](const ObjectTy &object) { return !inFirstprivate(object); });
675
676 if (!sharedObjects.empty()) {
677 // [5.2:340:22]
678 if (auto dirParallel = findDirective(llvm::omp::OMPD_parallel)) {
679 auto *shared = makeClause(
680 llvm::omp::Clause::OMPC_shared,
681 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
682 dirParallel->clauses.push_back(shared);
683 applied = true;
684 }
685
686 // [5.2:340:24]
687 if (auto dirTeams = findDirective(llvm::omp::OMPD_teams)) {
688 auto *shared = makeClause(
689 llvm::omp::Clause::OMPC_shared,
690 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
691 dirTeams->clauses.push_back(shared);
692 applied = true;
693 }
694 }
695
696 // [5.2:340:27]
697 if (auto dirTarget = findDirective(llvm::omp::OMPD_target)) {
698 tomp::ObjectListT<IdTy, ExprTy> tofrom;
699 llvm::copy_if(
700 objects, std::back_inserter(tofrom),
701 [&](const ObjectTy &object) { return !mapBases.count(object.id()); });
702
703 if (!tofrom.empty()) {
704 using MapType =
705 typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
706 auto *map =
707 makeClause(llvm::omp::Clause::OMPC_map,
708 tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
709 {/*MapType=*/MapType::Tofrom,
710 /*MapTypeModifier=*/std::nullopt,
711 /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
712 /*LocatorList=*/std::move(tofrom)}});
713 dirTarget->clauses.push_back(map);
714 applied = true;
715 }
716 }
717
718 return applied;
719 }
720
721 // SHARED
722 // [5.2:110:5-6]
723 // Directives: parallel, task, taskloop, teams
724 //
725 // [5.2:340:31-32]
726 // (31) The effect of the shared, default, thread_limit, or order clause is as
727 // if it is applied to all leaf constructs that permit the clause.
728 template <typename C, typename H>
applyClause(const tomp::clause::SharedT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)729 bool ConstructDecompositionT<C, H>::applyClause(
730 const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
731 const ClauseTy *node) {
732 // [5.2:340:31]
733 return applyToAll(node);
734 }
735
736 // DEFAULT
737 // [5.2:109:5-6]
738 // Directives: parallel, task, taskloop, teams
739 //
740 // [5.2:340:31-32]
741 // (31) The effect of the shared, default, thread_limit, or order clause is as
742 // if it is applied to all leaf constructs that permit the clause.
743 template <typename C, typename H>
applyClause(const tomp::clause::DefaultT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)744 bool ConstructDecompositionT<C, H>::applyClause(
745 const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
746 const ClauseTy *node) {
747 // [5.2:340:31]
748 return applyToAll(node);
749 }
750
751 // THREAD_LIMIT
752 // [5.2:277:14-15]
753 // Directives: target, teams
754 //
755 // [5.2:340:31-32]
756 // (31) The effect of the shared, default, thread_limit, or order clause is as
757 // if it is applied to all leaf constructs that permit the clause.
758 template <typename C, typename H>
applyClause(const tomp::clause::ThreadLimitT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)759 bool ConstructDecompositionT<C, H>::applyClause(
760 const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
761 const ClauseTy *node) {
762 // [5.2:340:31]
763 return applyToAll(node);
764 }
765
766 // ORDER
767 // [5.2:234:3-4]
768 // Directives: distribute, do, for, loop, simd
769 //
770 // [5.2:340:31-32]
771 // (31) The effect of the shared, default, thread_limit, or order clause is as
772 // if it is applied to all leaf constructs that permit the clause.
773 template <typename C, typename H>
applyClause(const tomp::clause::OrderT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)774 bool ConstructDecompositionT<C, H>::applyClause(
775 const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
776 const ClauseTy *node) {
777 // [5.2:340:31]
778 return applyToAll(node);
779 }
780
781 // ALLOCATE
782 // [5.2:178:7-9]
783 // Directives: allocators, distribute, do, for, parallel, scope, sections,
784 // single, target, task, taskgroup, taskloop, teams
785 //
786 // [5.2:340:33-35]
787 // (33) The effect of the allocate clause is as if it is applied to all leaf
788 // constructs that permit the clause and to which a data-sharing attribute
789 // clause that may create a private copy of the same list item is applied.
790 template <typename C, typename H>
applyClause(const tomp::clause::AllocateT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)791 bool ConstructDecompositionT<C, H>::applyClause(
792 const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
793 const ClauseTy *node) {
794 // This one needs to be applied at the end, once we know which clauses are
795 // assigned to which leaf constructs.
796
797 // [5.2:340:33]
798 bool applied = applyIf(node, [&](const auto &leaf) {
799 return llvm::any_of(leaf.clauses, [&](const ClauseTy *n) {
800 return llvm::omp::isPrivatizingClause(n->id);
801 });
802 });
803
804 return applied;
805 }
806
807 // REDUCTION
808 // [5.2:134:17-18]
809 // Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams
810 //
811 // [5.2:340:36-37], [5.2:341:1-13]
812 // (36) The effect of the reduction clause is as if it is applied to all leaf
813 // constructs that permit the clause, except for the following constructs:
814 // (1) The parallel construct, when combined with the sections,
815 // worksharing-loop, loop, or taskloop construct; and
816 // (3) The teams construct, when combined with the loop construct.
817 // (4) For the parallel and teams constructs above, the effect of the reduction
818 // clause instead is as if each list item or, for any list item that is an array
819 // item, its corresponding base array or base pointer appears in a shared clause
820 // for the construct.
821 // (6) If the task reduction-modifier is specified, the effect is as if it only
822 // modifies the behavior of the reduction clause on the innermost leaf construct
823 // that accepts the modifier (see Section 5.5.8).
824 // (8) If the inscan reduction-modifier is specified, the effect is as if it
825 // modifies the behavior of the reduction clause on all constructs of the
826 // combined construct to which the clause is applied and that accept the
827 // modifier.
828 // (10) If a list item in a reduction clause on a combined target construct does
829 // not have the same base variable or base pointer as a list item in a map
830 // clause on the construct, then the effect is as if the list item in the
831 // reduction clause appears as a list item in a map clause with a map-type of
832 // tofrom.
833 template <typename C, typename H>
applyClause(const tomp::clause::ReductionT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)834 bool ConstructDecompositionT<C, H>::applyClause(
835 const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
836 const ClauseTy *node) {
837 using ReductionTy = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>;
838
839 // [5.2:340:36], [5.2:341:1], [5.2:341:3]
840 bool applyToParallel = true, applyToTeams = true;
841
842 auto dirParallel = findDirective(llvm::omp::Directive::OMPD_parallel);
843 if (dirParallel) {
844 auto exclusions = llvm::concat<const llvm::omp::Directive>(
845 getWorksharingLoop(), tomp::ListT<llvm::omp::Directive>{
846 llvm::omp::Directive::OMPD_loop,
847 llvm::omp::Directive::OMPD_sections,
848 llvm::omp::Directive::OMPD_taskloop,
849 });
850 auto present = [&](llvm::omp::Directive id) {
851 return findDirective(id) != nullptr;
852 };
853
854 if (llvm::any_of(exclusions, present))
855 applyToParallel = false;
856 }
857
858 auto dirTeams = findDirective(llvm::omp::Directive::OMPD_teams);
859 if (dirTeams) {
860 // The only exclusion is OMPD_loop.
861 if (findDirective(llvm::omp::Directive::OMPD_loop))
862 applyToTeams = false;
863 }
864
865 using ReductionModifier = typename ReductionTy::ReductionModifier;
866 using ReductionIdentifiers = typename ReductionTy::ReductionIdentifiers;
867
868 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
869 auto &modifier = std::get<std::optional<ReductionModifier>>(clause.t);
870
871 // Apply the reduction clause first to all directives according to the spec.
872 // If the reduction was applied at least once, proceed with the data sharing
873 // side-effects.
874 bool applied = false;
875
876 // [5.2:341:6], [5.2:341:8]
877 auto isValidModifier = [](llvm::omp::Directive dir, ReductionModifier mod,
878 bool alreadyApplied) {
879 switch (mod) {
880 case ReductionModifier::Inscan:
881 // According to [5.2:135:11-13], "inscan" only applies to
882 // worksharing-loop, worksharing-loop-simd, or "simd" constructs.
883 return dir == llvm::omp::Directive::OMPD_simd ||
884 llvm::is_contained(getWorksharingLoop(), dir);
885 case ReductionModifier::Task:
886 if (alreadyApplied)
887 return false;
888 // According to [5.2:135:16-18], "task" only applies to "parallel" and
889 // worksharing constructs.
890 return dir == llvm::omp::Directive::OMPD_parallel ||
891 llvm::is_contained(getWorksharing(), dir);
892 case ReductionModifier::Default:
893 return true;
894 }
895 llvm_unreachable("Unexpected modifier");
896 };
897
898 auto *unmodified = makeClause(
899 llvm::omp::Clause::OMPC_reduction,
900 ReductionTy{
901 {/*ReductionModifier=*/std::nullopt,
902 /*ReductionIdentifiers=*/std::get<ReductionIdentifiers>(clause.t),
903 /*List=*/objects}});
904
905 ReductionModifier effective = modifier.value_or(ReductionModifier::Default);
906 bool effectiveApplied = false;
907 // Walk over the leaf constructs starting from the innermost, and apply
908 // the clause as required by the spec.
909 for (auto &leaf : llvm::reverse(leafs)) {
910 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
911 continue;
912 if (!applyToParallel && &leaf == dirParallel)
913 continue;
914 if (!applyToTeams && &leaf == dirTeams)
915 continue;
916 // Some form of the clause will be applied past this point.
917 if (isValidModifier(leaf.id, effective, effectiveApplied)) {
918 // Apply clause with modifier.
919 leaf.clauses.push_back(node);
920 effectiveApplied = true;
921 } else {
922 // Apply clause without modifier.
923 leaf.clauses.push_back(unmodified);
924 }
925 // The modifier must be applied to some construct.
926 applied = effectiveApplied;
927 }
928
929 if (!applied)
930 return false;
931
932 tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
933 llvm::transform(objects, std::back_inserter(sharedObjects),
934 [&](const ObjectTy &object) {
935 auto maybeBase = helper.getBaseObject(object);
936 return maybeBase ? *maybeBase : object;
937 });
938
939 // [5.2:341:4]
940 if (!sharedObjects.empty()) {
941 if (dirParallel && !applyToParallel) {
942 auto *shared = makeClause(
943 llvm::omp::Clause::OMPC_shared,
944 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
945 dirParallel->clauses.push_back(shared);
946 }
947 if (dirTeams && !applyToTeams) {
948 auto *shared = makeClause(
949 llvm::omp::Clause::OMPC_shared,
950 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
951 dirTeams->clauses.push_back(shared);
952 }
953 }
954
955 // [5.2:341:10]
956 auto dirTarget = findDirective(llvm::omp::Directive::OMPD_target);
957 if (dirTarget && leafs.size() > 1) {
958 tomp::ObjectListT<IdTy, ExprTy> tofrom;
959 llvm::copy_if(objects, std::back_inserter(tofrom),
960 [&](const ObjectTy &object) {
961 if (auto maybeBase = helper.getBaseObject(object))
962 return !mapBases.count(maybeBase->id());
963 return !mapBases.count(object.id()); // XXX is this ok?
964 });
965 if (!tofrom.empty()) {
966 using MapType =
967 typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
968 auto *map = makeClause(
969 llvm::omp::Clause::OMPC_map,
970 tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
971 {/*MapType=*/MapType::Tofrom, /*MapTypeModifier=*/std::nullopt,
972 /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
973 /*LocatorList=*/std::move(tofrom)}});
974
975 dirTarget->clauses.push_back(map);
976 applied = true;
977 }
978 }
979
980 return applied;
981 }
982
983 // IF
984 // [5.2:72:7-9]
985 // Directives: cancel, parallel, simd, target, target data, target enter data,
986 // target exit data, target update, task, taskloop
987 //
988 // [5.2:72:15-18]
989 // (15) For combined or composite constructs, the if clause only applies to the
990 // semantics of the construct named in the directive-name-modifier.
991 // (16) For a combined or composite construct, if no directive-name-modifier is
992 // specified then the if clause applies to all constituent constructs to which
993 // an if clause can apply.
994 template <typename C, typename H>
applyClause(const tomp::clause::IfT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)995 bool ConstructDecompositionT<C, H>::applyClause(
996 const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
997 const ClauseTy *node) {
998 using DirectiveNameModifier =
999 typename clause::IfT<TypeTy, IdTy, ExprTy>::DirectiveNameModifier;
1000 using IfExpression = typename clause::IfT<TypeTy, IdTy, ExprTy>::IfExpression;
1001 auto &modifier = std::get<std::optional<DirectiveNameModifier>>(clause.t);
1002
1003 if (modifier) {
1004 llvm::omp::Directive dirId = *modifier;
1005 auto *unmodified =
1006 makeClause(llvm::omp::Clause::OMPC_if,
1007 tomp::clause::IfT<TypeTy, IdTy, ExprTy>{
1008 {/*DirectiveNameModifier=*/std::nullopt,
1009 /*IfExpression=*/std::get<IfExpression>(clause.t)}});
1010
1011 if (auto *hasDir = findDirective(dirId)) {
1012 hasDir->clauses.push_back(unmodified);
1013 return true;
1014 }
1015 return false;
1016 }
1017
1018 return applyToAll(node);
1019 }
1020
1021 // LINEAR
1022 // [5.2:118:1-2]
1023 // Directives: declare simd, do, for, simd
1024 //
1025 // [5.2:341:15-22]
1026 // (15.1) The effect of the linear clause is as if it is applied to the
1027 // innermost leaf construct.
1028 // (15.2) Additionally, if the list item is not the iteration variable of a simd
1029 // or worksharing-loop SIMD construct, the effect on the outer leaf constructs
1030 // is as if the list item was specified in firstprivate and lastprivate clauses
1031 // on the combined or composite construct, with the rules specified above
1032 // applied.
1033 // (19) If a list item of the linear clause is the iteration variable of a simd
1034 // or worksharing-loop SIMD construct and it is not declared in the construct,
1035 // the effect on the outer leaf constructs is as if the list item was specified
1036 // in a lastprivate clause on the combined or composite construct with the rules
1037 // specified above applied.
1038 template <typename C, typename H>
applyClause(const tomp::clause::LinearT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)1039 bool ConstructDecompositionT<C, H>::applyClause(
1040 const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
1041 const ClauseTy *node) {
1042 // [5.2:341:15.1]
1043 if (!applyToInnermost(node))
1044 return false;
1045
1046 // [5.2:341:15.2], [5.2:341:19]
1047 auto dirSimd = findDirective(llvm::omp::Directive::OMPD_simd);
1048 std::optional<ObjectTy> iterVar = helper.getLoopIterVar();
1049 const auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
1050
1051 // Lists of objects that will be used to construct "firstprivate" and
1052 // "lastprivate" clauses.
1053 tomp::ObjectListT<IdTy, ExprTy> first, last;
1054
1055 for (const ObjectTy &object : objects) {
1056 last.push_back(object);
1057 if (!dirSimd || !iterVar || object.id() != iterVar->id())
1058 first.push_back(object);
1059 }
1060
1061 if (!first.empty()) {
1062 auto *firstp = makeClause(
1063 llvm::omp::Clause::OMPC_firstprivate,
1064 tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/first});
1065 nodes.push_back(firstp); // Appending to the main clause list.
1066 }
1067 if (!last.empty()) {
1068 auto *lastp =
1069 makeClause(llvm::omp::Clause::OMPC_lastprivate,
1070 tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>{
1071 {/*LastprivateModifier=*/std::nullopt, /*List=*/last}});
1072 nodes.push_back(lastp); // Appending to the main clause list.
1073 }
1074 return true;
1075 }
1076
1077 // NOWAIT
1078 // [5.2:308:11-13]
1079 // Directives: dispatch, do, for, interop, scope, sections, single, target,
1080 // target enter data, target exit data, target update, taskwait, workshare
1081 //
1082 // [5.2:341:23]
1083 // (23) The effect of the nowait clause is as if it is applied to the outermost
1084 // leaf construct that permits it.
1085 template <typename C, typename H>
applyClause(const tomp::clause::NowaitT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)1086 bool ConstructDecompositionT<C, H>::applyClause(
1087 const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
1088 const ClauseTy *node) {
1089 return applyToOutermost(node);
1090 }
1091
1092 template <typename C, typename H>
applyClause(const tomp::clause::OmpxBareT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)1093 bool ConstructDecompositionT<C, H>::applyClause(
1094 const tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy> &clause,
1095 const ClauseTy *node) {
1096 return applyToOutermost(node);
1097 }
1098
1099 template <typename C, typename H>
applyClause(const tomp::clause::OmpxAttributeT<TypeTy,IdTy,ExprTy> & clause,const ClauseTy * node)1100 bool ConstructDecompositionT<C, H>::applyClause(
1101 const tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy> &clause,
1102 const ClauseTy *node) {
1103 return applyToAll(node);
1104 }
1105
split()1106 template <typename C, typename H> bool ConstructDecompositionT<C, H>::split() {
1107 bool success = true;
1108
1109 auto isImplicit = [this](const ClauseTy *node) {
1110 return llvm::is_contained(llvm::make_pointer_range(implicit), node);
1111 };
1112
1113 for (llvm::omp::Directive leaf :
1114 llvm::omp::getLeafConstructsOrSelf(construct))
1115 leafs.push_back(LeafReprInternal{leaf, /*clauses=*/{}});
1116
1117 for (const ClauseTy *node : nodes)
1118 addClauseSymsToMap(*node, node);
1119
1120 // First we need to apply LINEAR, because it can generate additional
1121 // "firstprivate" and "lastprivate" clauses that apply to the combined/
1122 // composite construct.
1123 // Collect them separately, because they may modify the clause list.
1124 llvm::SmallVector<const ClauseTy *> linears;
1125 for (const ClauseTy *node : nodes) {
1126 if (node->id == llvm::omp::Clause::OMPC_linear)
1127 linears.push_back(node);
1128 }
1129 for (const auto *node : linears) {
1130 success = success &&
1131 applyClause(std::get<tomp::clause::LinearT<TypeTy, IdTy, ExprTy>>(
1132 node->u),
1133 node);
1134 }
1135
1136 // "allocate" clauses need to be applied last since they need to see
1137 // which directives have data-privatizing clauses.
1138 auto skip = [](const ClauseTy *node) {
1139 switch (node->id) {
1140 case llvm::omp::Clause::OMPC_allocate:
1141 case llvm::omp::Clause::OMPC_linear:
1142 return true;
1143 default:
1144 return false;
1145 }
1146 };
1147
1148 // Apply (almost) all clauses.
1149 for (const ClauseTy *node : nodes) {
1150 if (skip(node))
1151 continue;
1152 bool result =
1153 std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1154 if (!isImplicit(node))
1155 success = success && result;
1156 }
1157
1158 // Apply "allocate".
1159 for (const ClauseTy *node : nodes) {
1160 if (node->id != llvm::omp::Clause::OMPC_allocate)
1161 continue;
1162 success =
1163 success &&
1164 std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1165 }
1166
1167 return success;
1168 }
1169
1170 } // namespace tomp
1171
1172 #endif // LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
1173