xref: /llvm-project/llvm/include/llvm/Frontend/OpenMP/ConstructDecompositionT.h (revision 7c9404c279cfa13e24a043e6357cc85bd12f55f1)
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 
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 
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
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 
94   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 
127   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 
133   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>
140   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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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   auto canMakePrivateCopy = [](llvm::omp::Clause id) {
799     switch (id) {
800     // Clauses with "privatization" property:
801     case llvm::omp::Clause::OMPC_firstprivate:
802     case llvm::omp::Clause::OMPC_in_reduction:
803     case llvm::omp::Clause::OMPC_lastprivate:
804     case llvm::omp::Clause::OMPC_linear:
805     case llvm::omp::Clause::OMPC_private:
806     case llvm::omp::Clause::OMPC_reduction:
807     case llvm::omp::Clause::OMPC_task_reduction:
808       return true;
809     default:
810       return false;
811     }
812   };
813 
814   bool applied = applyIf(node, [&](const auto &leaf) {
815     return llvm::any_of(leaf.clauses, [&](const ClauseTy *n) {
816       return canMakePrivateCopy(n->id);
817     });
818   });
819 
820   return applied;
821 }
822 
823 // REDUCTION
824 // [5.2:134:17-18]
825 // Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams
826 //
827 // [5.2:340:36-37], [5.2:341:1-13]
828 // (36) The effect of the reduction clause is as if it is applied to all leaf
829 // constructs that permit the clause, except for the following constructs:
830 //  (1) The parallel construct, when combined with the sections,
831 //      worksharing-loop, loop, or taskloop construct; and
832 //  (3) The teams construct, when combined with the loop construct.
833 // (4) For the parallel and teams constructs above, the effect of the reduction
834 // clause instead is as if each list item or, for any list item that is an array
835 // item, its corresponding base array or base pointer appears in a shared clause
836 // for the construct.
837 // (6) If the task reduction-modifier is specified, the effect is as if it only
838 // modifies the behavior of the reduction clause on the innermost leaf construct
839 // that accepts the modifier (see Section 5.5.8).
840 // (8) If the inscan reduction-modifier is specified, the effect is as if it
841 // modifies the behavior of the reduction clause on all constructs of the
842 // combined construct to which the clause is applied and that accept the
843 // modifier.
844 // (10) If a list item in a reduction clause on a combined target construct does
845 // not have the same base variable or base pointer as a list item in a map
846 // clause on the construct, then the effect is as if the list item in the
847 // reduction clause appears as a list item in a map clause with a map-type of
848 // tofrom.
849 template <typename C, typename H>
850 bool ConstructDecompositionT<C, H>::applyClause(
851     const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
852     const ClauseTy *node) {
853   using ReductionTy = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>;
854 
855   // [5.2:340:36], [5.2:341:1], [5.2:341:3]
856   bool applyToParallel = true, applyToTeams = true;
857 
858   auto dirParallel = findDirective(llvm::omp::Directive::OMPD_parallel);
859   if (dirParallel) {
860     auto exclusions = llvm::concat<const llvm::omp::Directive>(
861         getWorksharingLoop(), tomp::ListT<llvm::omp::Directive>{
862                                   llvm::omp::Directive::OMPD_loop,
863                                   llvm::omp::Directive::OMPD_sections,
864                                   llvm::omp::Directive::OMPD_taskloop,
865                               });
866     auto present = [&](llvm::omp::Directive id) {
867       return findDirective(id) != nullptr;
868     };
869 
870     if (llvm::any_of(exclusions, present))
871       applyToParallel = false;
872   }
873 
874   auto dirTeams = findDirective(llvm::omp::Directive::OMPD_teams);
875   if (dirTeams) {
876     // The only exclusion is OMPD_loop.
877     if (findDirective(llvm::omp::Directive::OMPD_loop))
878       applyToTeams = false;
879   }
880 
881   using ReductionModifier = typename ReductionTy::ReductionModifier;
882   using ReductionIdentifiers = typename ReductionTy::ReductionIdentifiers;
883 
884   auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
885   auto &modifier = std::get<std::optional<ReductionModifier>>(clause.t);
886 
887   // Apply the reduction clause first to all directives according to the spec.
888   // If the reduction was applied at least once, proceed with the data sharing
889   // side-effects.
890   bool applied = false;
891 
892   // [5.2:341:6], [5.2:341:8]
893   auto isValidModifier = [](llvm::omp::Directive dir, ReductionModifier mod,
894                             bool alreadyApplied) {
895     switch (mod) {
896     case ReductionModifier::Inscan:
897       // According to [5.2:135:11-13], "inscan" only applies to
898       // worksharing-loop, worksharing-loop-simd, or "simd" constructs.
899       return dir == llvm::omp::Directive::OMPD_simd ||
900              llvm::is_contained(getWorksharingLoop(), dir);
901     case ReductionModifier::Task:
902       if (alreadyApplied)
903         return false;
904       // According to [5.2:135:16-18], "task" only applies to "parallel" and
905       // worksharing constructs.
906       return dir == llvm::omp::Directive::OMPD_parallel ||
907              llvm::is_contained(getWorksharing(), dir);
908     case ReductionModifier::Default:
909       return true;
910     }
911     llvm_unreachable("Unexpected modifier");
912   };
913 
914   auto *unmodified = makeClause(
915       llvm::omp::Clause::OMPC_reduction,
916       ReductionTy{
917           {/*ReductionModifier=*/std::nullopt,
918            /*ReductionIdentifiers=*/std::get<ReductionIdentifiers>(clause.t),
919            /*List=*/objects}});
920 
921   ReductionModifier effective = modifier.value_or(ReductionModifier::Default);
922   bool effectiveApplied = false;
923   // Walk over the leaf constructs starting from the innermost, and apply
924   // the clause as required by the spec.
925   for (auto &leaf : llvm::reverse(leafs)) {
926     if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
927       continue;
928     if (!applyToParallel && &leaf == dirParallel)
929       continue;
930     if (!applyToTeams && &leaf == dirTeams)
931       continue;
932     // Some form of the clause will be applied past this point.
933     if (isValidModifier(leaf.id, effective, effectiveApplied)) {
934       // Apply clause with modifier.
935       leaf.clauses.push_back(node);
936       effectiveApplied = true;
937     } else {
938       // Apply clause without modifier.
939       leaf.clauses.push_back(unmodified);
940     }
941     // The modifier must be applied to some construct.
942     applied = effectiveApplied;
943   }
944 
945   if (!applied)
946     return false;
947 
948   tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
949   llvm::transform(objects, std::back_inserter(sharedObjects),
950                   [&](const ObjectTy &object) {
951                     auto maybeBase = helper.getBaseObject(object);
952                     return maybeBase ? *maybeBase : object;
953                   });
954 
955   // [5.2:341:4]
956   if (!sharedObjects.empty()) {
957     if (dirParallel && !applyToParallel) {
958       auto *shared = makeClause(
959           llvm::omp::Clause::OMPC_shared,
960           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
961       dirParallel->clauses.push_back(shared);
962     }
963     if (dirTeams && !applyToTeams) {
964       auto *shared = makeClause(
965           llvm::omp::Clause::OMPC_shared,
966           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
967       dirTeams->clauses.push_back(shared);
968     }
969   }
970 
971   // [5.2:341:10]
972   auto dirTarget = findDirective(llvm::omp::Directive::OMPD_target);
973   if (dirTarget && leafs.size() > 1) {
974     tomp::ObjectListT<IdTy, ExprTy> tofrom;
975     llvm::copy_if(objects, std::back_inserter(tofrom),
976                   [&](const ObjectTy &object) {
977                     if (auto maybeBase = helper.getBaseObject(object))
978                       return !mapBases.count(maybeBase->id());
979                     return !mapBases.count(object.id()); // XXX is this ok?
980                   });
981     if (!tofrom.empty()) {
982       using MapType =
983           typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
984       auto *map = makeClause(
985           llvm::omp::Clause::OMPC_map,
986           tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
987               {/*MapType=*/MapType::Tofrom, /*MapTypeModifier=*/std::nullopt,
988                /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
989                /*LocatorList=*/std::move(tofrom)}});
990 
991       dirTarget->clauses.push_back(map);
992       applied = true;
993     }
994   }
995 
996   return applied;
997 }
998 
999 // IF
1000 // [5.2:72:7-9]
1001 // Directives: cancel, parallel, simd, target, target data, target enter data,
1002 // target exit data, target update, task, taskloop
1003 //
1004 // [5.2:72:15-18]
1005 // (15) For combined or composite constructs, the if clause only applies to the
1006 // semantics of the construct named in the directive-name-modifier.
1007 // (16) For a combined or composite construct, if no directive-name-modifier is
1008 // specified then the if clause applies to all constituent constructs to which
1009 // an if clause can apply.
1010 template <typename C, typename H>
1011 bool ConstructDecompositionT<C, H>::applyClause(
1012     const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
1013     const ClauseTy *node) {
1014   using DirectiveNameModifier =
1015       typename clause::IfT<TypeTy, IdTy, ExprTy>::DirectiveNameModifier;
1016   using IfExpression = typename clause::IfT<TypeTy, IdTy, ExprTy>::IfExpression;
1017   auto &modifier = std::get<std::optional<DirectiveNameModifier>>(clause.t);
1018 
1019   if (modifier) {
1020     llvm::omp::Directive dirId = *modifier;
1021     auto *unmodified =
1022         makeClause(llvm::omp::Clause::OMPC_if,
1023                    tomp::clause::IfT<TypeTy, IdTy, ExprTy>{
1024                        {/*DirectiveNameModifier=*/std::nullopt,
1025                         /*IfExpression=*/std::get<IfExpression>(clause.t)}});
1026 
1027     if (auto *hasDir = findDirective(dirId)) {
1028       hasDir->clauses.push_back(unmodified);
1029       return true;
1030     }
1031     return false;
1032   }
1033 
1034   return applyToAll(node);
1035 }
1036 
1037 // LINEAR
1038 // [5.2:118:1-2]
1039 // Directives: declare simd, do, for, simd
1040 //
1041 // [5.2:341:15-22]
1042 // (15.1) The effect of the linear clause is as if it is applied to the
1043 // innermost leaf construct.
1044 // (15.2) Additionally, if the list item is not the iteration variable of a simd
1045 // or worksharing-loop SIMD construct, the effect on the outer leaf constructs
1046 // is as if the list item was specified in firstprivate and lastprivate clauses
1047 // on the combined or composite construct, with the rules specified above
1048 // applied.
1049 // (19) If a list item of the linear clause is the iteration variable of a simd
1050 // or worksharing-loop SIMD construct and it is not declared in the construct,
1051 // the effect on the outer leaf constructs is as if the list item was specified
1052 // in a lastprivate clause on the combined or composite construct with the rules
1053 // specified above applied.
1054 template <typename C, typename H>
1055 bool ConstructDecompositionT<C, H>::applyClause(
1056     const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
1057     const ClauseTy *node) {
1058   // [5.2:341:15.1]
1059   if (!applyToInnermost(node))
1060     return false;
1061 
1062   // [5.2:341:15.2], [5.2:341:19]
1063   auto dirSimd = findDirective(llvm::omp::Directive::OMPD_simd);
1064   std::optional<ObjectTy> iterVar = helper.getLoopIterVar();
1065   const auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
1066 
1067   // Lists of objects that will be used to construct "firstprivate" and
1068   // "lastprivate" clauses.
1069   tomp::ObjectListT<IdTy, ExprTy> first, last;
1070 
1071   for (const ObjectTy &object : objects) {
1072     last.push_back(object);
1073     if (!dirSimd || !iterVar || object.id() != iterVar->id())
1074       first.push_back(object);
1075   }
1076 
1077   if (!first.empty()) {
1078     auto *firstp = makeClause(
1079         llvm::omp::Clause::OMPC_firstprivate,
1080         tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/first});
1081     nodes.push_back(firstp); // Appending to the main clause list.
1082   }
1083   if (!last.empty()) {
1084     auto *lastp =
1085         makeClause(llvm::omp::Clause::OMPC_lastprivate,
1086                    tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>{
1087                        {/*LastprivateModifier=*/std::nullopt, /*List=*/last}});
1088     nodes.push_back(lastp); // Appending to the main clause list.
1089   }
1090   return true;
1091 }
1092 
1093 // NOWAIT
1094 // [5.2:308:11-13]
1095 // Directives: dispatch, do, for, interop, scope, sections, single, target,
1096 // target enter data, target exit data, target update, taskwait, workshare
1097 //
1098 // [5.2:341:23]
1099 // (23) The effect of the nowait clause is as if it is applied to the outermost
1100 // leaf construct that permits it.
1101 template <typename C, typename H>
1102 bool ConstructDecompositionT<C, H>::applyClause(
1103     const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
1104     const ClauseTy *node) {
1105   return applyToOutermost(node);
1106 }
1107 
1108 template <typename C, typename H>
1109 bool ConstructDecompositionT<C, H>::applyClause(
1110     const tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy> &clause,
1111     const ClauseTy *node) {
1112   return applyToOutermost(node);
1113 }
1114 
1115 template <typename C, typename H>
1116 bool ConstructDecompositionT<C, H>::applyClause(
1117     const tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy> &clause,
1118     const ClauseTy *node) {
1119   return applyToAll(node);
1120 }
1121 
1122 template <typename C, typename H> bool ConstructDecompositionT<C, H>::split() {
1123   bool success = true;
1124 
1125   auto isImplicit = [this](const ClauseTy *node) {
1126     return llvm::any_of(
1127         implicit, [node](const ClauseTy &clause) { return &clause == node; });
1128   };
1129 
1130   for (llvm::omp::Directive leaf :
1131        llvm::omp::getLeafConstructsOrSelf(construct))
1132     leafs.push_back(LeafReprInternal{leaf, /*clauses=*/{}});
1133 
1134   for (const ClauseTy *node : nodes)
1135     addClauseSymsToMap(*node, node);
1136 
1137   // First we need to apply LINEAR, because it can generate additional
1138   // "firstprivate" and "lastprivate" clauses that apply to the combined/
1139   // composite construct.
1140   // Collect them separately, because they may modify the clause list.
1141   llvm::SmallVector<const ClauseTy *> linears;
1142   for (const ClauseTy *node : nodes) {
1143     if (node->id == llvm::omp::Clause::OMPC_linear)
1144       linears.push_back(node);
1145   }
1146   for (const auto *node : linears) {
1147     success = success &&
1148               applyClause(std::get<tomp::clause::LinearT<TypeTy, IdTy, ExprTy>>(
1149                               node->u),
1150                           node);
1151   }
1152 
1153   // "allocate" clauses need to be applied last since they need to see
1154   // which directives have data-privatizing clauses.
1155   auto skip = [](const ClauseTy *node) {
1156     switch (node->id) {
1157     case llvm::omp::Clause::OMPC_allocate:
1158     case llvm::omp::Clause::OMPC_linear:
1159       return true;
1160     default:
1161       return false;
1162     }
1163   };
1164 
1165   // Apply (almost) all clauses.
1166   for (const ClauseTy *node : nodes) {
1167     if (skip(node))
1168       continue;
1169     bool result =
1170         std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1171     if (!isImplicit(node))
1172       success = success && result;
1173   }
1174 
1175   // Apply "allocate".
1176   for (const ClauseTy *node : nodes) {
1177     if (node->id != llvm::omp::Clause::OMPC_allocate)
1178       continue;
1179     success =
1180         success &&
1181         std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1182   }
1183 
1184   return success;
1185 }
1186 
1187 } // namespace tomp
1188 
1189 #endif // LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
1190