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