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 /*RefModifier=*/std::nullopt,
712 /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
713 /*LocatorList=*/std::move(tofrom)}});
714 dirTarget->clauses.push_back(map);
715 applied = true;
716 }
717 }
718
719 return applied;
720}
721
722// SHARED
723// [5.2:110:5-6]
724// Directives: parallel, task, taskloop, teams
725//
726// [5.2:340:31-32]
727// (31) The effect of the shared, default, thread_limit, or order clause is as
728// if it is applied to all leaf constructs that permit the clause.
729template <typename C, typename H>
730bool ConstructDecompositionT<C, H>::applyClause(
731 const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
732 const ClauseTy *node) {
733 // [5.2:340:31]
734 return applyToAll(node);
735}
736
737// DEFAULT
738// [5.2:109:5-6]
739// Directives: parallel, task, taskloop, teams
740//
741// [5.2:340:31-32]
742// (31) The effect of the shared, default, thread_limit, or order clause is as
743// if it is applied to all leaf constructs that permit the clause.
744template <typename C, typename H>
745bool ConstructDecompositionT<C, H>::applyClause(
746 const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
747 const ClauseTy *node) {
748 // [5.2:340:31]
749 return applyToAll(node);
750}
751
752// THREAD_LIMIT
753// [5.2:277:14-15]
754// Directives: target, teams
755//
756// [5.2:340:31-32]
757// (31) The effect of the shared, default, thread_limit, or order clause is as
758// if it is applied to all leaf constructs that permit the clause.
759template <typename C, typename H>
760bool ConstructDecompositionT<C, H>::applyClause(
761 const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
762 const ClauseTy *node) {
763 // [5.2:340:31]
764 return applyToAll(node);
765}
766
767// ORDER
768// [5.2:234:3-4]
769// Directives: distribute, do, for, loop, simd
770//
771// [5.2:340:31-32]
772// (31) The effect of the shared, default, thread_limit, or order clause is as
773// if it is applied to all leaf constructs that permit the clause.
774template <typename C, typename H>
775bool ConstructDecompositionT<C, H>::applyClause(
776 const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
777 const ClauseTy *node) {
778 // [5.2:340:31]
779 return applyToAll(node);
780}
781
782// ALLOCATE
783// [5.2:178:7-9]
784// Directives: allocators, distribute, do, for, parallel, scope, sections,
785// single, target, task, taskgroup, taskloop, teams
786//
787// [5.2:340:33-35]
788// (33) The effect of the allocate clause is as if it is applied to all leaf
789// constructs that permit the clause and to which a data-sharing attribute
790// clause that may create a private copy of the same list item is applied.
791template <typename C, typename H>
792bool ConstructDecompositionT<C, H>::applyClause(
793 const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
794 const ClauseTy *node) {
795 // This one needs to be applied at the end, once we know which clauses are
796 // assigned to which leaf constructs.
797
798 // [5.2:340:33]
799 bool applied = applyIf(node, [&](const auto &leaf) {
800 return llvm::any_of(leaf.clauses, [&](const ClauseTy *n) {
801 return llvm::omp::isPrivatizingClause(n->id);
802 });
803 });
804
805 return applied;
806}
807
808// REDUCTION
809// [5.2:134:17-18]
810// Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams
811//
812// [5.2:340:36-37], [5.2:341:1-13]
813// (36) The effect of the reduction clause is as if it is applied to all leaf
814// constructs that permit the clause, except for the following constructs:
815// (1) The parallel construct, when combined with the sections,
816// worksharing-loop, loop, or taskloop construct; and
817// (3) The teams construct, when combined with the loop construct.
818// (4) For the parallel and teams constructs above, the effect of the reduction
819// clause instead is as if each list item or, for any list item that is an array
820// item, its corresponding base array or base pointer appears in a shared clause
821// for the construct.
822// (6) If the task reduction-modifier is specified, the effect is as if it only
823// modifies the behavior of the reduction clause on the innermost leaf construct
824// that accepts the modifier (see Section 5.5.8).
825// (8) If the inscan reduction-modifier is specified, the effect is as if it
826// modifies the behavior of the reduction clause on all constructs of the
827// combined construct to which the clause is applied and that accept the
828// modifier.
829// (10) If a list item in a reduction clause on a combined target construct does
830// not have the same base variable or base pointer as a list item in a map
831// clause on the construct, then the effect is as if the list item in the
832// reduction clause appears as a list item in a map clause with a map-type of
833// tofrom.
834template <typename C, typename H>
835bool ConstructDecompositionT<C, H>::applyClause(
836 const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
837 const ClauseTy *node) {
838 using ReductionTy = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>;
839
840 // [5.2:340:36], [5.2:341:1], [5.2:341:3]
841 bool applyToParallel = true, applyToTeams = true;
842
843 auto dirParallel = findDirective(llvm::omp::Directive::OMPD_parallel);
844 if (dirParallel) {
847 llvm::omp::Directive::OMPD_loop,
848 llvm::omp::Directive::OMPD_sections,
849 llvm::omp::Directive::OMPD_taskloop,
850 });
851 auto present = [&](llvm::omp::Directive id) {
852 return findDirective(id) != nullptr;
853 };
854
855 if (llvm::any_of(exclusions, present))
856 applyToParallel = false;
857 }
858
859 auto dirTeams = findDirective(llvm::omp::Directive::OMPD_teams);
860 if (dirTeams) {
861 // The only exclusion is OMPD_loop.
862 if (findDirective(llvm::omp::Directive::OMPD_loop))
863 applyToTeams = false;
864 }
865
866 using ReductionModifier = typename ReductionTy::ReductionModifier;
867 using ReductionIdentifiers = typename ReductionTy::ReductionIdentifiers;
868
869 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
870 auto &modifier = std::get<std::optional<ReductionModifier>>(clause.t);
871
872 // Apply the reduction clause first to all directives according to the spec.
873 // If the reduction was applied at least once, proceed with the data sharing
874 // side-effects.
875 bool applied = false;
876
877 // [5.2:341:6], [5.2:341:8]
878 auto isValidModifier = [](llvm::omp::Directive dir, ReductionModifier mod,
879 bool alreadyApplied) {
880 switch (mod) {
881 case ReductionModifier::Inscan:
882 // According to [5.2:135:11-13], "inscan" only applies to
883 // worksharing-loop, worksharing-loop-simd, or "simd" constructs.
884 return dir == llvm::omp::Directive::OMPD_simd ||
886 case ReductionModifier::Task:
887 if (alreadyApplied)
888 return false;
889 // According to [5.2:135:16-18], "task" only applies to "parallel" and
890 // worksharing constructs.
891 return dir == llvm::omp::Directive::OMPD_parallel ||
893 case ReductionModifier::Default:
894 return true;
895 }
896 llvm_unreachable("Unexpected modifier");
897 };
898
899 auto *unmodified = makeClause(
900 llvm::omp::Clause::OMPC_reduction,
901 ReductionTy{
902 {/*ReductionModifier=*/std::nullopt,
903 /*ReductionIdentifiers=*/std::get<ReductionIdentifiers>(clause.t),
904 /*List=*/objects}});
905
906 ReductionModifier effective = modifier.value_or(ReductionModifier::Default);
907 bool effectiveApplied = false;
908 // Walk over the leaf constructs starting from the innermost, and apply
909 // the clause as required by the spec.
910 for (auto &leaf : llvm::reverse(leafs)) {
911 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
912 continue;
913 if (!applyToParallel && &leaf == dirParallel)
914 continue;
915 if (!applyToTeams && &leaf == dirTeams)
916 continue;
917 // Some form of the clause will be applied past this point.
918 if (isValidModifier(leaf.id, effective, effectiveApplied)) {
919 // Apply clause with modifier.
920 leaf.clauses.push_back(node);
921 effectiveApplied = true;
922 } else {
923 // Apply clause without modifier.
924 leaf.clauses.push_back(unmodified);
925 }
926 // The modifier must be applied to some construct.
927 applied = effectiveApplied;
928 }
929
930 if (!applied)
931 return false;
932
934 llvm::transform(objects, std::back_inserter(sharedObjects),
935 [&](const ObjectTy &object) {
936 auto maybeBase = helper.getBaseObject(object);
937 return maybeBase ? *maybeBase : object;
938 });
939
940 // [5.2:341:4]
941 if (!sharedObjects.empty()) {
942 if (dirParallel && !applyToParallel) {
943 auto *shared = makeClause(
944 llvm::omp::Clause::OMPC_shared,
945 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
946 dirParallel->clauses.push_back(shared);
947 }
948 if (dirTeams && !applyToTeams) {
949 auto *shared = makeClause(
950 llvm::omp::Clause::OMPC_shared,
951 tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
952 dirTeams->clauses.push_back(shared);
953 }
954 }
955
956 // [5.2:341:10]
957 auto dirTarget = findDirective(llvm::omp::Directive::OMPD_target);
958 if (dirTarget && leafs.size() > 1) {
960 llvm::copy_if(objects, std::back_inserter(tofrom),
961 [&](const ObjectTy &object) {
962 if (auto maybeBase = helper.getBaseObject(object))
963 return !mapBases.count(maybeBase->id());
964 return !mapBases.count(object.id()); // XXX is this ok?
965 });
966 if (!tofrom.empty()) {
967 using MapType =
968 typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
969 auto *map = makeClause(
970 llvm::omp::Clause::OMPC_map,
971 tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
972 {/*MapType=*/MapType::Tofrom, /*MapTypeModifier=*/std::nullopt,
973 /*RefModifier=*/std::nullopt, /*Mapper=*/std::nullopt,
974 /*Iterator=*/std::nullopt, /*LocatorList=*/std::move(tofrom)}});
975
976 dirTarget->clauses.push_back(map);
977 applied = true;
978 }
979 }
980
981 return applied;
982}
983
984// IF
985// [5.2:72:7-9]
986// Directives: cancel, parallel, simd, target, target data, target enter data,
987// target exit data, target update, task, taskloop
988//
989// [5.2:72:15-18]
990// (15) For combined or composite constructs, the if clause only applies to the
991// semantics of the construct named in the directive-name-modifier.
992// (16) For a combined or composite construct, if no directive-name-modifier is
993// specified then the if clause applies to all constituent constructs to which
994// an if clause can apply.
995template <typename C, typename H>
996bool ConstructDecompositionT<C, H>::applyClause(
997 const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
998 const ClauseTy *node) {
999 using DirectiveNameModifier =
1001 using IfExpression = typename clause::IfT<TypeTy, IdTy, ExprTy>::IfExpression;
1002 auto &modifier = std::get<std::optional<DirectiveNameModifier>>(clause.t);
1003
1004 if (modifier) {
1005 llvm::omp::Directive dirId = *modifier;
1006 auto *unmodified =
1007 makeClause(llvm::omp::Clause::OMPC_if,
1008 tomp::clause::IfT<TypeTy, IdTy, ExprTy>{
1009 {/*DirectiveNameModifier=*/std::nullopt,
1010 /*IfExpression=*/std::get<IfExpression>(clause.t)}});
1011
1012 if (auto *hasDir = findDirective(dirId)) {
1013 hasDir->clauses.push_back(unmodified);
1014 return true;
1015 }
1016 return false;
1017 }
1018
1019 return applyToAll(node);
1020}
1021
1022// LINEAR
1023// [5.2:118:1-2]
1024// Directives: declare simd, do, for, simd
1025//
1026// [5.2:341:15-22]
1027// (15.1) The effect of the linear clause is as if it is applied to the
1028// innermost leaf construct.
1029// (15.2) Additionally, if the list item is not the iteration variable of a simd
1030// or worksharing-loop SIMD construct, the effect on the outer leaf constructs
1031// is as if the list item was specified in firstprivate and lastprivate clauses
1032// on the combined or composite construct, with the rules specified above
1033// applied.
1034// (19) If a list item of the linear clause is the iteration variable of a simd
1035// or worksharing-loop SIMD construct and it is not declared in the construct,
1036// the effect on the outer leaf constructs is as if the list item was specified
1037// in a lastprivate clause on the combined or composite construct with the rules
1038// specified above applied.
1039template <typename C, typename H>
1040bool ConstructDecompositionT<C, H>::applyClause(
1041 const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
1042 const ClauseTy *node) {
1043 // [5.2:341:15.1]
1044 if (!applyToInnermost(node))
1045 return false;
1046
1047 // [5.2:341:15.2], [5.2:341:19]
1048 auto dirSimd = findDirective(llvm::omp::Directive::OMPD_simd);
1049 std::optional<ObjectTy> iterVar = helper.getLoopIterVar();
1050 const auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
1051
1052 // Lists of objects that will be used to construct "firstprivate" and
1053 // "lastprivate" clauses.
1055
1056 for (const ObjectTy &object : objects) {
1057 last.push_back(object);
1058 if (!dirSimd || !iterVar || object.id() != iterVar->id())
1059 first.push_back(object);
1060 }
1061
1062 if (!first.empty()) {
1063 auto *firstp = makeClause(
1064 llvm::omp::Clause::OMPC_firstprivate,
1065 tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/first});
1066 nodes.push_back(firstp); // Appending to the main clause list.
1067 }
1068 if (!last.empty()) {
1069 auto *lastp =
1070 makeClause(llvm::omp::Clause::OMPC_lastprivate,
1071 tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>{
1072 {/*LastprivateModifier=*/std::nullopt, /*List=*/last}});
1073 nodes.push_back(lastp); // Appending to the main clause list.
1074 }
1075 return true;
1076}
1077
1078// NOWAIT
1079// [5.2:308:11-13]
1080// Directives: dispatch, do, for, interop, scope, sections, single, target,
1081// target enter data, target exit data, target update, taskwait, workshare
1082//
1083// [5.2:341:23]
1084// (23) The effect of the nowait clause is as if it is applied to the outermost
1085// leaf construct that permits it.
1086template <typename C, typename H>
1087bool ConstructDecompositionT<C, H>::applyClause(
1088 const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
1089 const ClauseTy *node) {
1090 return applyToOutermost(node);
1091}
1092
1093template <typename C, typename H>
1094bool ConstructDecompositionT<C, H>::applyClause(
1095 const tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy> &clause,
1096 const ClauseTy *node) {
1097 return applyToOutermost(node);
1098}
1099
1100template <typename C, typename H>
1101bool ConstructDecompositionT<C, H>::applyClause(
1102 const tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy> &clause,
1103 const ClauseTy *node) {
1104 return applyToAll(node);
1105}
1106
1107template <typename C, typename H> bool ConstructDecompositionT<C, H>::split() {
1108 bool success = true;
1109
1110 auto isImplicit = [this](const ClauseTy *node) {
1111 return llvm::is_contained(llvm::make_pointer_range(implicit), node);
1112 };
1113
1114 for (llvm::omp::Directive leaf :
1116 leafs.push_back(LeafReprInternal{leaf, /*clauses=*/{}});
1117
1118 for (const ClauseTy *node : nodes)
1119 addClauseSymsToMap(*node, node);
1120
1121 // First we need to apply LINEAR, because it can generate additional
1122 // "firstprivate" and "lastprivate" clauses that apply to the combined/
1123 // composite construct.
1124 // Collect them separately, because they may modify the clause list.
1126 for (const ClauseTy *node : nodes) {
1127 if (node->id == llvm::omp::Clause::OMPC_linear)
1128 linears.push_back(node);
1129 }
1130 for (const auto *node : linears) {
1131 success = success &&
1132 applyClause(std::get<tomp::clause::LinearT<TypeTy, IdTy, ExprTy>>(
1133 node->u),
1134 node);
1135 }
1136
1137 // "allocate" clauses need to be applied last since they need to see
1138 // which directives have data-privatizing clauses.
1139 auto skip = [](const ClauseTy *node) {
1140 switch (node->id) {
1141 case llvm::omp::Clause::OMPC_allocate:
1142 case llvm::omp::Clause::OMPC_linear:
1143 return true;
1144 default:
1145 return false;
1146 }
1147 };
1148
1149 // Apply (almost) all clauses.
1150 for (const ClauseTy *node : nodes) {
1151 if (skip(node))
1152 continue;
1153 bool result =
1154 std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1155 if (!isImplicit(node))
1156 success = success && result;
1157 }
1158
1159 // Apply "allocate".
1160 for (const ClauseTy *node : nodes) {
1161 if (node->id != llvm::omp::Clause::OMPC_allocate)
1162 continue;
1163 success =
1164 success &&
1165 std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
1166 }
1167
1168 return success;
1169}
1170
1171} // namespace tomp
1172
1173#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:1731
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:2056
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:1757
detail::concat_range< ValueT, RangeTs... > concat(RangeTs &&...Ranges)
Returns a concatenated range across two or more ranges.
Definition STLExtras.h:1152
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:1948
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:1712
auto reverse(ContainerTy &&C)
Definition STLExtras.h:408
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:1738
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:1877
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:701
std::tuple< OPT(MapType), OPT(MapTypeModifiers), OPT(RefModifier), OPT(Mappers), OPT(Iterator), LocatorList > t
Definition ClauseT.h:815