Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:43:55

0001 //===- ConstructDecompositionT.h -- Decomposing compound constructs -------===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 // Given a compound construct with a set of clauses, generate the list of
0009 // constituent leaf constructs, each with a list of clauses that apply to it.
0010 //
0011 // Note: Clauses that are not originally present, but that are implied by the
0012 // OpenMP spec are materialized, and are present in the output.
0013 //
0014 // Note: Composite constructs will also be broken up into leaf constructs.
0015 // If composite constructs require processing as a whole, the lists of clauses
0016 // for each leaf constituent should be merged.
0017 //===----------------------------------------------------------------------===//
0018 #ifndef LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
0019 #define LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
0020 
0021 #include "llvm/ADT/ArrayRef.h"
0022 #include "llvm/ADT/STLExtras.h"
0023 #include "llvm/ADT/SmallVector.h"
0024 #include "llvm/ADT/iterator_range.h"
0025 #include "llvm/Frontend/OpenMP/ClauseT.h"
0026 #include "llvm/Frontend/OpenMP/OMP.h"
0027 
0028 #include <iterator>
0029 #include <list>
0030 #include <optional>
0031 #include <tuple>
0032 #include <type_traits>
0033 #include <unordered_map>
0034 #include <unordered_set>
0035 #include <utility>
0036 #include <variant>
0037 
0038 static inline llvm::ArrayRef<llvm::omp::Directive> getWorksharing() {
0039   static llvm::omp::Directive worksharing[] = {
0040       llvm::omp::Directive::OMPD_do,     llvm::omp::Directive::OMPD_for,
0041       llvm::omp::Directive::OMPD_scope,  llvm::omp::Directive::OMPD_sections,
0042       llvm::omp::Directive::OMPD_single, llvm::omp::Directive::OMPD_workshare,
0043   };
0044   return worksharing;
0045 }
0046 
0047 static inline llvm::ArrayRef<llvm::omp::Directive> getWorksharingLoop() {
0048   static llvm::omp::Directive worksharingLoop[] = {
0049       llvm::omp::Directive::OMPD_do,
0050       llvm::omp::Directive::OMPD_for,
0051   };
0052   return worksharingLoop;
0053 }
0054 
0055 namespace detail {
0056 template <typename Container, typename Predicate>
0057 typename std::remove_reference_t<Container>::iterator
0058 find_unique(Container &&container, Predicate &&pred) {
0059   auto first = std::find_if(container.begin(), container.end(), pred);
0060   if (first == container.end())
0061     return first;
0062   auto second = std::find_if(std::next(first), container.end(), pred);
0063   if (second == container.end())
0064     return first;
0065   return container.end();
0066 }
0067 } // namespace detail
0068 
0069 namespace tomp {
0070 
0071 // ClauseType - Either instance of ClauseT, or a type derived from ClauseT.
0072 //
0073 // This is the clause representation in the code using this infrastructure.
0074 //
0075 // HelperType - A class that implements two member functions:
0076 //
0077 //   // Return the base object of the given object, if any.
0078 //   std::optional<Object> getBaseObject(const Object &object) const
0079 //   // Return the iteration variable of the outermost loop associated
0080 //   // with the construct being worked on, if any.
0081 //   std::optional<Object> getLoopIterVar() const
0082 template <typename ClauseType, typename HelperType>
0083 struct ConstructDecompositionT {
0084   using ClauseTy = ClauseType;
0085 
0086   using TypeTy = typename ClauseTy::TypeTy;
0087   using IdTy = typename ClauseTy::IdTy;
0088   using ExprTy = typename ClauseTy::ExprTy;
0089   using HelperTy = HelperType;
0090   using ObjectTy = tomp::ObjectT<IdTy, ExprTy>;
0091 
0092   using ClauseSet = std::unordered_set<const ClauseTy *>;
0093 
0094   ConstructDecompositionT(uint32_t ver, HelperType &helper,
0095                           llvm::omp::Directive dir,
0096                           llvm::ArrayRef<ClauseTy> clauses)
0097       : version(ver), construct(dir), helper(helper) {
0098     for (const ClauseTy &clause : clauses)
0099       nodes.push_back(&clause);
0100 
0101     bool success = split();
0102     if (!success)
0103       return;
0104 
0105     // Copy the individual leaf directives with their clauses to the
0106     // output list. Copy by value, since we don't own the storage
0107     // with the input clauses, and the internal representation uses
0108     // clause addresses.
0109     for (auto &leaf : leafs) {
0110       output.push_back({leaf.id, {}});
0111       auto &out = output.back();
0112       for (const ClauseTy *c : leaf.clauses)
0113         out.clauses.push_back(*c);
0114     }
0115   }
0116 
0117   tomp::ListT<DirectiveWithClauses<ClauseType>> output;
0118 
0119 private:
0120   bool split();
0121 
0122   struct LeafReprInternal {
0123     llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown;
0124     tomp::type::ListT<const ClauseTy *> clauses;
0125   };
0126 
0127   LeafReprInternal *findDirective(llvm::omp::Directive dirId) {
0128     auto found = llvm::find_if(
0129         leafs, [&](const LeafReprInternal &leaf) { return leaf.id == dirId; });
0130     return found != leafs.end() ? &*found : nullptr;
0131   }
0132 
0133   ClauseSet *findClausesWith(const ObjectTy &object) {
0134     if (auto found = syms.find(object.id()); found != syms.end())
0135       return &found->second;
0136     return nullptr;
0137   }
0138 
0139   template <typename S>
0140   ClauseTy *makeClause(llvm::omp::Clause clauseId, S &&specific) {
0141     implicit.push_back(typename ClauseTy::BaseT{clauseId, std::move(specific)});
0142     return &implicit.back();
0143   }
0144 
0145   void addClauseSymsToMap(const ObjectTy &object, const ClauseTy *);
0146   void addClauseSymsToMap(const tomp::ObjectListT<IdTy, ExprTy> &objects,
0147                           const ClauseTy *);
0148   void addClauseSymsToMap(const TypeTy &item, const ClauseTy *);
0149   void addClauseSymsToMap(const ExprTy &item, const ClauseTy *);
0150   void addClauseSymsToMap(const tomp::clause::MapT<TypeTy, IdTy, ExprTy> &item,
0151                           const ClauseTy *);
0152 
0153   template <typename U>
0154   void addClauseSymsToMap(const std::optional<U> &item, const ClauseTy *);
0155   template <typename U>
0156   void addClauseSymsToMap(const tomp::ListT<U> &item, const ClauseTy *);
0157   template <typename... U, size_t... Is>
0158   void addClauseSymsToMap(const std::tuple<U...> &item, const ClauseTy *,
0159                           std::index_sequence<Is...> = {});
0160   template <typename U>
0161   std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, void>
0162   addClauseSymsToMap(U &&item, const ClauseTy *);
0163 
0164   template <typename U>
0165   std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, void>
0166   addClauseSymsToMap(U &&item, const ClauseTy *);
0167 
0168   template <typename U>
0169   std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, void>
0170   addClauseSymsToMap(U &&item, const ClauseTy *);
0171 
0172   template <typename U>
0173   std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, void>
0174   addClauseSymsToMap(U &&item, const ClauseTy *);
0175 
0176   template <typename U>
0177   std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, void>
0178   addClauseSymsToMap(U &&item, const ClauseTy *);
0179 
0180   template <typename U>
0181   std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, void>
0182   addClauseSymsToMap(U &&item, const ClauseTy *);
0183 
0184   // Apply a clause to the only directive that allows it. If there are no
0185   // directives that allow it, or if there is more that one, do not apply
0186   // anything and return false, otherwise return true.
0187   bool applyToUnique(const ClauseTy *node);
0188 
0189   // Apply a clause to the first directive in given range that allows it.
0190   // If such a directive does not exist, return false, otherwise return true.
0191   template <typename Iterator>
0192   bool applyToFirst(const ClauseTy *node, llvm::iterator_range<Iterator> range);
0193 
0194   // Apply a clause to the innermost directive that allows it. If such a
0195   // directive does not exist, return false, otherwise return true.
0196   bool applyToInnermost(const ClauseTy *node);
0197 
0198   // Apply a clause to the outermost directive that allows it. If such a
0199   // directive does not exist, return false, otherwise return true.
0200   bool applyToOutermost(const ClauseTy *node);
0201 
0202   template <typename Predicate>
0203   bool applyIf(const ClauseTy *node, Predicate shouldApply);
0204 
0205   bool applyToAll(const ClauseTy *node);
0206 
0207   template <typename Clause>
0208   bool applyClause(Clause &&clause, const ClauseTy *node);
0209 
0210   bool applyClause(const tomp::clause::CollapseT<TypeTy, IdTy, ExprTy> &clause,
0211                    const ClauseTy *);
0212   bool applyClause(const tomp::clause::PrivateT<TypeTy, IdTy, ExprTy> &clause,
0213                    const ClauseTy *);
0214   bool
0215   applyClause(const tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy> &clause,
0216               const ClauseTy *);
0217   bool
0218   applyClause(const tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy> &clause,
0219               const ClauseTy *);
0220   bool applyClause(const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
0221                    const ClauseTy *);
0222   bool applyClause(const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
0223                    const ClauseTy *);
0224   bool
0225   applyClause(const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
0226               const ClauseTy *);
0227   bool applyClause(const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
0228                    const ClauseTy *);
0229   bool applyClause(const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
0230                    const ClauseTy *);
0231   bool applyClause(const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
0232                    const ClauseTy *);
0233   bool applyClause(const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
0234                    const ClauseTy *);
0235   bool applyClause(const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
0236                    const ClauseTy *);
0237   bool applyClause(const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
0238                    const ClauseTy *);
0239   bool
0240   applyClause(const tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy> &clause,
0241               const ClauseTy *);
0242   bool applyClause(const tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy> &clause,
0243                    const ClauseTy *);
0244 
0245   uint32_t version;
0246   llvm::omp::Directive construct;
0247   HelperType &helper;
0248   ListT<LeafReprInternal> leafs;
0249   tomp::ListT<const ClauseTy *> nodes;
0250   std::list<ClauseTy> implicit; // Container for materialized implicit clauses.
0251                                 // Inserting must preserve element addresses.
0252   std::unordered_map<IdTy, ClauseSet> syms;
0253   std::unordered_set<IdTy> mapBases;
0254 };
0255 
0256 // Deduction guide
0257 template <typename ClauseType, typename HelperType>
0258 ConstructDecompositionT(uint32_t, HelperType &, llvm::omp::Directive,
0259                         llvm::ArrayRef<ClauseType>)
0260     -> ConstructDecompositionT<ClauseType, HelperType>;
0261 
0262 template <typename C, typename H>
0263 void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ObjectTy &object,
0264                                                        const ClauseTy *node) {
0265   syms[object.id()].insert(node);
0266 }
0267 
0268 template <typename C, typename H>
0269 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
0270     const tomp::ObjectListT<IdTy, ExprTy> &objects, const ClauseTy *node) {
0271   for (auto &object : objects)
0272     syms[object.id()].insert(node);
0273 }
0274 
0275 template <typename C, typename H>
0276 void ConstructDecompositionT<C, H>::addClauseSymsToMap(const TypeTy &item,
0277                                                        const ClauseTy *node) {
0278   // Nothing to do for types.
0279 }
0280 
0281 template <typename C, typename H>
0282 void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ExprTy &item,
0283                                                        const ClauseTy *node) {
0284   // Nothing to do for expressions.
0285 }
0286 
0287 template <typename C, typename H>
0288 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
0289     const tomp::clause::MapT<TypeTy, IdTy, ExprTy> &item,
0290     const ClauseTy *node) {
0291   auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(item.t);
0292   addClauseSymsToMap(objects, node);
0293   for (auto &object : objects) {
0294     if (auto base = helper.getBaseObject(object))
0295       mapBases.insert(base->id());
0296   }
0297 }
0298 
0299 template <typename C, typename H>
0300 template <typename U>
0301 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
0302     const std::optional<U> &item, const ClauseTy *node) {
0303   if (item)
0304     addClauseSymsToMap(*item, node);
0305 }
0306 
0307 template <typename C, typename H>
0308 template <typename U>
0309 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
0310     const tomp::ListT<U> &item, const ClauseTy *node) {
0311   for (auto &s : item)
0312     addClauseSymsToMap(s, node);
0313 }
0314 
0315 template <typename C, typename H>
0316 template <typename... U, size_t... Is>
0317 void ConstructDecompositionT<C, H>::addClauseSymsToMap(
0318     const std::tuple<U...> &item, const ClauseTy *node,
0319     std::index_sequence<Is...>) {
0320   (void)node; // Silence strange warning from GCC.
0321   (addClauseSymsToMap(std::get<Is>(item), node), ...);
0322 }
0323 
0324 template <typename C, typename H>
0325 template <typename U>
0326 std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, void>
0327 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
0328                                                   const ClauseTy *node) {
0329   // Nothing to do for enums.
0330 }
0331 
0332 template <typename C, typename H>
0333 template <typename U>
0334 std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, void>
0335 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
0336                                                   const ClauseTy *node) {
0337   // Nothing to do for an empty class.
0338 }
0339 
0340 template <typename C, typename H>
0341 template <typename U>
0342 std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, void>
0343 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
0344                                                   const ClauseTy *node) {
0345   // Nothing to do for an incomplete class (they're empty).
0346 }
0347 
0348 template <typename C, typename H>
0349 template <typename U>
0350 std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, void>
0351 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
0352                                                   const ClauseTy *node) {
0353   addClauseSymsToMap(item.v, node);
0354 }
0355 
0356 template <typename C, typename H>
0357 template <typename U>
0358 std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, void>
0359 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
0360                                                   const ClauseTy *node) {
0361   constexpr size_t tuple_size =
0362       std::tuple_size_v<llvm::remove_cvref_t<decltype(item.t)>>;
0363   addClauseSymsToMap(item.t, node, std::make_index_sequence<tuple_size>{});
0364 }
0365 
0366 template <typename C, typename H>
0367 template <typename U>
0368 std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, void>
0369 ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
0370                                                   const ClauseTy *node) {
0371   std::visit([&](auto &&s) { addClauseSymsToMap(s, node); }, item.u);
0372 }
0373 
0374 // Apply a clause to the only directive that allows it. If there are no
0375 // directives that allow it, or if there is more that one, do not apply
0376 // anything and return false, otherwise return true.
0377 template <typename C, typename H>
0378 bool ConstructDecompositionT<C, H>::applyToUnique(const ClauseTy *node) {
0379   auto unique = detail::find_unique(leafs, [=](const auto &leaf) {
0380     return llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version);
0381   });
0382 
0383   if (unique != leafs.end()) {
0384     unique->clauses.push_back(node);
0385     return true;
0386   }
0387   return false;
0388 }
0389 
0390 // Apply a clause to the first directive in given range that allows it.
0391 // If such a directive does not exist, return false, otherwise return true.
0392 template <typename C, typename H>
0393 template <typename Iterator>
0394 bool ConstructDecompositionT<C, H>::applyToFirst(
0395     const ClauseTy *node, llvm::iterator_range<Iterator> range) {
0396   if (range.empty())
0397     return false;
0398 
0399   for (auto &leaf : range) {
0400     if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
0401       continue;
0402     leaf.clauses.push_back(node);
0403     return true;
0404   }
0405   return false;
0406 }
0407 
0408 // Apply a clause to the innermost directive that allows it. If such a
0409 // directive does not exist, return false, otherwise return true.
0410 template <typename C, typename H>
0411 bool ConstructDecompositionT<C, H>::applyToInnermost(const ClauseTy *node) {
0412   return applyToFirst(node, llvm::reverse(leafs));
0413 }
0414 
0415 // Apply a clause to the outermost directive that allows it. If such a
0416 // directive does not exist, return false, otherwise return true.
0417 template <typename C, typename H>
0418 bool ConstructDecompositionT<C, H>::applyToOutermost(const ClauseTy *node) {
0419   return applyToFirst(node, llvm::iterator_range(leafs));
0420 }
0421 
0422 template <typename C, typename H>
0423 template <typename Predicate>
0424 bool ConstructDecompositionT<C, H>::applyIf(const ClauseTy *node,
0425                                             Predicate shouldApply) {
0426   bool applied = false;
0427   for (auto &leaf : leafs) {
0428     if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
0429       continue;
0430     if (!shouldApply(leaf))
0431       continue;
0432     leaf.clauses.push_back(node);
0433     applied = true;
0434   }
0435 
0436   return applied;
0437 }
0438 
0439 template <typename C, typename H>
0440 bool ConstructDecompositionT<C, H>::applyToAll(const ClauseTy *node) {
0441   return applyIf(node, [](auto) { return true; });
0442 }
0443 
0444 template <typename C, typename H>
0445 template <typename Specific>
0446 bool ConstructDecompositionT<C, H>::applyClause(Specific &&specific,
0447                                                 const ClauseTy *node) {
0448   // The default behavior is to find the unique directive to which the
0449   // given clause may be applied. If there are no such directives, or
0450   // if there are multiple ones, flag an error.
0451   // From "OpenMP Application Programming Interface", Version 5.2:
0452   // S Some clauses are permitted only on a single leaf construct of the
0453   // S combined or composite construct, in which case the effect is as if
0454   // S the clause is applied to that specific construct. (p339, 31-33)
0455   if (applyToUnique(node))
0456     return true;
0457 
0458   return false;
0459 }
0460 
0461 // COLLAPSE
0462 // [5.2:93:20-21]
0463 // Directives: distribute, do, for, loop, simd, taskloop
0464 //
0465 // [5.2:339:35]
0466 // (35) The collapse clause is applied once to the combined or composite
0467 // construct.
0468 template <typename C, typename H>
0469 bool ConstructDecompositionT<C, H>::applyClause(
0470     const tomp::clause::CollapseT<TypeTy, IdTy, ExprTy> &clause,
0471     const ClauseTy *node) {
0472   // Apply "collapse" to the innermost directive. If it's not one that
0473   // allows it flag an error.
0474   if (!leafs.empty()) {
0475     auto &last = leafs.back();
0476 
0477     if (llvm::omp::isAllowedClauseForDirective(last.id, node->id, version)) {
0478       last.clauses.push_back(node);
0479       return true;
0480     }
0481   }
0482 
0483   return false;
0484 }
0485 
0486 // PRIVATE
0487 // [5.2:111:5-7]
0488 // Directives: distribute, do, for, loop, parallel, scope, sections, simd,
0489 // single, target, task, taskloop, teams
0490 //
0491 // [5.2:340:1-2]
0492 // (1) The effect of the 1 private clause is as if it is applied only to the
0493 // innermost leaf construct that permits it.
0494 template <typename C, typename H>
0495 bool ConstructDecompositionT<C, H>::applyClause(
0496     const tomp::clause::PrivateT<TypeTy, IdTy, ExprTy> &clause,
0497     const ClauseTy *node) {
0498   return applyToInnermost(node);
0499 }
0500 
0501 // FIRSTPRIVATE
0502 // [5.2:112:5-7]
0503 // Directives: distribute, do, for, parallel, scope, sections, single, target,
0504 // task, taskloop, teams
0505 //
0506 // [5.2:340:3-20]
0507 // (3) The effect of the firstprivate clause is as if it is applied to one or
0508 // more leaf constructs as follows:
0509 //  (5) To the distribute construct if it is among the constituent constructs;
0510 //  (6) To the teams construct if it is among the constituent constructs and the
0511 //      distribute construct is not;
0512 //  (8) To a worksharing construct that accepts the clause if one is among the
0513 //      constituent constructs;
0514 //  (9) To the taskloop construct if it is among the constituent constructs;
0515 // (10) To the parallel construct if it is among the constituent constructs and
0516 //      neither a taskloop construct nor a worksharing construct that accepts
0517 //      the clause is among them;
0518 // (12) To the target construct if it is among the constituent constructs and
0519 //      the same list item neither appears in a lastprivate clause nor is the
0520 //      base variable or base pointer of a list item that appears in a map
0521 //      clause.
0522 //
0523 // (15) If the parallel construct is among the constituent constructs and the
0524 // effect is not as if the firstprivate clause is applied to it by the above
0525 // rules, then the effect is as if the shared clause with the same list item is
0526 // applied to the parallel construct.
0527 // (17) If the teams construct is among the constituent constructs and the
0528 // effect is not as if the firstprivate clause is applied to it by the above
0529 // rules, then the effect is as if the shared clause with the same list item is
0530 // applied to the teams construct.
0531 template <typename C, typename H>
0532 bool ConstructDecompositionT<C, H>::applyClause(
0533     const tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy> &clause,
0534     const ClauseTy *node) {
0535   bool applied = false;
0536 
0537   // [5.2:340:3-6]
0538   auto dirDistribute = findDirective(llvm::omp::OMPD_distribute);
0539   auto dirTeams = findDirective(llvm::omp::OMPD_teams);
0540   if (dirDistribute != nullptr) {
0541     dirDistribute->clauses.push_back(node);
0542     applied = true;
0543     // [5.2:340:17]
0544     if (dirTeams != nullptr) {
0545       auto *shared = makeClause(
0546           llvm::omp::Clause::OMPC_shared,
0547           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/clause.v});
0548       dirTeams->clauses.push_back(shared);
0549     }
0550   } else if (dirTeams != nullptr) {
0551     dirTeams->clauses.push_back(node);
0552     applied = true;
0553   }
0554 
0555   // [5.2:340:8]
0556   auto findWorksharing = [&]() {
0557     auto worksharing = getWorksharing();
0558     for (auto &leaf : leafs) {
0559       auto found = llvm::find(worksharing, leaf.id);
0560       if (found != std::end(worksharing))
0561         return &leaf;
0562     }
0563     return static_cast<typename decltype(leafs)::value_type *>(nullptr);
0564   };
0565 
0566   auto dirWorksharing = findWorksharing();
0567   if (dirWorksharing != nullptr) {
0568     dirWorksharing->clauses.push_back(node);
0569     applied = true;
0570   }
0571 
0572   // [5.2:340:9]
0573   auto dirTaskloop = findDirective(llvm::omp::OMPD_taskloop);
0574   if (dirTaskloop != nullptr) {
0575     dirTaskloop->clauses.push_back(node);
0576     applied = true;
0577   }
0578 
0579   // [5.2:340:10]
0580   auto dirParallel = findDirective(llvm::omp::OMPD_parallel);
0581   if (dirParallel != nullptr) {
0582     if (dirTaskloop == nullptr && dirWorksharing == nullptr) {
0583       dirParallel->clauses.push_back(node);
0584       applied = true;
0585     } else {
0586       // [5.2:340:15]
0587       auto *shared = makeClause(
0588           llvm::omp::Clause::OMPC_shared,
0589           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/clause.v});
0590       dirParallel->clauses.push_back(shared);
0591     }
0592   }
0593 
0594   // [5.2:340:12]
0595   auto inLastprivate = [&](const ObjectTy &object) {
0596     if (ClauseSet *set = findClausesWith(object)) {
0597       return llvm::find_if(*set, [](const ClauseTy *c) {
0598                return c->id == llvm::omp::Clause::OMPC_lastprivate;
0599              }) != set->end();
0600     }
0601     return false;
0602   };
0603 
0604   auto dirTarget = findDirective(llvm::omp::OMPD_target);
0605   if (dirTarget != nullptr) {
0606     tomp::ObjectListT<IdTy, ExprTy> objects;
0607     llvm::copy_if(
0608         clause.v, std::back_inserter(objects), [&](const ObjectTy &object) {
0609           return !inLastprivate(object) && !mapBases.count(object.id());
0610         });
0611     if (!objects.empty()) {
0612       auto *firstp = makeClause(
0613           llvm::omp::Clause::OMPC_firstprivate,
0614           tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/objects});
0615       dirTarget->clauses.push_back(firstp);
0616       applied = true;
0617     }
0618   }
0619 
0620   // "task" is not handled by any of the cases above.
0621   if (auto dirTask = findDirective(llvm::omp::OMPD_task)) {
0622     dirTask->clauses.push_back(node);
0623     applied = true;
0624   }
0625 
0626   return applied;
0627 }
0628 
0629 // LASTPRIVATE
0630 // [5.2:115:7-8]
0631 // Directives: distribute, do, for, loop, sections, simd, taskloop
0632 //
0633 // [5.2:340:21-30]
0634 // (21) The effect of the lastprivate clause is as if it is applied to all leaf
0635 // constructs that permit the clause.
0636 // (22) If the parallel construct is among the constituent constructs and the
0637 // list item is not also specified in the firstprivate clause, then the effect
0638 // of the lastprivate clause is as if the shared clause with the same list item
0639 // is applied to the parallel construct.
0640 // (24) If the teams construct is among the constituent constructs and the list
0641 // item is not also specified in the firstprivate clause, then the effect of the
0642 // lastprivate clause is as if the shared clause with the same list item is
0643 // applied to the teams construct.
0644 // (27) If the target construct is among the constituent constructs and the list
0645 // item is not the base variable or base pointer of a list item that appears in
0646 // a map clause, the effect of the lastprivate clause is as if the same list
0647 // item appears in a map clause with a map-type of tofrom.
0648 template <typename C, typename H>
0649 bool ConstructDecompositionT<C, H>::applyClause(
0650     const tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy> &clause,
0651     const ClauseTy *node) {
0652   bool applied = false;
0653 
0654   // [5.2:340:21]
0655   applied = applyToAll(node);
0656   if (!applied)
0657     return false;
0658 
0659   auto inFirstprivate = [&](const ObjectTy &object) {
0660     if (ClauseSet *set = findClausesWith(object)) {
0661       return llvm::find_if(*set, [](const ClauseTy *c) {
0662                return c->id == llvm::omp::Clause::OMPC_firstprivate;
0663              }) != set->end();
0664     }
0665     return false;
0666   };
0667 
0668   auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
0669 
0670   // Prepare list of objects that could end up in a "shared" clause.
0671   tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
0672   llvm::copy_if(
0673       objects, std::back_inserter(sharedObjects),
0674       [&](const ObjectTy &object) { return !inFirstprivate(object); });
0675 
0676   if (!sharedObjects.empty()) {
0677     // [5.2:340:22]
0678     if (auto dirParallel = findDirective(llvm::omp::OMPD_parallel)) {
0679       auto *shared = makeClause(
0680           llvm::omp::Clause::OMPC_shared,
0681           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
0682       dirParallel->clauses.push_back(shared);
0683       applied = true;
0684     }
0685 
0686     // [5.2:340:24]
0687     if (auto dirTeams = findDirective(llvm::omp::OMPD_teams)) {
0688       auto *shared = makeClause(
0689           llvm::omp::Clause::OMPC_shared,
0690           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
0691       dirTeams->clauses.push_back(shared);
0692       applied = true;
0693     }
0694   }
0695 
0696   // [5.2:340:27]
0697   if (auto dirTarget = findDirective(llvm::omp::OMPD_target)) {
0698     tomp::ObjectListT<IdTy, ExprTy> tofrom;
0699     llvm::copy_if(
0700         objects, std::back_inserter(tofrom),
0701         [&](const ObjectTy &object) { return !mapBases.count(object.id()); });
0702 
0703     if (!tofrom.empty()) {
0704       using MapType =
0705           typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
0706       auto *map =
0707           makeClause(llvm::omp::Clause::OMPC_map,
0708                      tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
0709                          {/*MapType=*/MapType::Tofrom,
0710                           /*MapTypeModifier=*/std::nullopt,
0711                           /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
0712                           /*LocatorList=*/std::move(tofrom)}});
0713       dirTarget->clauses.push_back(map);
0714       applied = true;
0715     }
0716   }
0717 
0718   return applied;
0719 }
0720 
0721 // SHARED
0722 // [5.2:110:5-6]
0723 // Directives: parallel, task, taskloop, teams
0724 //
0725 // [5.2:340:31-32]
0726 // (31) The effect of the shared, default, thread_limit, or order clause is as
0727 // if it is applied to all leaf constructs that permit the clause.
0728 template <typename C, typename H>
0729 bool ConstructDecompositionT<C, H>::applyClause(
0730     const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
0731     const ClauseTy *node) {
0732   // [5.2:340:31]
0733   return applyToAll(node);
0734 }
0735 
0736 // DEFAULT
0737 // [5.2:109:5-6]
0738 // Directives: parallel, task, taskloop, teams
0739 //
0740 // [5.2:340:31-32]
0741 // (31) The effect of the shared, default, thread_limit, or order clause is as
0742 // if it is applied to all leaf constructs that permit the clause.
0743 template <typename C, typename H>
0744 bool ConstructDecompositionT<C, H>::applyClause(
0745     const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
0746     const ClauseTy *node) {
0747   // [5.2:340:31]
0748   return applyToAll(node);
0749 }
0750 
0751 // THREAD_LIMIT
0752 // [5.2:277:14-15]
0753 // Directives: target, teams
0754 //
0755 // [5.2:340:31-32]
0756 // (31) The effect of the shared, default, thread_limit, or order clause is as
0757 // if it is applied to all leaf constructs that permit the clause.
0758 template <typename C, typename H>
0759 bool ConstructDecompositionT<C, H>::applyClause(
0760     const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
0761     const ClauseTy *node) {
0762   // [5.2:340:31]
0763   return applyToAll(node);
0764 }
0765 
0766 // ORDER
0767 // [5.2:234:3-4]
0768 // Directives: distribute, do, for, loop, simd
0769 //
0770 // [5.2:340:31-32]
0771 // (31) The effect of the shared, default, thread_limit, or order clause is as
0772 // if it is applied to all leaf constructs that permit the clause.
0773 template <typename C, typename H>
0774 bool ConstructDecompositionT<C, H>::applyClause(
0775     const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
0776     const ClauseTy *node) {
0777   // [5.2:340:31]
0778   return applyToAll(node);
0779 }
0780 
0781 // ALLOCATE
0782 // [5.2:178:7-9]
0783 // Directives: allocators, distribute, do, for, parallel, scope, sections,
0784 // single, target, task, taskgroup, taskloop, teams
0785 //
0786 // [5.2:340:33-35]
0787 // (33) The effect of the allocate clause is as if it is applied to all leaf
0788 // constructs that permit the clause and to which a data-sharing attribute
0789 // clause that may create a private copy of the same list item is applied.
0790 template <typename C, typename H>
0791 bool ConstructDecompositionT<C, H>::applyClause(
0792     const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
0793     const ClauseTy *node) {
0794   // This one needs to be applied at the end, once we know which clauses are
0795   // assigned to which leaf constructs.
0796 
0797   // [5.2:340:33]
0798   auto canMakePrivateCopy = [](llvm::omp::Clause id) {
0799     switch (id) {
0800     // Clauses with "privatization" property:
0801     case llvm::omp::Clause::OMPC_firstprivate:
0802     case llvm::omp::Clause::OMPC_in_reduction:
0803     case llvm::omp::Clause::OMPC_lastprivate:
0804     case llvm::omp::Clause::OMPC_linear:
0805     case llvm::omp::Clause::OMPC_private:
0806     case llvm::omp::Clause::OMPC_reduction:
0807     case llvm::omp::Clause::OMPC_task_reduction:
0808       return true;
0809     default:
0810       return false;
0811     }
0812   };
0813 
0814   bool applied = applyIf(node, [&](const auto &leaf) {
0815     return llvm::any_of(leaf.clauses, [&](const ClauseTy *n) {
0816       return canMakePrivateCopy(n->id);
0817     });
0818   });
0819 
0820   return applied;
0821 }
0822 
0823 // REDUCTION
0824 // [5.2:134:17-18]
0825 // Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams
0826 //
0827 // [5.2:340:36-37], [5.2:341:1-13]
0828 // (36) The effect of the reduction clause is as if it is applied to all leaf
0829 // constructs that permit the clause, except for the following constructs:
0830 //  (1) The parallel construct, when combined with the sections,
0831 //      worksharing-loop, loop, or taskloop construct; and
0832 //  (3) The teams construct, when combined with the loop construct.
0833 // (4) For the parallel and teams constructs above, the effect of the reduction
0834 // clause instead is as if each list item or, for any list item that is an array
0835 // item, its corresponding base array or base pointer appears in a shared clause
0836 // for the construct.
0837 // (6) If the task reduction-modifier is specified, the effect is as if it only
0838 // modifies the behavior of the reduction clause on the innermost leaf construct
0839 // that accepts the modifier (see Section 5.5.8).
0840 // (8) If the inscan reduction-modifier is specified, the effect is as if it
0841 // modifies the behavior of the reduction clause on all constructs of the
0842 // combined construct to which the clause is applied and that accept the
0843 // modifier.
0844 // (10) If a list item in a reduction clause on a combined target construct does
0845 // not have the same base variable or base pointer as a list item in a map
0846 // clause on the construct, then the effect is as if the list item in the
0847 // reduction clause appears as a list item in a map clause with a map-type of
0848 // tofrom.
0849 template <typename C, typename H>
0850 bool ConstructDecompositionT<C, H>::applyClause(
0851     const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
0852     const ClauseTy *node) {
0853   using ReductionTy = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>;
0854 
0855   // [5.2:340:36], [5.2:341:1], [5.2:341:3]
0856   bool applyToParallel = true, applyToTeams = true;
0857 
0858   auto dirParallel = findDirective(llvm::omp::Directive::OMPD_parallel);
0859   if (dirParallel) {
0860     auto exclusions = llvm::concat<const llvm::omp::Directive>(
0861         getWorksharingLoop(), tomp::ListT<llvm::omp::Directive>{
0862                                   llvm::omp::Directive::OMPD_loop,
0863                                   llvm::omp::Directive::OMPD_sections,
0864                                   llvm::omp::Directive::OMPD_taskloop,
0865                               });
0866     auto present = [&](llvm::omp::Directive id) {
0867       return findDirective(id) != nullptr;
0868     };
0869 
0870     if (llvm::any_of(exclusions, present))
0871       applyToParallel = false;
0872   }
0873 
0874   auto dirTeams = findDirective(llvm::omp::Directive::OMPD_teams);
0875   if (dirTeams) {
0876     // The only exclusion is OMPD_loop.
0877     if (findDirective(llvm::omp::Directive::OMPD_loop))
0878       applyToTeams = false;
0879   }
0880 
0881   using ReductionModifier = typename ReductionTy::ReductionModifier;
0882   using ReductionIdentifiers = typename ReductionTy::ReductionIdentifiers;
0883 
0884   auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
0885   auto &modifier = std::get<std::optional<ReductionModifier>>(clause.t);
0886 
0887   // Apply the reduction clause first to all directives according to the spec.
0888   // If the reduction was applied at least once, proceed with the data sharing
0889   // side-effects.
0890   bool applied = false;
0891 
0892   // [5.2:341:6], [5.2:341:8]
0893   auto isValidModifier = [](llvm::omp::Directive dir, ReductionModifier mod,
0894                             bool alreadyApplied) {
0895     switch (mod) {
0896     case ReductionModifier::Inscan:
0897       // According to [5.2:135:11-13], "inscan" only applies to
0898       // worksharing-loop, worksharing-loop-simd, or "simd" constructs.
0899       return dir == llvm::omp::Directive::OMPD_simd ||
0900              llvm::is_contained(getWorksharingLoop(), dir);
0901     case ReductionModifier::Task:
0902       if (alreadyApplied)
0903         return false;
0904       // According to [5.2:135:16-18], "task" only applies to "parallel" and
0905       // worksharing constructs.
0906       return dir == llvm::omp::Directive::OMPD_parallel ||
0907              llvm::is_contained(getWorksharing(), dir);
0908     case ReductionModifier::Default:
0909       return true;
0910     }
0911     llvm_unreachable("Unexpected modifier");
0912   };
0913 
0914   auto *unmodified = makeClause(
0915       llvm::omp::Clause::OMPC_reduction,
0916       ReductionTy{
0917           {/*ReductionModifier=*/std::nullopt,
0918            /*ReductionIdentifiers=*/std::get<ReductionIdentifiers>(clause.t),
0919            /*List=*/objects}});
0920 
0921   ReductionModifier effective = modifier.value_or(ReductionModifier::Default);
0922   bool effectiveApplied = false;
0923   // Walk over the leaf constructs starting from the innermost, and apply
0924   // the clause as required by the spec.
0925   for (auto &leaf : llvm::reverse(leafs)) {
0926     if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
0927       continue;
0928     if (!applyToParallel && &leaf == dirParallel)
0929       continue;
0930     if (!applyToTeams && &leaf == dirTeams)
0931       continue;
0932     // Some form of the clause will be applied past this point.
0933     if (isValidModifier(leaf.id, effective, effectiveApplied)) {
0934       // Apply clause with modifier.
0935       leaf.clauses.push_back(node);
0936       effectiveApplied = true;
0937     } else {
0938       // Apply clause without modifier.
0939       leaf.clauses.push_back(unmodified);
0940     }
0941     // The modifier must be applied to some construct.
0942     applied = effectiveApplied;
0943   }
0944 
0945   if (!applied)
0946     return false;
0947 
0948   tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
0949   llvm::transform(objects, std::back_inserter(sharedObjects),
0950                   [&](const ObjectTy &object) {
0951                     auto maybeBase = helper.getBaseObject(object);
0952                     return maybeBase ? *maybeBase : object;
0953                   });
0954 
0955   // [5.2:341:4]
0956   if (!sharedObjects.empty()) {
0957     if (dirParallel && !applyToParallel) {
0958       auto *shared = makeClause(
0959           llvm::omp::Clause::OMPC_shared,
0960           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
0961       dirParallel->clauses.push_back(shared);
0962     }
0963     if (dirTeams && !applyToTeams) {
0964       auto *shared = makeClause(
0965           llvm::omp::Clause::OMPC_shared,
0966           tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
0967       dirTeams->clauses.push_back(shared);
0968     }
0969   }
0970 
0971   // [5.2:341:10]
0972   auto dirTarget = findDirective(llvm::omp::Directive::OMPD_target);
0973   if (dirTarget && leafs.size() > 1) {
0974     tomp::ObjectListT<IdTy, ExprTy> tofrom;
0975     llvm::copy_if(objects, std::back_inserter(tofrom),
0976                   [&](const ObjectTy &object) {
0977                     if (auto maybeBase = helper.getBaseObject(object))
0978                       return !mapBases.count(maybeBase->id());
0979                     return !mapBases.count(object.id()); // XXX is this ok?
0980                   });
0981     if (!tofrom.empty()) {
0982       using MapType =
0983           typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
0984       auto *map = makeClause(
0985           llvm::omp::Clause::OMPC_map,
0986           tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
0987               {/*MapType=*/MapType::Tofrom, /*MapTypeModifier=*/std::nullopt,
0988                /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
0989                /*LocatorList=*/std::move(tofrom)}});
0990 
0991       dirTarget->clauses.push_back(map);
0992       applied = true;
0993     }
0994   }
0995 
0996   return applied;
0997 }
0998 
0999 // 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