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