LLVM 22.0.0git
ConstructDecompositionT.h
Go to the documentation of this file.
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"
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
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
48 static llvm::omp::Directive worksharingLoop[] = {
49 llvm::omp::Directive::OMPD_do,
50 llvm::omp::Directive::OMPD_for,
51 };
52 return worksharingLoop;
53}
54
55namespace detail {
56template <typename Container, typename Predicate>
57typename std::remove_reference_t<Container>::iterator
58find_unique(Container &&container, Predicate &&pred) {
59 auto first = llvm::find_if(container, pred);
60 if (first == container.end())
61 return first;
62 auto second = std::find_if(std::next(first), container.end(), pred);
63 if (second == container.end())
64 return first;
65 return container.end();
66}
67} // namespace detail
68
69namespace 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
82template <typename ClauseType, typename HelperType>
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;
91
92 using ClauseSet = std::unordered_set<const ClauseTy *>;
93
94 ConstructDecompositionT(uint32_t ver, HelperType &helper,
95 llvm::omp::Directive dir,
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
118
119private:
120 bool split();
121
122 struct LeafReprInternal {
123 llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown;
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;
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
257template <typename ClauseType, typename HelperType>
258ConstructDecompositionT(uint32_t, HelperType &, llvm::omp::Directive,
261
262template <typename C, typename H>
263void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ObjectTy &object,
264 const ClauseTy *node) {
265 syms[object.id()].insert(node);
266}
267
268template <typename C, typename H>
269void 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
275template <typename C, typename H>
276void ConstructDecompositionT<C, H>::addClauseSymsToMap(const TypeTy &item,
277 const ClauseTy *node) {
278 // Nothing to do for types.
279}
280
281template <typename C, typename H>
282void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ExprTy &item,
283 const ClauseTy *node) {
284 // Nothing to do for expressions.
285}
286
287template <typename C, typename H>
288void 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
299template <typename C, typename H>
300template <typename U>
301void ConstructDecompositionT<C, H>::addClauseSymsToMap(
302 const std::optional<U> &item, const ClauseTy *node) {
303 if (item)
304 addClauseSymsToMap(*item, node);
305}
306
307template <typename C, typename H>
308template <typename U>
309void ConstructDecompositionT<C, H>::addClauseSymsToMap(
310 const tomp::ListT<U> &item, const ClauseTy *node) {
311 for (auto &s : item)
312 addClauseSymsToMap(s, node);
313}
314
315template <typename C, typename H>
316template <typename... U, size_t... Is>
317void 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
324template <typename C, typename H>
325template <typename U>
326std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, void>
327ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
328 const ClauseTy *node) {
329 // Nothing to do for enums.
330}
331
332template <typename C, typename H>
333template <typename U>
334std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, void>
335ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
336 const ClauseTy *node) {
337 // Nothing to do for an empty class.
338}
339
340template <typename C, typename H>
341template <typename U>
342std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, void>
343ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
344 const ClauseTy *node) {
345 // Nothing to do for an incomplete class (they're empty).
346}
347
348template <typename C, typename H>
349template <typename U>
350std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, void>
351ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
352 const ClauseTy *node) {
353 addClauseSymsToMap(item.v, node);
354}
355
356template <typename C, typename H>
357template <typename U>
358std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, void>
359ConstructDecompositionT<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
366template <typename C, typename H>
367template <typename U>
368std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, void>
369ConstructDecompositionT<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.
377template <typename C, typename H>
378bool 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.
392template <typename C, typename H>
393template <typename Iterator>
394bool 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.
410template <typename C, typename H>
411bool 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.
417template <typename C, typename H>
418bool ConstructDecompositionT<C, H>::applyToOutermost(const ClauseTy *node) {
419 return applyToFirst(node, llvm::iterator_range(leafs));
420}
421
422template <typename C, typename H>
423template <typename Predicate>
424bool 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
439template <typename C, typename H>
440bool ConstructDecompositionT<C, H>::applyToAll(const ClauseTy *node) {
441 return applyIf(node, [](auto) { return true; });
442}
443
444template <typename C, typename H>
445template <typename Specific>
446bool 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.
468template <typename C, typename H>
469bool ConstructDecompositionT<C, H>::applyClause(
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.
494template <typename C, typename H>
495bool 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.
531template <typename C, typename H>
532bool 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) {
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.
648template <typename C, typename H>
649bool 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.
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)) {
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 /*AttachModifier=*/std::nullopt,
712 /*RefModifier=*/std::nullopt,
713 /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
714 /*LocatorList=*/std::move(tofrom)}});
715 dirTarget->clauses.push_back(map);
716 applied = true;
717 }
718 }
719
720 return applied;
721}
722
723// SHARED
724// [5.2:110:5-6]
725// Directives: parallel, task, taskloop, teams
726//
727// [5.2:340:31-32]
728// (31) The effect of the shared, default, thread_limit, or order clause is as
729// if it is applied to all leaf constructs that permit the clause.
730template <typename C, typename H>
731bool ConstructDecompositionT<C, H>::applyClause(
732 const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
733 const ClauseTy *node) {
734 // [5.2:340:31]
735 return applyToAll(node);
736}
737
738// DEFAULT
739// [5.2:109:5-6]
740// Directives: parallel, task, taskloop, teams
741//
742// [5.2:340:31-32]
743// (31) The effect of the shared, default, thread_limit, or order clause is as
744// if it is applied to all leaf constructs that permit the clause.
745template <typename C, typename H>
746bool ConstructDecompositionT<C, H>::applyClause(
747 const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
748 const ClauseTy *node) {
749 // [5.2:340:31]
750 return applyToAll(node);
751}
752
753// THREAD_LIMIT
754// [5.2:277:14-15]
755// Directives: target, teams
756//
757// [5.2:340:31-32]
758// (31) The effect of the shared, default, thread_limit, or order clause is as
759// if it is applied to all leaf constructs that permit the clause.
760template <typename C, typename H>
761bool ConstructDecompositionT<C, H>::applyClause(
762 const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
763 const ClauseTy *node) {
764 // [5.2:340:31]
765 return applyToAll(node);
766}
767
768// ORDER
769// [5.2:234:3-4]
770// Directives: distribute, do, for, loop, simd
771//
772// [5.2:340:31-32]
773// (31) The effect of the shared, default, thread_limit, or order clause is as
774// if it is applied to all leaf constructs that permit the clause.
775template <typename C, typename H>
776bool ConstructDecompositionT<C, H>::applyClause(
777 const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
778 const ClauseTy *node) {
779 // [5.2:340:31]
780 return applyToAll(node);
781}
782
783// ALLOCATE
784// [5.2:178:7-9]
785// Directives: allocators, distribute, do, for, parallel, scope, sections,
786// single, target, task, taskgroup, taskloop, teams
787//
788// [5.2:340:33-35]
789// (33) The effect of the allocate clause is as if it is applied to all leaf
790// constructs that permit the clause and to which a data-sharing attribute
791// clause that may create a private copy of the same list item is applied.
792template <typename C, typename H>
793bool ConstructDecompositionT<C, H>::applyClause(
794 const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
795 const ClauseTy *node) {
796 // This one needs to be applied at the end, once we know which clauses are
797 // assigned to which leaf constructs.
798
799 // [5.2:340:33]
800 bool applied = applyIf(node, [&](const auto &leaf) {
801 return llvm::any_of(leaf.clauses, [&](const ClauseTy *n) {
802 return llvm::omp::isPrivatizingClause(n->id);
803 });
804 });
805
806 return applied;
807}
808
809// REDUCTION
810// [5.2:134:17-18]
811// Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams
812//
813// [5.2:340:36-37], [5.2:341:1-13]
814// (36) The effect of the reduction clause is as if it is applied to all leaf
815// constructs that permit the clause, except for the following constructs:
816// (1) The parallel construct, when combined with the sections,
817// worksharing-loop, loop, or taskloop construct; and
818// (3) The teams construct, when combined with the loop construct.
819// (4) For the parallel and teams constructs above, the effect of the reduction
820// clause instead is as if each list item or, for any list item that is an array
821// item, its corresponding base array or base pointer appears in a shared clause
822// for the construct.
823// (6) If the task reduction-modifier is specified, the effect is as if it only
824// modifies the behavior of the reduction clause on the innermost leaf construct
825// that accepts the modifier (see Section 5.5.8).
826// (8) If the inscan reduction-modifier is specified, the effect is as if it
827// modifies the behavior of the reduction clause on all constructs of the
828// combined construct to which the clause is applied and that accept the
829// modifier.
830// (10) If a list item in a reduction clause on a combined target construct does
831// not have the same base variable or base pointer as a list item in a map
832// clause on the construct, then the effect is as if the list item in the
833// reduction clause appears as a list item in a map clause with a map-type of
834// tofrom.
835template <typename C, typename H>
836bool ConstructDecompositionT<C, H>::applyClause(
837 const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
838 const ClauseTy *node) {
839 using ReductionTy = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>;
840
841 // [5.2:340:36], [5.2:341:1], [5.2:341:3]
842 bool applyToParallel = true, applyToTeams = true;
843
844 auto dirParallel = findDirective(llvm::omp::Directive::OMPD_parallel);
845 if (dirParallel) {
848 llvm::omp::Directive::OMPD_loop,
849 llvm::omp::Directive::OMPD_sections,
850 llvm::omp::Directive::OMPD_taskloop,
851 });
852 auto present = [&](llvm::omp::Directive id) {
853 return findDirective(id) != nullptr;
854 };
855
856 if (llvm::any_of(exclusions, present))
857 applyToParallel = false;
858 }
859
860 auto dirTeams = findDirective(llvm::omp::Directive::OMPD_teams);
861 if (dirTeams) {
862 // The only exclusion is OMPD_loop.
863 if (findDirective(llvm::omp::Directive::OMPD_loop))
864 applyToTeams = false;
865 }
866
867 using ReductionModifier = typename ReductionTy::ReductionModifier;
868 using ReductionIdentifiers = typename ReductionTy::ReductionIdentifiers;
869
870 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
871 auto &modifier = std::get<std::optional<ReductionModifier>>(clause.t);
872
873 // Apply the reduction clause first to all directives according to the spec.
874 // If the reduction was applied at least once, proceed with the data sharing
875 // side-effects.
876 bool applied = false;
877
878 // [5.2:341:6], [5.2:341:8]
879 auto isValidModifier = [](llvm::omp::Directive dir, ReductionModifier mod,
880 bool alreadyApplied) {
881 switch (mod) {
882 case ReductionModifier::Inscan:
883 // According to [5.2:135:11-13], "inscan" only applies to
884 // worksharing-loop, worksharing-loop-simd, or "simd" constructs.
885 return dir == llvm::omp::Directive::OMPD_simd ||
887 case ReductionModifier::Task:
888 if (alreadyApplied)
889 return false;
890 // According to [5.2:135:16-18], "task" only applies to "parallel" and
891 // worksharing constructs.
892 return dir == llvm::omp::Directive::OMPD_parallel ||
894 case ReductionModifier::Default:
895 return true;
896 }
897 llvm_unreachable("Unexpected modifier");
898 };
899
900 auto *unmodified = makeClause(
901 llvm::omp::Clause::OMPC_reduction,
902 ReductionTy{
903 {/*ReductionModifier=*/std::nullopt,
904 /*ReductionIdentifiers=*/std::get<ReductionIdentifiers>(clause.t),
905 /*List=*/objects}});
906
907 ReductionModifier effective = modifier.value_or(ReductionModifier::Default);
908 bool effectiveApplied = false;
909 // Walk over the leaf constructs starting from the innermost, and apply
910 // the clause as required by the spec.
911 for (auto &leaf : llvm::reverse(leafs)) {
912 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
913 continue;
914 if (!applyToParallel && &leaf == dirParallel)
915 continue;
916 if (!applyToTeams && &leaf == dirTeams)
917 continue;
918 // Some form of the clause will be applied past this point.
919 if (isValidModifier(leaf.id, effective, effectiveApplied)) {
920 // Apply clause with modifier.
921 leaf.clauses.push_back(node);
922 effectiveApplied = true;
923 } else {
924 // Apply clause without modifier.
925 leaf.clauses.push_back(unmodified);
926 }
927 // The modifier must be applied to some construct.
928 applied = effectiveApplied;
929 }
930
931 if (!applied)
932 return false;
933
935 llvm::transform(objects, std::back_inserter(sharedObjects),
936 [&](const ObjectTy &object) {
937 auto maybeBase = helper.getBaseObject(object);
938 return maybeBase ? *maybeBase : object;
939 });
940
941 // [5.2:341:4]
942 if (!sharedObjects.empty()) {
943 if (dirParallel && !applyToParallel) {
944 auto *shared = makeClause(
945 llvm::omp::Clause::OMPC_shared,
946 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
947 dirParallel->clauses.push_back(shared);
948 }
949 if (dirTeams && !applyToTeams) {
950 auto *shared = makeClause(
951 llvm::omp::Clause::OMPC_shared,
952 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
953 dirTeams->clauses.push_back(shared);
954 }
955 }
956
957 // [5.2:341:10]
958 auto dirTarget = findDirective(llvm::omp::Directive::OMPD_target);
959 if (dirTarget && leafs.size() > 1) {
961 llvm::copy_if(objects, std::back_inserter(tofrom),
962 [&](const ObjectTy &object) {
963 if (auto maybeBase = helper.getBaseObject(object))
964 return !mapBases.count(maybeBase->id());
965 return !mapBases.count(object.id()); // XXX is this ok?
966 });
967 if (!tofrom.empty()) {
968 using MapType =
969 typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
970 auto *map = makeClause(
971 llvm::omp::Clause::OMPC_map,
972 tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
973 {/*MapType=*/MapType::Tofrom, /*MapTypeModifier=*/std::nullopt,
974 /*AttachModifier=*/std::nullopt, /*RefModifier=*/std::nullopt,
975 /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
976 /*LocatorList=*/std::move(tofrom)}});
977
978 dirTarget->clauses.push_back(map);
979 applied = true;
980 }
981 }
982
983 return applied;
984}
985
986// IF
987// [5.2:72:7-9]
988// Directives: cancel, parallel, simd, target, target data, target enter data,
989// target exit data, target update, task, taskloop
990//
991// [5.2:72:15-18]
992// (15) For combined or composite constructs, the if clause only applies to the
993// semantics of the construct named in the directive-name-modifier.
994// (16) For a combined or composite construct, if no directive-name-modifier is
995// specified then the if clause applies to all constituent constructs to which
996// an if clause can apply.
997template <typename C, typename H>
998bool ConstructDecompositionT<C, H>::applyClause(
999 const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
1000 const ClauseTy *node) {
1001 using DirectiveNameModifier =
1003 using IfExpression = typename clause::IfT<TypeTy, IdTy, ExprTy>::IfExpression;
1004 auto &modifier = std::get<std::optional<DirectiveNameModifier>>(clause.t);
1005
1006 if (modifier) {
1007 llvm::omp::Directive dirId = *modifier;
1008 auto *unmodified =
1009 makeClause(llvm::omp::Clause::OMPC_if,
1010 tomp::clause::IfT<TypeTy, IdTy, ExprTy>{
1011 {/*DirectiveNameModifier=*/std::nullopt,
1012 /*IfExpression=*/std::get<IfExpression>(clause.t)}});
1013
1014 if (auto *hasDir = findDirective(dirId)) {
1015 hasDir->clauses.push_back(unmodified);
1016 return true;
1017 }
1018 return false;
1019 }
1020
1021 return applyToAll(node);
1022}
1023
1024// LINEAR
1025// [5.2:118:1-2]
1026// Directives: declare simd, do, for, simd
1027//
1028// [5.2:341:15-22]
1029// (15.1) The effect of the linear clause is as if it is applied to the
1030// innermost leaf construct.
1031// (15.2) Additionally, if the list item is not the iteration variable of a simd
1032// or worksharing-loop SIMD construct, the effect on the outer leaf constructs
1033// is as if the list item was specified in firstprivate and lastprivate clauses
1034// on the combined or composite construct, with the rules specified above
1035// applied.
1036// (19) If a list item of the linear clause is the iteration variable of a simd
1037// or worksharing-loop SIMD construct and it is not declared in the construct,
1038// the effect on the outer leaf constructs is as if the list item was specified
1039// in a lastprivate clause on the combined or composite construct with the rules
1040// specified above applied.
1041template <typename C, typename H>
1042bool ConstructDecompositionT<C, H>::applyClause(
1043 const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
1044 const ClauseTy *node) {
1045 // [5.2:341:15.1]
1046 if (!applyToInnermost(node))
1047 return false;
1048
1049 // [5.2:341:15.2], [5.2:341:19]
1050 auto dirSimd = findDirective(llvm::omp::Directive::OMPD_simd);
1051 std::optional<ObjectTy> iterVar = helper.getLoopIterVar();
1052 const auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
1053
1054 // Lists of objects that will be used to construct "firstprivate" and
1055 // "lastprivate" clauses.
1057
1058 for (const ObjectTy &object : objects) {
1059 last.push_back(object);
1060 if (!dirSimd || !iterVar || object.id() != iterVar->id())
1061 first.push_back(object);
1062 }
1063
1064 if (!first.empty()) {
1065 auto *firstp = makeClause(
1066 llvm::omp::Clause::OMPC_firstprivate,
1067 tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/first});
1068 nodes.push_back(firstp); // Appending to the main clause list.
1069 }
1070 if (!last.empty()) {
1071 auto *lastp =
1072 makeClause(llvm::omp::Clause::OMPC_lastprivate,
1073 tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>{
1074 {/*LastprivateModifier=*/std::nullopt, /*List=*/last}});
1075 nodes.push_back(lastp); // Appending to the main clause list.
1076 }
1077 return true;
1078}
1079
1080// NOWAIT
1081// [5.2:308:11-13]
1082// Directives: dispatch, do, for, interop, scope, sections, single, target,
1083// target enter data, target exit data, target update, taskwait, workshare
1084//
1085// [5.2:341:23]
1086// (23) The effect of the nowait clause is as if it is applied to the outermost
1087// leaf construct that permits it.
1088template <typename C, typename H>
1089bool ConstructDecompositionT<C, H>::applyClause(
1090 const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
1091 const ClauseTy *node) {
1092 return applyToOutermost(node);
1093}
1094
1095template <typename C, typename H>
1096bool ConstructDecompositionT<C, H>::applyClause(
1097 const tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy> &clause,
1098 const ClauseTy *node) {
1099 return applyToOutermost(node);
1100}
1101
1102template <typename C, typename H>
1103bool ConstructDecompositionT<C, H>::applyClause(
1104 const tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy> &clause,
1105 const ClauseTy *node) {
1106 return applyToAll(node);
1107}
1108
1109template <typename C, typename H> bool ConstructDecompositionT<C, H>::split() {
1110 bool success = true;
1111
1112 auto isImplicit = [this](const ClauseTy *node) {
1113 return llvm::is_contained(llvm::make_pointer_range(implicit), node);
1114 };
1115
1116 for (llvm::omp::Directive leaf :
1118 leafs.push_back(LeafReprInternal{leaf, /*clauses=*/{}});
1119
1120 for (const ClauseTy *node : nodes)
1121 addClauseSymsToMap(*node, node);
1122
1123 // First we need to apply LINEAR, because it can generate additional
1124 // "firstprivate" and "lastprivate" clauses that apply to the combined/
1125 // composite construct.
1126 // Collect them separately, because they may modify the clause list.
1128 for (const ClauseTy *node : nodes) {
1129 if (node->id == llvm::omp::Clause::OMPC_linear)
1130 linears.push_back(node);
1131 }
1132 for (const auto *node : linears) {
1133 success = success &&
1134 applyClause(std::get<tomp::clause::LinearT<TypeTy, IdTy, ExprTy>>(
1135 node->u),
1136 node);
1137 }
1138
1139 // "allocate" clauses need to be applied last since they need to see
1140 // which directives have data-privatizing clauses.
1141 auto skip = [](const ClauseTy *node) {
1142 switch (node->id) {
1143 case llvm::omp::Clause::OMPC_allocate:
1144 case llvm::omp::Clause::OMPC_linear:
1145 return true;
1146 default:
1147 return false;
1148 }
1149 };
1150
1151 // Apply (almost) all clauses.
1152 for (const ClauseTy *node : nodes) {
1153 if (skip(node))
1154 continue;
1155 bool result =
1156 std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1157 if (!isImplicit(node))
1158 success = success && result;
1159 }
1160
1161 // Apply "allocate".
1162 for (const ClauseTy *node : nodes) {
1163 if (node->id != llvm::omp::Clause::OMPC_allocate)
1164 continue;
1165 success =
1166 success &&
1167 std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1168 }
1169
1170 return success;
1171}
1172
1173} // namespace tomp
1174
1175#endif // LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
Unify divergent function exit nodes
static llvm::ArrayRef< llvm::omp::Directive > getWorksharing()
static llvm::ArrayRef< llvm::omp::Directive > getWorksharingLoop()
static bool shouldApply(Function &F, ProfileSummaryInfo &PSI)
static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges)
Skip an InlineInfo object in the specified data at the specified offset.
SI Form memory clauses
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:41
void push_back(const T &Elt)
This provides a very simple, boring adaptor for a begin and end iterator into a range type.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
std::remove_reference_t< Container >::iterator find_unique(Container &&container, Predicate &&pred)
LLVM_ABI ArrayRef< Directive > getLeafConstructsOrSelf(Directive D)
Definition OMP.cpp:109
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1751
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt mod(const DynamicAPInt &LHS, const DynamicAPInt &RHS)
is always non-negative.
auto unique(Range &&R, Predicate P)
Definition STLExtras.h:2076
OutputIt copy_if(R &&Range, OutputIt Out, UnaryPredicate P)
Provide wrappers to std::copy_if which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1777
detail::concat_range< ValueT, RangeTs... > concat(RangeTs &&...Ranges)
Returns a concatenated range across two or more ranges.
Definition STLExtras.h:1150
OutputIt transform(R &&Range, OutputIt d_first, UnaryFunction F)
Wrapper function around std::transform to apply a function to a range and store the result elsewhere.
Definition STLExtras.h:1968
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1732
auto reverse(ContainerTy &&C)
Definition STLExtras.h:406
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
iterator_range(Container &&) -> iterator_range< llvm::detail::IterOfRange< Container > >
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1758
iterator_range< pointer_iterator< WrappedIteratorT > > make_pointer_range(RangeT &&Range)
Definition iterator.h:363
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1897
LogicalResult success(bool IsSuccess=true)
Utility function to generate a LogicalResult.
llvm::SmallVector< T, 0 > ListT
Definition ClauseT.h:151
type::ObjectListT< I, E > ObjectListT
Definition ClauseT.h:309
ConstructDecompositionT(uint32_t, HelperType &, llvm::omp::Directive, llvm::ArrayRef< ClauseType >) -> ConstructDecompositionT< ClauseType, HelperType >
type::ListT< T > ListT
Definition ClauseT.h:306
type::ObjectT< I, E > ObjectT
Definition ClauseT.h:308
std::unordered_set< const ClauseTy * > ClauseSet
tomp::ObjectT< IdTy, ExprTy > ObjectTy
ConstructDecompositionT(uint32_t ver, HelperType &helper, llvm::omp::Directive dir, llvm::ArrayRef< ClauseTy > clauses)
tomp::ListT< DirectiveWithClauses< ClauseType > > output
type::DirectiveName DirectiveNameModifier
Definition ClauseT.h:711
std::tuple< OPT(MapType), OPT(MapTypeModifiers), OPT(AttachModifier), OPT(RefModifier), OPT(Mappers), OPT(Iterator), LocatorList > t
Definition ClauseT.h:826