18#ifndef LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
19#define LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
33#include <unordered_map>
34#include <unordered_set>
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,
48 static llvm::omp::Directive worksharingLoop[] = {
49 llvm::omp::Directive::OMPD_do,
50 llvm::omp::Directive::OMPD_for,
52 return worksharingLoop;
56template <
typename Container,
typename Predicate>
57typename std::remove_reference_t<Container>::iterator
59 auto first = std::find_if(container.begin(), container.end(),
pred);
60 if (first == container.end())
62 auto second = std::find_if(std::next(first), container.end(),
pred);
63 if (second == container.end())
65 return container.end();
82template <
typename ClauseType,
typename HelperType>
86 using TypeTy =
typename ClauseTy::TypeTy;
87 using IdTy =
typename ClauseTy::IdTy;
88 using ExprTy =
typename ClauseTy::ExprTy;
92 using ClauseSet = std::unordered_set<const ClauseTy *>;
95 llvm::omp::Directive dir,
97 : version(ver), construct(dir), helper(helper) {
99 nodes.push_back(&clause);
101 bool success = split();
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);
122 struct LeafReprInternal {
123 llvm::omp::Directive
id = llvm::omp::Directive::OMPD_unknown;
127 LeafReprInternal *findDirective(llvm::omp::Directive dirId) {
129 leafs, [&](
const LeafReprInternal &leaf) {
return leaf.id == dirId; });
130 return found != leafs.end() ? &*found :
nullptr;
134 if (
auto found = syms.find(
object.id()); found != syms.end())
135 return &found->second;
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();
153 template <
typename U>
154 void addClauseSymsToMap(
const std::optional<U> &item,
const ClauseTy *);
155 template <
typename U>
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 *);
164 template <
typename U>
165 std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value,
void>
166 addClauseSymsToMap(U &&item,
const ClauseTy *);
168 template <
typename U>
169 std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value,
void>
170 addClauseSymsToMap(U &&item,
const ClauseTy *);
172 template <
typename U>
173 std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value,
void>
174 addClauseSymsToMap(U &&item,
const ClauseTy *);
176 template <
typename U>
177 std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value,
void>
178 addClauseSymsToMap(U &&item,
const ClauseTy *);
180 template <
typename U>
181 std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value,
void>
182 addClauseSymsToMap(U &&item,
const ClauseTy *);
187 bool applyToUnique(
const ClauseTy *node);
191 template <
typename Iterator>
196 bool applyToInnermost(
const ClauseTy *node);
200 bool applyToOutermost(
const ClauseTy *node);
202 template <
typename Predicate>
205 bool applyToAll(
const ClauseTy *node);
207 template <
typename Clause>
208 bool applyClause(Clause &&clause,
const ClauseTy *node);
241 llvm::omp::Directive construct;
243 ListT<LeafReprInternal> leafs;
245 std::list<ClauseTy> implicit;
247 std::unordered_map<IdTy, ClauseSet> syms;
248 std::unordered_set<IdTy> mapBases;
252template <
typename ClauseType,
typename HelperType>
257template <
typename C,
typename H>
259 const ClauseTy *node) {
260 syms[
object.id()].insert(node);
263template <
typename C,
typename H>
264void ConstructDecompositionT<C, H>::addClauseSymsToMap(
266 for (
auto &
object : objects)
267 syms[
object.id()].insert(node);
270template <
typename C,
typename H>
271void ConstructDecompositionT<C, H>::addClauseSymsToMap(
const TypeTy &item,
272 const ClauseTy *node) {
276template <
typename C,
typename H>
277void ConstructDecompositionT<C, H>::addClauseSymsToMap(
const ExprTy &item,
278 const ClauseTy *node) {
282template <
typename C,
typename H>
283void ConstructDecompositionT<C, H>::addClauseSymsToMap(
285 const ClauseTy *node) {
286 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(item.
t);
287 addClauseSymsToMap(objects, node);
288 for (
auto &
object : objects) {
289 if (
auto base = helper.getBaseObject(
object))
290 mapBases.insert(base->id());
294template <
typename C,
typename H>
296void ConstructDecompositionT<C, H>::addClauseSymsToMap(
297 const std::optional<U> &item,
const ClauseTy *node) {
299 addClauseSymsToMap(*item, node);
302template <
typename C,
typename H>
304void ConstructDecompositionT<C, H>::addClauseSymsToMap(
307 addClauseSymsToMap(s, node);
310template <
typename C,
typename H>
311template <
typename...
U,
size_t... Is>
312void ConstructDecompositionT<C, H>::addClauseSymsToMap(
313 const std::tuple<U...> &item,
const ClauseTy *node,
314 std::index_sequence<Is...>) {
316 (addClauseSymsToMap(std::get<Is>(item), node), ...);
319template <
typename C,
typename H>
321std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>,
void>
322ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
323 const ClauseTy *node) {
327template <
typename C,
typename H>
329std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value,
void>
330ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
331 const ClauseTy *node) {
335template <
typename C,
typename H>
337std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value,
void>
338ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
339 const ClauseTy *node) {
343template <
typename C,
typename H>
345std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value,
void>
346ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
347 const ClauseTy *node) {
348 addClauseSymsToMap(item.v, node);
351template <
typename C,
typename H>
353std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value,
void>
354ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
355 const ClauseTy *node) {
356 constexpr size_t tuple_size =
358 addClauseSymsToMap(item.t, node, std::make_index_sequence<tuple_size>{});
361template <
typename C,
typename H>
363std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value,
void>
364ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
365 const ClauseTy *node) {
366 std::visit([&](
auto &&s) { addClauseSymsToMap(s, node); }, item.u);
372template <
typename C,
typename H>
373bool ConstructDecompositionT<C, H>::applyToUnique(
const ClauseTy *node) {
375 return llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version);
378 if (unique != leafs.end()) {
379 unique->clauses.push_back(node);
387template <
typename C,
typename H>
388template <
typename Iterator>
389bool ConstructDecompositionT<C, H>::applyToFirst(
394 for (
auto &leaf : range) {
395 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
397 leaf.clauses.push_back(node);
405template <
typename C,
typename H>
406bool ConstructDecompositionT<C, H>::applyToInnermost(
const ClauseTy *node) {
412template <
typename C,
typename H>
413bool ConstructDecompositionT<C, H>::applyToOutermost(
const ClauseTy *node) {
417template <
typename C,
typename H>
418template <
typename Predicate>
419bool ConstructDecompositionT<C, H>::applyIf(
const ClauseTy *node,
421 bool applied =
false;
422 for (
auto &leaf : leafs) {
423 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
427 leaf.clauses.push_back(node);
434template <
typename C,
typename H>
435bool ConstructDecompositionT<C, H>::applyToAll(
const ClauseTy *node) {
436 return applyIf(node, [](
auto) {
return true; });
439template <
typename C,
typename H>
440template <
typename Specific>
450 if (applyToUnique(node))
463template <
typename C,
typename H>
466 const ClauseTy *node) {
469 if (!leafs.empty()) {
470 auto &last = leafs.back();
472 if (llvm::omp::isAllowedClauseForDirective(last.id, node->id, version)) {
473 last.clauses.push_back(node);
489template <
typename C,
typename H>
490bool ConstructDecompositionT<C, H>::applyClause(
492 const ClauseTy *node) {
493 return applyToInnermost(node);
526template <
typename C,
typename H>
527bool ConstructDecompositionT<C, H>::applyClause(
529 const ClauseTy *node) {
530 bool applied =
false;
533 auto dirDistribute = findDirective(llvm::omp::OMPD_distribute);
534 auto dirTeams = findDirective(llvm::omp::OMPD_teams);
535 if (dirDistribute !=
nullptr) {
536 dirDistribute->clauses.push_back(node);
539 if (dirTeams !=
nullptr) {
540 auto *shared = makeClause(
541 llvm::omp::Clause::OMPC_shared,
543 dirTeams->clauses.push_back(shared);
545 }
else if (dirTeams !=
nullptr) {
546 dirTeams->clauses.push_back(node);
551 auto findWorksharing = [&]() {
553 for (
auto &leaf : leafs) {
554 auto found =
llvm::find(worksharing, leaf.id);
555 if (found != std::end(worksharing))
558 return static_cast<typename decltype(leafs)::value_type *
>(
nullptr);
561 auto dirWorksharing = findWorksharing();
562 if (dirWorksharing !=
nullptr) {
563 dirWorksharing->clauses.push_back(node);
568 auto dirTaskloop = findDirective(llvm::omp::OMPD_taskloop);
569 if (dirTaskloop !=
nullptr) {
570 dirTaskloop->clauses.push_back(node);
575 auto dirParallel = findDirective(llvm::omp::OMPD_parallel);
576 if (dirParallel !=
nullptr) {
577 if (dirTaskloop ==
nullptr && dirWorksharing ==
nullptr) {
578 dirParallel->clauses.push_back(node);
582 auto *shared = makeClause(
583 llvm::omp::Clause::OMPC_shared,
585 dirParallel->clauses.push_back(shared);
590 auto inLastprivate = [&](
const ObjectTy &object) {
591 if (ClauseSet *set = findClausesWith(
object)) {
593 return c->id == llvm::omp::Clause::OMPC_lastprivate;
599 auto dirTarget = findDirective(llvm::omp::OMPD_target);
600 if (dirTarget !=
nullptr) {
603 clause.
v, std::back_inserter(objects), [&](
const ObjectTy &
object) {
604 return !inLastprivate(object) && !mapBases.count(object.id());
606 if (!objects.
empty()) {
607 auto *firstp = makeClause(
608 llvm::omp::Clause::OMPC_firstprivate,
610 dirTarget->clauses.push_back(firstp);
616 if (
auto dirTask = findDirective(llvm::omp::OMPD_task)) {
617 dirTask->clauses.push_back(node);
643template <
typename C,
typename H>
644bool ConstructDecompositionT<C, H>::applyClause(
646 const ClauseTy *node) {
647 bool applied =
false;
650 applied = applyToAll(node);
654 auto inFirstprivate = [&](
const ObjectTy &object) {
655 if (ClauseSet *set = findClausesWith(
object)) {
657 return c->id == llvm::omp::Clause::OMPC_firstprivate;
663 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.
t);
668 objects, std::back_inserter(sharedObjects),
669 [&](
const ObjectTy &
object) {
return !inFirstprivate(
object); });
671 if (!sharedObjects.empty()) {
673 if (
auto dirParallel = findDirective(llvm::omp::OMPD_parallel)) {
674 auto *shared = makeClause(
675 llvm::omp::Clause::OMPC_shared,
677 dirParallel->clauses.push_back(shared);
682 if (
auto dirTeams = findDirective(llvm::omp::OMPD_teams)) {
683 auto *shared = makeClause(
684 llvm::omp::Clause::OMPC_shared,
686 dirTeams->clauses.push_back(shared);
692 if (
auto dirTarget = findDirective(llvm::omp::OMPD_target)) {
695 objects, std::back_inserter(tofrom),
696 [&](
const ObjectTy &
object) {
return !mapBases.count(
object.
id()); });
698 if (!tofrom.
empty()) {
702 makeClause(llvm::omp::Clause::OMPC_map,
706 std::nullopt, std::nullopt,
707 std::move(tofrom)}});
708 dirTarget->clauses.push_back(map);
723template <
typename C,
typename H>
724bool ConstructDecompositionT<C, H>::applyClause(
726 const ClauseTy *node) {
728 return applyToAll(node);
738template <
typename C,
typename H>
739bool ConstructDecompositionT<C, H>::applyClause(
741 const ClauseTy *node) {
743 return applyToAll(node);
753template <
typename C,
typename H>
754bool ConstructDecompositionT<C, H>::applyClause(
756 const ClauseTy *node) {
758 return applyToAll(node);
768template <
typename C,
typename H>
769bool ConstructDecompositionT<C, H>::applyClause(
771 const ClauseTy *node) {
773 return applyToAll(node);
785template <
typename C,
typename H>
786bool ConstructDecompositionT<C, H>::applyClause(
788 const ClauseTy *node) {
793 auto canMakePrivateCopy = [](llvm::omp::Clause id) {
796 case llvm::omp::Clause::OMPC_firstprivate:
797 case llvm::omp::Clause::OMPC_in_reduction:
798 case llvm::omp::Clause::OMPC_lastprivate:
799 case llvm::omp::Clause::OMPC_linear:
800 case llvm::omp::Clause::OMPC_private:
801 case llvm::omp::Clause::OMPC_reduction:
802 case llvm::omp::Clause::OMPC_task_reduction:
809 bool applied = applyIf(node, [&](
const auto &leaf) {
810 return llvm::any_of(leaf.clauses, [&](
const ClauseTy *n) {
811 return canMakePrivateCopy(n->id);
844template <
typename C,
typename H>
845bool ConstructDecompositionT<C, H>::applyClause(
847 const ClauseTy *node) {
851 bool applyToParallel =
true, applyToTeams =
true;
853 auto dirParallel = findDirective(llvm::omp::Directive::OMPD_parallel);
855 auto exclusions = llvm::concat<const llvm::omp::Directive>(
857 llvm::omp::Directive::OMPD_loop,
858 llvm::omp::Directive::OMPD_sections,
859 llvm::omp::Directive::OMPD_taskloop,
861 auto present = [&](llvm::omp::Directive id) {
862 return findDirective(
id) !=
nullptr;
866 applyToParallel =
false;
869 auto dirTeams = findDirective(llvm::omp::Directive::OMPD_teams);
872 if (findDirective(llvm::omp::Directive::OMPD_loop))
873 applyToTeams =
false;
876 using ReductionModifier =
typename ReductionTy::ReductionModifier;
877 using ReductionIdentifiers =
typename ReductionTy::ReductionIdentifiers;
879 auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.
t);
880 auto &modifier = std::get<std::optional<ReductionModifier>>(clause.
t);
885 bool applied =
false;
888 auto isValidModifier = [](llvm::omp::Directive dir, ReductionModifier
mod,
889 bool alreadyApplied) {
891 case ReductionModifier::Inscan:
894 return dir == llvm::omp::Directive::OMPD_simd ||
896 case ReductionModifier::Task:
901 return dir == llvm::omp::Directive::OMPD_parallel ||
903 case ReductionModifier::Default:
909 auto *unmodified = makeClause(
910 llvm::omp::Clause::OMPC_reduction,
913 std::get<ReductionIdentifiers>(clause.
t),
916 ReductionModifier effective =
917 modifier.has_value() ? *modifier : ReductionModifier::Default;
918 bool effectiveApplied =
false;
922 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
924 if (!applyToParallel && &leaf == dirParallel)
926 if (!applyToTeams && &leaf == dirTeams)
929 if (isValidModifier(leaf.id, effective, effectiveApplied)) {
931 leaf.clauses.push_back(node);
932 effectiveApplied =
true;
935 leaf.clauses.push_back(unmodified);
938 applied = effectiveApplied;
946 [&](
const ObjectTy &
object) {
947 auto maybeBase = helper.getBaseObject(
object);
948 return maybeBase ? *maybeBase : object;
952 if (!sharedObjects.
empty()) {
953 if (dirParallel && !applyToParallel) {
954 auto *shared = makeClause(
955 llvm::omp::Clause::OMPC_shared,
957 dirParallel->clauses.push_back(shared);
959 if (dirTeams && !applyToTeams) {
960 auto *shared = makeClause(
961 llvm::omp::Clause::OMPC_shared,
963 dirTeams->clauses.push_back(shared);
968 auto dirTarget = findDirective(llvm::omp::Directive::OMPD_target);
969 if (dirTarget && leafs.size() > 1) {
972 [&](
const ObjectTy &
object) {
973 if (
auto maybeBase = helper.getBaseObject(
object))
974 return !mapBases.count(maybeBase->id());
975 return !mapBases.count(
object.id());
977 if (!tofrom.
empty()) {
980 auto *map = makeClause(
981 llvm::omp::Clause::OMPC_map,
983 {MapType::Tofrom, std::nullopt,
984 std::nullopt, std::nullopt,
985 std::move(tofrom)}});
987 dirTarget->clauses.push_back(map);
1006template <
typename C,
typename H>
1007bool ConstructDecompositionT<C, H>::applyClause(
1009 const ClauseTy *node) {
1010 using DirectiveNameModifier =
1013 auto &modifier = std::get<std::optional<DirectiveNameModifier>>(clause.
t);
1016 llvm::omp::Directive dirId = *modifier;
1018 makeClause(llvm::omp::Clause::OMPC_if,
1021 std::get<IfExpression>(clause.
t)}});
1023 if (
auto *hasDir = findDirective(dirId)) {
1024 hasDir->clauses.push_back(unmodified);
1030 return applyToAll(node);
1050template <
typename C,
typename H>
1051bool ConstructDecompositionT<C, H>::applyClause(
1053 const ClauseTy *node) {
1055 if (!applyToInnermost(node))
1059 auto dirSimd = findDirective(llvm::omp::Directive::OMPD_simd);
1060 std::optional<ObjectTy> iterVar = helper.getLoopIterVar();
1061 const auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.
t);
1067 for (
const ObjectTy &
object : objects) {
1068 last.push_back(
object);
1069 if (!dirSimd || !iterVar ||
object.
id() != iterVar->id())
1070 first.push_back(
object);
1073 if (!first.empty()) {
1074 auto *firstp = makeClause(
1075 llvm::omp::Clause::OMPC_firstprivate,
1077 nodes.push_back(firstp);
1079 if (!
last.empty()) {
1081 makeClause(llvm::omp::Clause::OMPC_lastprivate,
1083 {std::nullopt,
last}});
1084 nodes.push_back(lastp);
1097template <
typename C,
typename H>
1098bool ConstructDecompositionT<C, H>::applyClause(
1100 const ClauseTy *node) {
1101 return applyToOutermost(node);
1104template <
typename C,
typename H>
bool ConstructDecompositionT<C, H>::split() {
1107 for (llvm::omp::Directive leaf :
1109 leafs.push_back(LeafReprInternal{leaf, {}});
1111 for (
const ClauseTy *node :
nodes)
1112 addClauseSymsToMap(*node, node);
1119 for (
const ClauseTy *node :
nodes) {
1120 if (node->id == llvm::omp::Clause::OMPC_linear)
1123 for (
const auto *node : linears) {
1132 auto skip = [](
const ClauseTy *node) {
1134 case llvm::omp::Clause::OMPC_allocate:
1135 case llvm::omp::Clause::OMPC_linear:
1143 for (
const ClauseTy *node :
nodes) {
1148 std::visit([&](
auto &&s) {
return applyClause(s, node); }, node->u);
1152 for (
const ClauseTy *node :
nodes) {
1153 if (node->id != llvm::omp::Clause::OMPC_allocate)
1157 std::visit([&](
auto &&s) {
return applyClause(s, node); }, node->u);
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.
This file defines the SmallVector class.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
void push_back(const T &Elt)
A range adaptor for a pair of iterators.
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)
ArrayRef< Directive > getLeafConstructsOrSelf(Directive D)
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt mod(const DynamicAPInt &LHS, const DynamicAPInt &RHS)
is always non-negative.
auto unique(Range &&R, Predicate P)
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.
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.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
auto reverse(ContainerTy &&C)
typename llvm::remove_cvref< T >::type remove_cvref_t
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
LogicalResult success(bool IsSuccess=true)
Utility function to generate a LogicalResult.
typename ClauseTy::ExprTy ExprTy
std::unordered_set< const ClauseTy * > ClauseSet
typename ClauseTy::TypeTy TypeTy
typename ClauseTy::IdTy IdTy
tomp::ObjectT< IdTy, ExprTy > ObjectTy
ConstructDecompositionT(uint32_t ver, HelperType &helper, llvm::omp::Directive dir, llvm::ArrayRef< ClauseTy > clauses)
tomp::ListT< DirectiveWithClauses< ClauseType > > output
std::tuple< OPT(DirectiveNameModifier), IfExpression > t
type::DirectiveName DirectiveNameModifier
std::tuple< OPT(LastprivateModifier), List > t
std::tuple< OPT(StepSimpleModifier), OPT(StepComplexModifier), OPT(LinearModifier), List > t
std::tuple< OPT(MapType), OPT(MapTypeModifiers), OPT(Mappers), OPT(Iterator), LocatorList > t
std::tuple< OPT(ReductionModifier), ReductionIdentifiers, List > t