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