• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- ClauseT.h -- clause template definitions ---------------------------===//
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 // This file contains template classes that represent OpenMP clauses, as
9 // described in the OpenMP API specification.
10 //
11 // The general structure of any specific clause class is that it is either
12 // empty, or it consists of a single data member, which can take one of these
13 // three forms:
14 // - a value member, named `v`, or
15 // - a tuple of values, named `t`, or
16 // - a variant (i.e. union) of values, named `u`.
17 // To assist with generic visit algorithms, classes define one of the following
18 // traits:
19 // - EmptyTrait: the class has no data members.
20 // - WrapperTrait: the class has a single member `v`
21 // - TupleTrait: the class has a tuple member `t`
22 // - UnionTrait the class has a varuant member `u`
23 // - IncompleteTrait: the class is a placeholder class that is currently empty,
24 //   but will be completed at a later time.
25 // Note: This structure follows the one used in flang parser.
26 //
27 // The types used in the class definitions follow the names used in the spec
28 // (there are a few exceptions to this). For example, given
29 //   Clause `foo`
30 //   - foo-modifier : description...
31 //   - list         : list of variables
32 // the corresponding class would be
33 //   template <...>
34 //   struct FooT {
35 //     using FooModifier = type that can represent the modifier
36 //     using List = ListT<ObjectT<...>>;
37 //     using TupleTrait = std::true_type;
38 //     std::tuple<std::optional<FooModifier>, List> t;
39 //   };
40 //===----------------------------------------------------------------------===//
41 #ifndef LLVM_FRONTEND_OPENMP_CLAUSET_H
42 #define LLVM_FRONTEND_OPENMP_CLAUSET_H
43 
44 #include "llvm/ADT/ArrayRef.h"
45 #include "llvm/ADT/DenseMap.h"
46 #include "llvm/ADT/DenseSet.h"
47 #include "llvm/ADT/STLExtras.h"
48 #include "llvm/ADT/SmallVector.h"
49 #include "llvm/Frontend/OpenMP/OMP.h"
50 #include "llvm/Support/ErrorHandling.h"
51 #include "llvm/Support/raw_ostream.h"
52 
53 #include <algorithm>
54 #include <iterator>
55 #include <optional>
56 #include <tuple>
57 #include <type_traits>
58 #include <utility>
59 #include <variant>
60 
61 #define ENUM(Name, ...) enum class Name { __VA_ARGS__ }
62 #define OPT(x) std::optional<x>
63 
64 // A number of OpenMP clauses contain values that come from a given set of
65 // possibilities. In the IR these are usually represented by enums. Both
66 // clang and flang use different types for the enums, and the enum elements
67 // representing the same thing may have different values between clang and
68 // flang.
69 // Since the representation below tries to adhere to the spec, and be source
70 // language agnostic, it defines its own enums, independent from any language
71 // frontend. As a consequence, when instantiating the templates below,
72 // frontend-specific enums need to be translated into the representation
73 // used here. The macros below are intended to assist with the conversion.
74 
75 // Helper macro for enum-class conversion.
76 #define CLAUSET_SCOPED_ENUM_MEMBER_CONVERT(Ov, Tv)                             \
77   if (v == OtherEnum::Ov) {                                                    \
78     return ThisEnum::Tv;                                                       \
79   }
80 
81 // Helper macro for enum (non-class) conversion.
82 #define CLAUSET_UNSCOPED_ENUM_MEMBER_CONVERT(Ov, Tv)                           \
83   if (v == Ov) {                                                               \
84     return ThisEnum::Tv;                                                       \
85   }
86 
87 #define CLAUSET_ENUM_CONVERT(func, OtherE, ThisE, Maps)                        \
88   auto func = [](OtherE v) -> ThisE {                                          \
89     using ThisEnum = ThisE;                                                    \
90     using OtherEnum = OtherE;                                                  \
91     (void)sizeof(OtherEnum); /*Avoid "unused local typedef" warning*/          \
92     Maps;                                                                      \
93     llvm_unreachable("Unexpected value in " #OtherE);                          \
94   }
95 
96 // Usage:
97 //
98 // Given two enums,
99 //   enum class Other { o1, o2 };
100 //   enum class This { t1, t2 };
101 // generate conversion function "Func : Other -> This" with
102 //   CLAUSET_ENUM_CONVERT(
103 //       Func, Other, This,
104 //       CLAUSET_ENUM_MEMBER_CONVERT(o1, t1)      // <- No comma
105 //       CLAUSET_ENUM_MEMBER_CONVERT(o2, t2)
106 //       ...
107 //   )
108 //
109 // Note that the sequence of M(other-value, this-value) is separated
110 // with _spaces_, not commas.
111 
112 namespace detail {
113 // Type trait to determine whether T is a specialization of std::variant.
114 template <typename T> struct is_variant {
115   static constexpr bool value = false;
116 };
117 
118 template <typename... Ts> struct is_variant<std::variant<Ts...>> {
119   static constexpr bool value = true;
120 };
121 
122 template <typename T> constexpr bool is_variant_v = is_variant<T>::value;
123 
124 // Helper utility to create a type which is a union of two given variants.
125 template <typename...> struct UnionOfTwo;
126 
127 template <typename... Types1, typename... Types2>
128 struct UnionOfTwo<std::variant<Types1...>, std::variant<Types2...>> {
129   using type = std::variant<Types1..., Types2...>;
130 };
131 } // namespace detail
132 
133 namespace tomp {
134 namespace type {
135 
136 // Helper utility to create a type which is a union of an arbitrary number
137 // of variants.
138 template <typename...> struct Union;
139 
140 template <> struct Union<> {
141   // Legal to define, illegal to instantiate.
142   using type = std::variant<>;
143 };
144 
145 template <typename T, typename... Ts> struct Union<T, Ts...> {
146   static_assert(detail::is_variant_v<T>);
147   using type =
148       typename detail::UnionOfTwo<T, typename Union<Ts...>::type>::type;
149 };
150 
151 template <typename T> using ListT = llvm::SmallVector<T, 0>;
152 
153 // The ObjectT class represents a variable (as defined in the OpenMP spec).
154 //
155 // A specialization of ObjectT<Id, Expr> must provide the following definitions:
156 // {
157 //    using IdTy = Id;
158 //    using ExprTy = Expr;
159 //
160 //    auto id() const -> IdTy {
161 //      return the identifier of the object (for use in tests for
162 //         presence/absence of the object)
163 //    }
164 //
165 //    auto ref() const -> (e.g. const ExprTy&) {
166 //      return the expression accessing (referencing) the object
167 //    }
168 // }
169 //
170 // For example, the ObjectT instance created for "var[x+1]" would have
171 // the `id()` return the identifier for `var`, and the `ref()` return the
172 // representation of the array-access `var[x+1]`.
173 //
174 // The identity of an object must always be present, i.e. it cannot be
175 // nullptr, std::nullopt, etc. The reference is optional.
176 //
177 // Note: the ObjectT template is not defined. Any user of it is expected to
178 // provide their own specialization that conforms to the above requirements.
179 template <typename IdType, typename ExprType> struct ObjectT;
180 
181 template <typename I, typename E> using ObjectListT = ListT<ObjectT<I, E>>;
182 
183 using DirectiveName = llvm::omp::Directive;
184 
185 template <typename I, typename E> //
186 struct DefinedOperatorT {
187   struct DefinedOpName {
188     using WrapperTrait = std::true_type;
189     ObjectT<I, E> v;
190   };
191   ENUM(IntrinsicOperator, Power, Multiply, Divide, Add, Subtract, Concat, LT,
192        LE, EQ, NE, GE, GT, NOT, AND, OR, EQV, NEQV, Min, Max);
193   using UnionTrait = std::true_type;
194   std::variant<DefinedOpName, IntrinsicOperator> u;
195 };
196 
197 // V5.2: [3.2.6] `iterator` modifier
198 template <typename E> //
199 struct RangeT {
200   // range-specification: begin : end[: step]
201   using TupleTrait = std::true_type;
202   std::tuple<E, E, OPT(E)> t;
203 };
204 
205 // V5.2: [3.2.6] `iterator` modifier
206 template <typename TypeType, typename IdType, typename ExprType> //
207 struct IteratorSpecifierT {
208   // iterators-specifier: [ iterator-type ] identifier = range-specification
209   using TupleTrait = std::true_type;
210   std::tuple<OPT(TypeType), ObjectT<IdType, ExprType>, RangeT<ExprType>> t;
211 };
212 
213 // Note:
214 // For motion or map clauses the OpenMP spec allows a unique mapper modifier.
215 // In practice, since these clauses apply to multiple objects, there can be
216 // multiple effective mappers applicable to these objects (due to overloads,
217 // etc.). Because of that store a list of mappers every time a mapper modifier
218 // is allowed. If the mapper list contains a single element, it applies to
219 // all objects in the clause, otherwise there should be as many mappers as
220 // there are objects.
221 // V5.2: [5.8.2] Mapper identifiers and `mapper` modifiers
222 template <typename I, typename E> //
223 struct MapperT {
224   using MapperIdentifier = ObjectT<I, E>;
225   using WrapperTrait = std::true_type;
226   MapperIdentifier v;
227 };
228 
229 // V5.2: [15.8.1] `memory-order` clauses
230 // When used as arguments for other clauses, e.g. `fail`.
231 ENUM(MemoryOrder, AcqRel, Acquire, Relaxed, Release, SeqCst);
232 ENUM(MotionExpectation, Present);
233 // V5.2: [15.9.1] `task-dependence-type` modifier
234 ENUM(TaskDependenceType, In, Out, Inout, Mutexinoutset, Inoutset, Depobj);
235 
236 template <typename I, typename E> //
237 struct LoopIterationT {
238   struct Distance {
239     using TupleTrait = std::true_type;
240     std::tuple<DefinedOperatorT<I, E>, E> t;
241   };
242   using TupleTrait = std::true_type;
243   std::tuple<ObjectT<I, E>, OPT(Distance)> t;
244 };
245 
246 template <typename I, typename E> //
247 struct ProcedureDesignatorT {
248   using WrapperTrait = std::true_type;
249   ObjectT<I, E> v;
250 };
251 
252 // Note:
253 // For reduction clauses the OpenMP spec allows a unique reduction identifier.
254 // For reasons analogous to those listed for the MapperT type, clauses that
255 // according to the spec contain a reduction identifier will contain a list of
256 // reduction identifiers. The same constraints apply: there is either a single
257 // identifier that applies to all objects, or there are as many identifiers
258 // as there are objects.
259 template <typename I, typename E> //
260 struct ReductionIdentifierT {
261   using UnionTrait = std::true_type;
262   std::variant<DefinedOperatorT<I, E>, ProcedureDesignatorT<I, E>> u;
263 };
264 
265 template <typename T, typename I, typename E> //
266 using IteratorT = ListT<IteratorSpecifierT<T, I, E>>;
267 } // namespace type
268 
269 template <typename T> using ListT = type::ListT<T>;
270 
271 template <typename I, typename E> using ObjectT = type::ObjectT<I, E>;
272 template <typename I, typename E> using ObjectListT = type::ObjectListT<I, E>;
273 
274 template <typename T, typename I, typename E>
275 using IteratorT = type::IteratorT<T, I, E>;
276 
277 template <
278     typename ContainerTy, typename FunctionTy,
279     typename ElemTy = typename llvm::remove_cvref_t<ContainerTy>::value_type,
280     typename ResultTy = std::invoke_result_t<FunctionTy, ElemTy>>
281 ListT<ResultTy> makeList(ContainerTy &&container, FunctionTy &&func) {
282   ListT<ResultTy> v;
283   llvm::transform(container, std::back_inserter(v), func);
284   return v;
285 }
286 
287 namespace clause {
288 // V5.2: [8.3.1] `assumption` clauses
289 template <typename T, typename I, typename E> //
290 struct AbsentT {
291   using List = ListT<type::DirectiveName>;
292   using WrapperTrait = std::true_type;
293   List v;
294 };
295 
296 // V5.2: [15.8.1] `memory-order` clauses
297 template <typename T, typename I, typename E> //
298 struct AcqRelT {
299   using EmptyTrait = std::true_type;
300 };
301 
302 // V5.2: [15.8.1] `memory-order` clauses
303 template <typename T, typename I, typename E> //
304 struct AcquireT {
305   using EmptyTrait = std::true_type;
306 };
307 
308 // V5.2: [7.5.2] `adjust_args` clause
309 template <typename T, typename I, typename E> //
310 struct AdjustArgsT {
311   using IncompleteTrait = std::true_type;
312 };
313 
314 // V5.2: [12.5.1] `affinity` clause
315 template <typename T, typename I, typename E> //
316 struct AffinityT {
317   using Iterator = type::IteratorT<T, I, E>;
318   using LocatorList = ObjectListT<I, E>;
319 
320   using TupleTrait = std::true_type;
321   std::tuple<OPT(Iterator), LocatorList> t;
322 };
323 
324 // V5.2: [6.3] `align` clause
325 template <typename T, typename I, typename E> //
326 struct AlignT {
327   using Alignment = E;
328 
329   using WrapperTrait = std::true_type;
330   Alignment v;
331 };
332 
333 // V5.2: [5.11] `aligned` clause
334 template <typename T, typename I, typename E> //
335 struct AlignedT {
336   using Alignment = E;
337   using List = ObjectListT<I, E>;
338 
339   using TupleTrait = std::true_type;
340   std::tuple<OPT(Alignment), List> t;
341 };
342 
343 template <typename T, typename I, typename E> //
344 struct AllocatorT;
345 
346 // V5.2: [6.6] `allocate` clause
347 template <typename T, typename I, typename E> //
348 struct AllocateT {
349   using AllocatorSimpleModifier = E;
350   using AllocatorComplexModifier = AllocatorT<T, I, E>;
351   using AlignModifier = AlignT<T, I, E>;
352   using List = ObjectListT<I, E>;
353 
354   using TupleTrait = std::true_type;
355   std::tuple<OPT(AllocatorSimpleModifier), OPT(AllocatorComplexModifier),
356              OPT(AlignModifier), List>
357       t;
358 };
359 
360 // V5.2: [6.4] `allocator` clause
361 template <typename T, typename I, typename E> //
362 struct AllocatorT {
363   using Allocator = E;
364   using WrapperTrait = std::true_type;
365   Allocator v;
366 };
367 
368 // V5.2: [7.5.3] `append_args` clause
369 template <typename T, typename I, typename E> //
370 struct AppendArgsT {
371   using IncompleteTrait = std::true_type;
372 };
373 
374 // V5.2: [8.1] `at` clause
375 template <typename T, typename I, typename E> //
376 struct AtT {
377   ENUM(ActionTime, Compilation, Execution);
378   using WrapperTrait = std::true_type;
379   ActionTime v;
380 };
381 
382 // V5.2: [8.2.1] `requirement` clauses
383 template <typename T, typename I, typename E> //
384 struct AtomicDefaultMemOrderT {
385   using MemoryOrder = type::MemoryOrder;
386   using WrapperTrait = std::true_type;
387   MemoryOrder v; // Name not provided in spec
388 };
389 
390 // V5.2: [11.7.1] `bind` clause
391 template <typename T, typename I, typename E> //
392 struct BindT {
393   ENUM(Binding, Teams, Parallel, Thread);
394   using WrapperTrait = std::true_type;
395   Binding v;
396 };
397 
398 // V5.2: [15.8.3] `extended-atomic` clauses
399 template <typename T, typename I, typename E> //
400 struct CaptureT {
401   using EmptyTrait = std::true_type;
402 };
403 
404 // V5.2: [4.4.3] `collapse` clause
405 template <typename T, typename I, typename E> //
406 struct CollapseT {
407   using N = E;
408   using WrapperTrait = std::true_type;
409   N v;
410 };
411 
412 // V5.2: [15.8.3] `extended-atomic` clauses
413 template <typename T, typename I, typename E> //
414 struct CompareT {
415   using EmptyTrait = std::true_type;
416 };
417 
418 // V5.2: [8.3.1] `assumption` clauses
419 template <typename T, typename I, typename E> //
420 struct ContainsT {
421   using List = ListT<type::DirectiveName>;
422   using WrapperTrait = std::true_type;
423   List v;
424 };
425 
426 // V5.2: [5.7.1] `copyin` clause
427 template <typename T, typename I, typename E> //
428 struct CopyinT {
429   using List = ObjectListT<I, E>;
430   using WrapperTrait = std::true_type;
431   List v;
432 };
433 
434 // V5.2: [5.7.2] `copyprivate` clause
435 template <typename T, typename I, typename E> //
436 struct CopyprivateT {
437   using List = ObjectListT<I, E>;
438   using WrapperTrait = std::true_type;
439   List v;
440 };
441 
442 // V5.2: [5.4.1] `default` clause
443 template <typename T, typename I, typename E> //
444 struct DefaultT {
445   ENUM(DataSharingAttribute, Firstprivate, None, Private, Shared);
446   using WrapperTrait = std::true_type;
447   DataSharingAttribute v;
448 };
449 
450 // V5.2: [5.8.7] `defaultmap` clause
451 template <typename T, typename I, typename E> //
452 struct DefaultmapT {
453   ENUM(ImplicitBehavior, Alloc, To, From, Tofrom, Firstprivate, None, Default,
454        Present);
455   ENUM(VariableCategory, Scalar, Aggregate, Pointer, Allocatable);
456   using TupleTrait = std::true_type;
457   std::tuple<ImplicitBehavior, OPT(VariableCategory)> t;
458 };
459 
460 template <typename T, typename I, typename E> //
461 struct DoacrossT;
462 
463 // V5.2: [15.9.5] `depend` clause
464 template <typename T, typename I, typename E> //
465 struct DependT {
466   using Iterator = type::IteratorT<T, I, E>;
467   using LocatorList = ObjectListT<I, E>;
468   using TaskDependenceType = tomp::type::TaskDependenceType;
469 
470   struct WithLocators { // Modern form
471     using TupleTrait = std::true_type;
472     // Empty LocatorList means "omp_all_memory".
473     std::tuple<TaskDependenceType, OPT(Iterator), LocatorList> t;
474   };
475 
476   using Doacross = DoacrossT<T, I, E>;
477   using UnionTrait = std::true_type;
478   std::variant<Doacross, WithLocators> u; // Doacross form is legacy
479 };
480 
481 // V5.2: [3.5] `destroy` clause
482 template <typename T, typename I, typename E> //
483 struct DestroyT {
484   using DestroyVar = ObjectT<I, E>;
485   using WrapperTrait = std::true_type;
486   // DestroyVar can be ommitted in "depobj destroy".
487   OPT(DestroyVar) v;
488 };
489 
490 // V5.2: [12.5.2] `detach` clause
491 template <typename T, typename I, typename E> //
492 struct DetachT {
493   using EventHandle = ObjectT<I, E>;
494   using WrapperTrait = std::true_type;
495   EventHandle v;
496 };
497 
498 // V5.2: [13.2] `device` clause
499 template <typename T, typename I, typename E> //
500 struct DeviceT {
501   using DeviceDescription = E;
502   ENUM(DeviceModifier, Ancestor, DeviceNum);
503   using TupleTrait = std::true_type;
504   std::tuple<OPT(DeviceModifier), DeviceDescription> t;
505 };
506 
507 // V5.2: [13.1] `device_type` clause
508 template <typename T, typename I, typename E> //
509 struct DeviceTypeT {
510   ENUM(DeviceTypeDescription, Any, Host, Nohost);
511   using WrapperTrait = std::true_type;
512   DeviceTypeDescription v;
513 };
514 
515 // V5.2: [11.6.1] `dist_schedule` clause
516 template <typename T, typename I, typename E> //
517 struct DistScheduleT {
518   ENUM(Kind, Static);
519   using ChunkSize = E;
520   using TupleTrait = std::true_type;
521   std::tuple<Kind, OPT(ChunkSize)> t;
522 };
523 
524 // V5.2: [15.9.6] `doacross` clause
525 template <typename T, typename I, typename E> //
526 struct DoacrossT {
527   using Vector = ListT<type::LoopIterationT<I, E>>;
528   ENUM(DependenceType, Source, Sink);
529   using TupleTrait = std::true_type;
530   // Empty Vector means "omp_cur_iteration"
531   std::tuple<DependenceType, Vector> t;
532 };
533 
534 // V5.2: [8.2.1] `requirement` clauses
535 template <typename T, typename I, typename E> //
536 struct DynamicAllocatorsT {
537   using EmptyTrait = std::true_type;
538 };
539 
540 // V5.2: [5.8.4] `enter` clause
541 template <typename T, typename I, typename E> //
542 struct EnterT {
543   using List = ObjectListT<I, E>;
544   using WrapperTrait = std::true_type;
545   List v;
546 };
547 
548 // V5.2: [5.6.2] `exclusive` clause
549 template <typename T, typename I, typename E> //
550 struct ExclusiveT {
551   using WrapperTrait = std::true_type;
552   using List = ObjectListT<I, E>;
553   List v;
554 };
555 
556 // V5.2: [15.8.3] `extended-atomic` clauses
557 template <typename T, typename I, typename E> //
558 struct FailT {
559   using MemoryOrder = type::MemoryOrder;
560   using WrapperTrait = std::true_type;
561   MemoryOrder v;
562 };
563 
564 // V5.2: [10.5.1] `filter` clause
565 template <typename T, typename I, typename E> //
566 struct FilterT {
567   using ThreadNum = E;
568   using WrapperTrait = std::true_type;
569   ThreadNum v;
570 };
571 
572 // V5.2: [12.3] `final` clause
573 template <typename T, typename I, typename E> //
574 struct FinalT {
575   using Finalize = E;
576   using WrapperTrait = std::true_type;
577   Finalize v;
578 };
579 
580 // V5.2: [5.4.4] `firstprivate` clause
581 template <typename T, typename I, typename E> //
582 struct FirstprivateT {
583   using List = ObjectListT<I, E>;
584   using WrapperTrait = std::true_type;
585   List v;
586 };
587 
588 // V5.2: [5.9.2] `from` clause
589 template <typename T, typename I, typename E> //
590 struct FromT {
591   using LocatorList = ObjectListT<I, E>;
592   using Expectation = type::MotionExpectation;
593   using Iterator = type::IteratorT<T, I, E>;
594   // See note at the definition of the MapperT type.
595   using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name
596 
597   using TupleTrait = std::true_type;
598   std::tuple<OPT(Expectation), OPT(Mappers), OPT(Iterator), LocatorList> t;
599 };
600 
601 // V5.2: [9.2.1] `full` clause
602 template <typename T, typename I, typename E> //
603 struct FullT {
604   using EmptyTrait = std::true_type;
605 };
606 
607 // V5.2: [12.6.1] `grainsize` clause
608 template <typename T, typename I, typename E> //
609 struct GrainsizeT {
610   ENUM(Prescriptiveness, Strict);
611   using GrainSize = E;
612   using TupleTrait = std::true_type;
613   std::tuple<OPT(Prescriptiveness), GrainSize> t;
614 };
615 
616 // V5.2: [5.4.9] `has_device_addr` clause
617 template <typename T, typename I, typename E> //
618 struct HasDeviceAddrT {
619   using List = ObjectListT<I, E>;
620   using WrapperTrait = std::true_type;
621   List v;
622 };
623 
624 // V5.2: [15.1.2] `hint` clause
625 template <typename T, typename I, typename E> //
626 struct HintT {
627   using HintExpr = E;
628   using WrapperTrait = std::true_type;
629   HintExpr v;
630 };
631 
632 // V5.2: [8.3.1] Assumption clauses
633 template <typename T, typename I, typename E> //
634 struct HoldsT {
635   using WrapperTrait = std::true_type;
636   E v; // No argument name in spec 5.2
637 };
638 
639 // V5.2: [3.4] `if` clause
640 template <typename T, typename I, typename E> //
641 struct IfT {
642   using DirectiveNameModifier = type::DirectiveName;
643   using IfExpression = E;
644   using TupleTrait = std::true_type;
645   std::tuple<OPT(DirectiveNameModifier), IfExpression> t;
646 };
647 
648 // V5.2: [7.7.1] `branch` clauses
649 template <typename T, typename I, typename E> //
650 struct InbranchT {
651   using EmptyTrait = std::true_type;
652 };
653 
654 // V5.2: [5.6.1] `exclusive` clause
655 template <typename T, typename I, typename E> //
656 struct InclusiveT {
657   using List = ObjectListT<I, E>;
658   using WrapperTrait = std::true_type;
659   List v;
660 };
661 
662 // V5.2: [7.8.3] `indirect` clause
663 template <typename T, typename I, typename E> //
664 struct IndirectT {
665   using InvokedByFptr = E;
666   using WrapperTrait = std::true_type;
667   InvokedByFptr v;
668 };
669 
670 // V5.2: [14.1.2] `init` clause
671 template <typename T, typename I, typename E> //
672 struct InitT {
673   using ForeignRuntimeId = E;
674   using InteropVar = ObjectT<I, E>;
675   using InteropPreference = ListT<ForeignRuntimeId>;
676   ENUM(InteropType, Target, Targetsync);   // Repeatable
677   using InteropTypes = ListT<InteropType>; // Not a spec name
678 
679   using TupleTrait = std::true_type;
680   std::tuple<OPT(InteropPreference), InteropTypes, InteropVar> t;
681 };
682 
683 // V5.2: [5.5.4] `initializer` clause
684 template <typename T, typename I, typename E> //
685 struct InitializerT {
686   using InitializerExpr = E;
687   using WrapperTrait = std::true_type;
688   InitializerExpr v;
689 };
690 
691 // V5.2: [5.5.10] `in_reduction` clause
692 template <typename T, typename I, typename E> //
693 struct InReductionT {
694   using List = ObjectListT<I, E>;
695   // See note at the definition of the ReductionIdentifierT type.
696   // The name ReductionIdentifiers is not a spec name.
697   using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>;
698   using TupleTrait = std::true_type;
699   std::tuple<ReductionIdentifiers, List> t;
700 };
701 
702 // V5.2: [5.4.7] `is_device_ptr` clause
703 template <typename T, typename I, typename E> //
704 struct IsDevicePtrT {
705   using List = ObjectListT<I, E>;
706   using WrapperTrait = std::true_type;
707   List v;
708 };
709 
710 // V5.2: [5.4.5] `lastprivate` clause
711 template <typename T, typename I, typename E> //
712 struct LastprivateT {
713   using List = ObjectListT<I, E>;
714   ENUM(LastprivateModifier, Conditional);
715   using TupleTrait = std::true_type;
716   std::tuple<OPT(LastprivateModifier), List> t;
717 };
718 
719 // V5.2: [5.4.6] `linear` clause
720 template <typename T, typename I, typename E> //
721 struct LinearT {
722   // std::get<type> won't work here due to duplicate types in the tuple.
723   using List = ObjectListT<I, E>;
724   using StepSimpleModifier = E;
725   using StepComplexModifier = E;
726   ENUM(LinearModifier, Ref, Val, Uval);
727 
728   using TupleTrait = std::true_type;
729   // Step == nullptr means 1.
730   std::tuple<OPT(StepSimpleModifier), OPT(StepComplexModifier),
731              OPT(LinearModifier), List>
732       t;
733 };
734 
735 // V5.2: [5.8.5] `link` clause
736 template <typename T, typename I, typename E> //
737 struct LinkT {
738   using List = ObjectListT<I, E>;
739   using WrapperTrait = std::true_type;
740   List v;
741 };
742 
743 // V5.2: [5.8.3] `map` clause
744 template <typename T, typename I, typename E> //
745 struct MapT {
746   using LocatorList = ObjectListT<I, E>;
747   ENUM(MapType, To, From, Tofrom, Alloc, Release, Delete);
748   ENUM(MapTypeModifier, Always, Close, Present, OmpxHold);
749   // See note at the definition of the MapperT type.
750   using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name
751   using Iterator = type::IteratorT<T, I, E>;
752   using MapTypeModifiers = ListT<MapTypeModifier>; // Not a spec name
753 
754   using TupleTrait = std::true_type;
755   std::tuple<OPT(MapType), OPT(MapTypeModifiers), OPT(Mappers), OPT(Iterator),
756              LocatorList>
757       t;
758 };
759 
760 // V5.2: [7.5.1] `match` clause
761 template <typename T, typename I, typename E> //
762 struct MatchT {
763   using IncompleteTrait = std::true_type;
764 };
765 
766 // V5.2: [12.2] `mergeable` clause
767 template <typename T, typename I, typename E> //
768 struct MergeableT {
769   using EmptyTrait = std::true_type;
770 };
771 
772 // V5.2: [8.5.2] `message` clause
773 template <typename T, typename I, typename E> //
774 struct MessageT {
775   using MsgString = E;
776   using WrapperTrait = std::true_type;
777   MsgString v;
778 };
779 
780 // V5.2: [7.6.2] `nocontext` clause
781 template <typename T, typename I, typename E> //
782 struct NocontextT {
783   using DoNotUpdateContext = E;
784   using WrapperTrait = std::true_type;
785   DoNotUpdateContext v;
786 };
787 
788 // V5.2: [15.7] `nowait` clause
789 template <typename T, typename I, typename E> //
790 struct NogroupT {
791   using EmptyTrait = std::true_type;
792 };
793 
794 // V5.2: [10.4.1] `nontemporal` clause
795 template <typename T, typename I, typename E> //
796 struct NontemporalT {
797   using List = ObjectListT<I, E>;
798   using WrapperTrait = std::true_type;
799   List v;
800 };
801 
802 // V5.2: [8.3.1] `assumption` clauses
803 template <typename T, typename I, typename E> //
804 struct NoOpenmpT {
805   using EmptyTrait = std::true_type;
806 };
807 
808 // V5.2: [8.3.1] `assumption` clauses
809 template <typename T, typename I, typename E> //
810 struct NoOpenmpRoutinesT {
811   using EmptyTrait = std::true_type;
812 };
813 
814 // V5.2: [8.3.1] `assumption` clauses
815 template <typename T, typename I, typename E> //
816 struct NoParallelismT {
817   using EmptyTrait = std::true_type;
818 };
819 
820 // V5.2: [7.7.1] `branch` clauses
821 template <typename T, typename I, typename E> //
822 struct NotinbranchT {
823   using EmptyTrait = std::true_type;
824 };
825 
826 // V5.2: [7.6.1] `novariants` clause
827 template <typename T, typename I, typename E> //
828 struct NovariantsT {
829   using DoNotUseVariant = E;
830   using WrapperTrait = std::true_type;
831   DoNotUseVariant v;
832 };
833 
834 // V5.2: [15.6] `nowait` clause
835 template <typename T, typename I, typename E> //
836 struct NowaitT {
837   using EmptyTrait = std::true_type;
838 };
839 
840 // V5.2: [12.6.2] `num_tasks` clause
841 template <typename T, typename I, typename E> //
842 struct NumTasksT {
843   using NumTasks = E;
844   ENUM(Prescriptiveness, Strict);
845   using TupleTrait = std::true_type;
846   std::tuple<OPT(Prescriptiveness), NumTasks> t;
847 };
848 
849 // V5.2: [10.2.1] `num_teams` clause
850 template <typename T, typename I, typename E> //
851 struct NumTeamsT {
852   using TupleTrait = std::true_type;
853   using LowerBound = E;
854   using UpperBound = E;
855   std::tuple<OPT(LowerBound), UpperBound> t;
856 };
857 
858 // V5.2: [10.1.2] `num_threads` clause
859 template <typename T, typename I, typename E> //
860 struct NumThreadsT {
861   using Nthreads = E;
862   using WrapperTrait = std::true_type;
863   Nthreads v;
864 };
865 
866 template <typename T, typename I, typename E> //
867 struct OmpxAttributeT {
868   using EmptyTrait = std::true_type;
869 };
870 
871 template <typename T, typename I, typename E> //
872 struct OmpxBareT {
873   using EmptyTrait = std::true_type;
874 };
875 
876 template <typename T, typename I, typename E> //
877 struct OmpxDynCgroupMemT {
878   using WrapperTrait = std::true_type;
879   E v;
880 };
881 
882 // V5.2: [10.3] `order` clause
883 template <typename T, typename I, typename E> //
884 struct OrderT {
885   ENUM(OrderModifier, Reproducible, Unconstrained);
886   ENUM(Ordering, Concurrent);
887   using TupleTrait = std::true_type;
888   std::tuple<OPT(OrderModifier), Ordering> t;
889 };
890 
891 // V5.2: [4.4.4] `ordered` clause
892 template <typename T, typename I, typename E> //
893 struct OrderedT {
894   using N = E;
895   using WrapperTrait = std::true_type;
896   OPT(N) v;
897 };
898 
899 // V5.2: [7.4.2] `otherwise` clause
900 template <typename T, typename I, typename E> //
901 struct OtherwiseT {
902   using IncompleteTrait = std::true_type;
903 };
904 
905 // V5.2: [9.2.2] `partial` clause
906 template <typename T, typename I, typename E> //
907 struct PartialT {
908   using UnrollFactor = E;
909   using WrapperTrait = std::true_type;
910   OPT(UnrollFactor) v;
911 };
912 
913 // V5.2: [12.4] `priority` clause
914 template <typename T, typename I, typename E> //
915 struct PriorityT {
916   using PriorityValue = E;
917   using WrapperTrait = std::true_type;
918   PriorityValue v;
919 };
920 
921 // V5.2: [5.4.3] `private` clause
922 template <typename T, typename I, typename E> //
923 struct PrivateT {
924   using List = ObjectListT<I, E>;
925   using WrapperTrait = std::true_type;
926   List v;
927 };
928 
929 // V5.2: [10.1.4] `proc_bind` clause
930 template <typename T, typename I, typename E> //
931 struct ProcBindT {
932   ENUM(AffinityPolicy, Close, Master, Spread, Primary);
933   using WrapperTrait = std::true_type;
934   AffinityPolicy v;
935 };
936 
937 // V5.2: [15.8.2] Atomic clauses
938 template <typename T, typename I, typename E> //
939 struct ReadT {
940   using EmptyTrait = std::true_type;
941 };
942 
943 // V5.2: [5.5.8] `reduction` clause
944 template <typename T, typename I, typename E> //
945 struct ReductionT {
946   using List = ObjectListT<I, E>;
947   // See note at the definition of the ReductionIdentifierT type.
948   // The name ReductionIdentifiers is not a spec name.
949   using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>;
950   ENUM(ReductionModifier, Default, Inscan, Task);
951   using TupleTrait = std::true_type;
952   std::tuple<OPT(ReductionModifier), ReductionIdentifiers, List> t;
953 };
954 
955 // V5.2: [15.8.1] `memory-order` clauses
956 template <typename T, typename I, typename E> //
957 struct RelaxedT {
958   using EmptyTrait = std::true_type;
959 };
960 
961 // V5.2: [15.8.1] `memory-order` clauses
962 template <typename T, typename I, typename E> //
963 struct ReleaseT {
964   using EmptyTrait = std::true_type;
965 };
966 
967 // V5.2: [8.2.1] `requirement` clauses
968 template <typename T, typename I, typename E> //
969 struct ReverseOffloadT {
970   using EmptyTrait = std::true_type;
971 };
972 
973 // V5.2: [10.4.2] `safelen` clause
974 template <typename T, typename I, typename E> //
975 struct SafelenT {
976   using Length = E;
977   using WrapperTrait = std::true_type;
978   Length v;
979 };
980 
981 // V5.2: [11.5.3] `schedule` clause
982 template <typename T, typename I, typename E> //
983 struct ScheduleT {
984   ENUM(Kind, Static, Dynamic, Guided, Auto, Runtime);
985   using ChunkSize = E;
986   ENUM(OrderingModifier, Monotonic, Nonmonotonic);
987   ENUM(ChunkModifier, Simd);
988   using TupleTrait = std::true_type;
989   std::tuple<Kind, OPT(OrderingModifier), OPT(ChunkModifier), OPT(ChunkSize)> t;
990 };
991 
992 // V5.2: [15.8.1] Memory-order clauses
993 template <typename T, typename I, typename E> //
994 struct SeqCstT {
995   using EmptyTrait = std::true_type;
996 };
997 
998 // V5.2: [8.5.1] `severity` clause
999 template <typename T, typename I, typename E> //
1000 struct SeverityT {
1001   ENUM(SevLevel, Fatal, Warning);
1002   using WrapperTrait = std::true_type;
1003   SevLevel v;
1004 };
1005 
1006 // V5.2: [5.4.2] `shared` clause
1007 template <typename T, typename I, typename E> //
1008 struct SharedT {
1009   using List = ObjectListT<I, E>;
1010   using WrapperTrait = std::true_type;
1011   List v;
1012 };
1013 
1014 // V5.2: [15.10.3] `parallelization-level` clauses
1015 template <typename T, typename I, typename E> //
1016 struct SimdT {
1017   using EmptyTrait = std::true_type;
1018 };
1019 
1020 // V5.2: [10.4.3] `simdlen` clause
1021 template <typename T, typename I, typename E> //
1022 struct SimdlenT {
1023   using Length = E;
1024   using WrapperTrait = std::true_type;
1025   Length v;
1026 };
1027 
1028 // V5.2: [9.1.1] `sizes` clause
1029 template <typename T, typename I, typename E> //
1030 struct SizesT {
1031   using SizeList = ListT<E>;
1032   using WrapperTrait = std::true_type;
1033   SizeList v;
1034 };
1035 
1036 // V5.2: [5.5.9] `task_reduction` clause
1037 template <typename T, typename I, typename E> //
1038 struct TaskReductionT {
1039   using List = ObjectListT<I, E>;
1040   // See note at the definition of the ReductionIdentifierT type.
1041   // The name ReductionIdentifiers is not a spec name.
1042   using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>;
1043   using TupleTrait = std::true_type;
1044   std::tuple<ReductionIdentifiers, List> t;
1045 };
1046 
1047 // V5.2: [13.3] `thread_limit` clause
1048 template <typename T, typename I, typename E> //
1049 struct ThreadLimitT {
1050   using Threadlim = E;
1051   using WrapperTrait = std::true_type;
1052   Threadlim v;
1053 };
1054 
1055 // V5.2: [15.10.3] `parallelization-level` clauses
1056 template <typename T, typename I, typename E> //
1057 struct ThreadsT {
1058   using EmptyTrait = std::true_type;
1059 };
1060 
1061 // V5.2: [5.9.1] `to` clause
1062 template <typename T, typename I, typename E> //
1063 struct ToT {
1064   using LocatorList = ObjectListT<I, E>;
1065   using Expectation = type::MotionExpectation;
1066   // See note at the definition of the MapperT type.
1067   using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name
1068   using Iterator = type::IteratorT<T, I, E>;
1069 
1070   using TupleTrait = std::true_type;
1071   std::tuple<OPT(Expectation), OPT(Mappers), OPT(Iterator), LocatorList> t;
1072 };
1073 
1074 // V5.2: [8.2.1] `requirement` clauses
1075 template <typename T, typename I, typename E> //
1076 struct UnifiedAddressT {
1077   using EmptyTrait = std::true_type;
1078 };
1079 
1080 // V5.2: [8.2.1] `requirement` clauses
1081 template <typename T, typename I, typename E> //
1082 struct UnifiedSharedMemoryT {
1083   using EmptyTrait = std::true_type;
1084 };
1085 
1086 // V5.2: [5.10] `uniform` clause
1087 template <typename T, typename I, typename E> //
1088 struct UniformT {
1089   using ParameterList = ObjectListT<I, E>;
1090   using WrapperTrait = std::true_type;
1091   ParameterList v;
1092 };
1093 
1094 template <typename T, typename I, typename E> //
1095 struct UnknownT {
1096   using EmptyTrait = std::true_type;
1097 };
1098 
1099 // V5.2: [12.1] `untied` clause
1100 template <typename T, typename I, typename E> //
1101 struct UntiedT {
1102   using EmptyTrait = std::true_type;
1103 };
1104 
1105 // Both of the following
1106 // V5.2: [15.8.2] `atomic` clauses
1107 // V5.2: [15.9.3] `update` clause
1108 template <typename T, typename I, typename E> //
1109 struct UpdateT {
1110   using TaskDependenceType = tomp::type::TaskDependenceType;
1111   using WrapperTrait = std::true_type;
1112   OPT(TaskDependenceType) v;
1113 };
1114 
1115 // V5.2: [14.1.3] `use` clause
1116 template <typename T, typename I, typename E> //
1117 struct UseT {
1118   using InteropVar = ObjectT<I, E>;
1119   using WrapperTrait = std::true_type;
1120   InteropVar v;
1121 };
1122 
1123 // V5.2: [5.4.10] `use_device_addr` clause
1124 template <typename T, typename I, typename E> //
1125 struct UseDeviceAddrT {
1126   using List = ObjectListT<I, E>;
1127   using WrapperTrait = std::true_type;
1128   List v;
1129 };
1130 
1131 // V5.2: [5.4.8] `use_device_ptr` clause
1132 template <typename T, typename I, typename E> //
1133 struct UseDevicePtrT {
1134   using List = ObjectListT<I, E>;
1135   using WrapperTrait = std::true_type;
1136   List v;
1137 };
1138 
1139 // V5.2: [6.8] `uses_allocators` clause
1140 template <typename T, typename I, typename E> //
1141 struct UsesAllocatorsT {
1142   using MemSpace = E;
1143   using TraitsArray = ObjectT<I, E>;
1144   using Allocator = E;
1145   using AllocatorSpec =
1146       std::tuple<OPT(MemSpace), OPT(TraitsArray), Allocator>; // Not a spec name
1147   using Allocators = ListT<AllocatorSpec>;                    // Not a spec name
1148   using WrapperTrait = std::true_type;
1149   Allocators v;
1150 };
1151 
1152 // V5.2: [15.8.3] `extended-atomic` clauses
1153 template <typename T, typename I, typename E> //
1154 struct WeakT {
1155   using EmptyTrait = std::true_type;
1156 };
1157 
1158 // V5.2: [7.4.1] `when` clause
1159 template <typename T, typename I, typename E> //
1160 struct WhenT {
1161   using IncompleteTrait = std::true_type;
1162 };
1163 
1164 // V5.2: [15.8.2] Atomic clauses
1165 template <typename T, typename I, typename E> //
1166 struct WriteT {
1167   using EmptyTrait = std::true_type;
1168 };
1169 
1170 // ---
1171 
1172 template <typename T, typename I, typename E>
1173 using ExtensionClausesT =
1174     std::variant<OmpxAttributeT<T, I, E>, OmpxBareT<T, I, E>,
1175                  OmpxDynCgroupMemT<T, I, E>>;
1176 
1177 template <typename T, typename I, typename E>
1178 using EmptyClausesT = std::variant<
1179     AcqRelT<T, I, E>, AcquireT<T, I, E>, CaptureT<T, I, E>, CompareT<T, I, E>,
1180     DynamicAllocatorsT<T, I, E>, FullT<T, I, E>, InbranchT<T, I, E>,
1181     MergeableT<T, I, E>, NogroupT<T, I, E>, NoOpenmpRoutinesT<T, I, E>,
1182     NoOpenmpT<T, I, E>, NoParallelismT<T, I, E>, NotinbranchT<T, I, E>,
1183     NowaitT<T, I, E>, ReadT<T, I, E>, RelaxedT<T, I, E>, ReleaseT<T, I, E>,
1184     ReverseOffloadT<T, I, E>, SeqCstT<T, I, E>, SimdT<T, I, E>,
1185     ThreadsT<T, I, E>, UnifiedAddressT<T, I, E>, UnifiedSharedMemoryT<T, I, E>,
1186     UnknownT<T, I, E>, UntiedT<T, I, E>, UseT<T, I, E>, WeakT<T, I, E>,
1187     WriteT<T, I, E>>;
1188 
1189 template <typename T, typename I, typename E>
1190 using IncompleteClausesT =
1191     std::variant<AdjustArgsT<T, I, E>, AppendArgsT<T, I, E>, MatchT<T, I, E>,
1192                  OtherwiseT<T, I, E>, WhenT<T, I, E>>;
1193 
1194 template <typename T, typename I, typename E>
1195 using TupleClausesT =
1196     std::variant<AffinityT<T, I, E>, AlignedT<T, I, E>, AllocateT<T, I, E>,
1197                  DefaultmapT<T, I, E>, DeviceT<T, I, E>, DistScheduleT<T, I, E>,
1198                  DoacrossT<T, I, E>, FromT<T, I, E>, GrainsizeT<T, I, E>,
1199                  IfT<T, I, E>, InitT<T, I, E>, InReductionT<T, I, E>,
1200                  LastprivateT<T, I, E>, LinearT<T, I, E>, MapT<T, I, E>,
1201                  NumTasksT<T, I, E>, OrderT<T, I, E>, ReductionT<T, I, E>,
1202                  ScheduleT<T, I, E>, TaskReductionT<T, I, E>, ToT<T, I, E>>;
1203 
1204 template <typename T, typename I, typename E>
1205 using UnionClausesT = std::variant<DependT<T, I, E>>;
1206 
1207 template <typename T, typename I, typename E>
1208 using WrapperClausesT = std::variant<
1209     AbsentT<T, I, E>, AlignT<T, I, E>, AllocatorT<T, I, E>,
1210     AtomicDefaultMemOrderT<T, I, E>, AtT<T, I, E>, BindT<T, I, E>,
1211     CollapseT<T, I, E>, ContainsT<T, I, E>, CopyinT<T, I, E>,
1212     CopyprivateT<T, I, E>, DefaultT<T, I, E>, DestroyT<T, I, E>,
1213     DetachT<T, I, E>, DeviceTypeT<T, I, E>, EnterT<T, I, E>,
1214     ExclusiveT<T, I, E>, FailT<T, I, E>, FilterT<T, I, E>, FinalT<T, I, E>,
1215     FirstprivateT<T, I, E>, HasDeviceAddrT<T, I, E>, HintT<T, I, E>,
1216     HoldsT<T, I, E>, InclusiveT<T, I, E>, IndirectT<T, I, E>,
1217     InitializerT<T, I, E>, IsDevicePtrT<T, I, E>, LinkT<T, I, E>,
1218     MessageT<T, I, E>, NocontextT<T, I, E>, NontemporalT<T, I, E>,
1219     NovariantsT<T, I, E>, NumTeamsT<T, I, E>, NumThreadsT<T, I, E>,
1220     OrderedT<T, I, E>, PartialT<T, I, E>, PriorityT<T, I, E>, PrivateT<T, I, E>,
1221     ProcBindT<T, I, E>, SafelenT<T, I, E>, SeverityT<T, I, E>, SharedT<T, I, E>,
1222     SimdlenT<T, I, E>, SizesT<T, I, E>, ThreadLimitT<T, I, E>,
1223     UniformT<T, I, E>, UpdateT<T, I, E>, UseDeviceAddrT<T, I, E>,
1224     UseDevicePtrT<T, I, E>, UsesAllocatorsT<T, I, E>>;
1225 
1226 template <typename T, typename I, typename E>
1227 using UnionOfAllClausesT = typename type::Union< //
1228     EmptyClausesT<T, I, E>,                      //
1229     ExtensionClausesT<T, I, E>,                  //
1230     IncompleteClausesT<T, I, E>,                 //
1231     TupleClausesT<T, I, E>,                      //
1232     UnionClausesT<T, I, E>,                      //
1233     WrapperClausesT<T, I, E>                     //
1234     >::type;
1235 
1236 } // namespace clause
1237 
1238 // The variant wrapper that encapsulates all possible specific clauses.
1239 // The `Extras` arguments are additional types representing local extensions
1240 // to the clause set, e.g.
1241 //
1242 // using Clause = ClauseT<Type, Id, Expr,
1243 //                        MyClause1, MyClause2>;
1244 //
1245 // The member Clause::u will be a variant containing all specific clauses
1246 // defined above, plus MyClause1 and MyClause2.
1247 template <typename TypeType, typename IdType, typename ExprType,
1248           typename... Extras>
1249 struct ClauseT {
1250   using TypeTy = TypeType;
1251   using IdTy = IdType;
1252   using ExprTy = ExprType;
1253 
1254   using VariantTy = typename type::Union<
1255       clause::UnionOfAllClausesT<TypeType, IdType, ExprType>,
1256       std::variant<Extras...>>::type;
1257 
1258   llvm::omp::Clause id; // The numeric id of the clause
1259   using UnionTrait = std::true_type;
1260   VariantTy u;
1261 };
1262 
1263 } // namespace tomp
1264 
1265 #undef OPT
1266 #undef ENUM
1267 
1268 #endif // LLVM_FRONTEND_OPENMP_CLAUSET_H
1269