Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. //===- llvm/ADT/PointerSumType.h --------------------------------*- C++ -*-===//
  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.  
  9. #ifndef LLVM_ADT_POINTERSUMTYPE_H
  10. #define LLVM_ADT_POINTERSUMTYPE_H
  11.  
  12. #include "llvm/ADT/bit.h"
  13. #include "llvm/ADT/DenseMapInfo.h"
  14. #include "llvm/Support/PointerLikeTypeTraits.h"
  15. #include <cassert>
  16. #include <cstdint>
  17. #include <type_traits>
  18.  
  19. namespace llvm {
  20.  
  21. /// A compile time pair of an integer tag and the pointer-like type which it
  22. /// indexes within a sum type. Also allows the user to specify a particular
  23. /// traits class for pointer types with custom behavior such as over-aligned
  24. /// allocation.
  25. template <uintptr_t N, typename PointerArgT,
  26.           typename TraitsArgT = PointerLikeTypeTraits<PointerArgT>>
  27. struct PointerSumTypeMember {
  28.   enum { Tag = N };
  29.   using PointerT = PointerArgT;
  30.   using TraitsT = TraitsArgT;
  31. };
  32.  
  33. namespace detail {
  34.  
  35. template <typename TagT, typename... MemberTs> struct PointerSumTypeHelper;
  36.  
  37. } // end namespace detail
  38.  
  39. /// A sum type over pointer-like types.
  40. ///
  41. /// This is a normal tagged union across pointer-like types that uses the low
  42. /// bits of the pointers to store the tag.
  43. ///
  44. /// Each member of the sum type is specified by passing a \c
  45. /// PointerSumTypeMember specialization in the variadic member argument list.
  46. /// This allows the user to control the particular tag value associated with
  47. /// a particular type, use the same type for multiple different tags, and
  48. /// customize the pointer-like traits used for a particular member. Note that
  49. /// these *must* be specializations of \c PointerSumTypeMember, no other type
  50. /// will suffice, even if it provides a compatible interface.
  51. ///
  52. /// This type implements all of the comparison operators and even hash table
  53. /// support by comparing the underlying storage of the pointer values. It
  54. /// doesn't support delegating to particular members for comparisons.
  55. ///
  56. /// It also default constructs to a zero tag with a null pointer, whatever that
  57. /// would be. This means that the zero value for the tag type is significant
  58. /// and may be desirable to set to a state that is particularly desirable to
  59. /// default construct.
  60. ///
  61. /// Having a supported zero-valued tag also enables getting the address of a
  62. /// pointer stored with that tag provided it is stored in its natural bit
  63. /// representation. This works because in the case of a zero-valued tag, the
  64. /// pointer's value is directly stored into this object and we can expose the
  65. /// address of that internal storage. This is especially useful when building an
  66. /// `ArrayRef` of a single pointer stored in a sum type.
  67. ///
  68. /// There is no support for constructing or accessing with a dynamic tag as
  69. /// that would fundamentally violate the type safety provided by the sum type.
  70. template <typename TagT, typename... MemberTs> class PointerSumType {
  71.   using HelperT = detail::PointerSumTypeHelper<TagT, MemberTs...>;
  72.  
  73.   // We keep both the raw value and the min tag value's pointer in a union. When
  74.   // the minimum tag value is zero, this allows code below to cleanly expose the
  75.   // address of the zero-tag pointer instead of just the zero-tag pointer
  76.   // itself. This is especially useful when building `ArrayRef`s out of a single
  77.   // pointer. However, we have to carefully access the union due to the active
  78.   // member potentially changing. When we *store* a new value, we directly
  79.   // access the union to allow us to store using the obvious types. However,
  80.   // when we *read* a value, we copy the underlying storage out to avoid relying
  81.   // on one member or the other being active.
  82.   union StorageT {
  83.     // Ensure we get a null default constructed value. We don't use a member
  84.     // initializer because some compilers seem to not implement those correctly
  85.     // for a union.
  86.     StorageT() : Value(0) {}
  87.  
  88.     uintptr_t Value;
  89.  
  90.     typename HelperT::template Lookup<HelperT::MinTag>::PointerT MinTagPointer;
  91.   };
  92.  
  93.   StorageT Storage;
  94.  
  95. public:
  96.   constexpr PointerSumType() = default;
  97.  
  98.   /// A typed setter to a given tagged member of the sum type.
  99.   template <TagT N>
  100.   void set(typename HelperT::template Lookup<N>::PointerT Pointer) {
  101.     void *V = HelperT::template Lookup<N>::TraitsT::getAsVoidPointer(Pointer);
  102.     assert((reinterpret_cast<uintptr_t>(V) & HelperT::TagMask) == 0 &&
  103.            "Pointer is insufficiently aligned to store the discriminant!");
  104.     Storage.Value = reinterpret_cast<uintptr_t>(V) | N;
  105.   }
  106.  
  107.   /// A typed constructor for a specific tagged member of the sum type.
  108.   template <TagT N>
  109.   static PointerSumType
  110.   create(typename HelperT::template Lookup<N>::PointerT Pointer) {
  111.     PointerSumType Result;
  112.     Result.set<N>(Pointer);
  113.     return Result;
  114.   }
  115.  
  116.   /// Clear the value to null with the min tag type.
  117.   void clear() { set<HelperT::MinTag>(nullptr); }
  118.  
  119.   TagT getTag() const {
  120.     return static_cast<TagT>(getOpaqueValue() & HelperT::TagMask);
  121.   }
  122.  
  123.   template <TagT N> bool is() const { return N == getTag(); }
  124.  
  125.   template <TagT N> typename HelperT::template Lookup<N>::PointerT get() const {
  126.     void *P = is<N>() ? getVoidPtr() : nullptr;
  127.     return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(P);
  128.   }
  129.  
  130.   template <TagT N>
  131.   typename HelperT::template Lookup<N>::PointerT cast() const {
  132.     assert(is<N>() && "This instance has a different active member.");
  133.     return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(
  134.         getVoidPtr());
  135.   }
  136.  
  137.   /// If the tag is zero and the pointer's value isn't changed when being
  138.   /// stored, get the address of the stored value type-punned to the zero-tag's
  139.   /// pointer type.
  140.   typename HelperT::template Lookup<HelperT::MinTag>::PointerT const *
  141.   getAddrOfZeroTagPointer() const {
  142.     return const_cast<PointerSumType *>(this)->getAddrOfZeroTagPointer();
  143.   }
  144.  
  145.   /// If the tag is zero and the pointer's value isn't changed when being
  146.   /// stored, get the address of the stored value type-punned to the zero-tag's
  147.   /// pointer type.
  148.   typename HelperT::template Lookup<HelperT::MinTag>::PointerT *
  149.   getAddrOfZeroTagPointer() {
  150.     static_assert(HelperT::MinTag == 0, "Non-zero minimum tag value!");
  151.     assert(is<HelperT::MinTag>() && "The active tag is not zero!");
  152.     // Store the initial value of the pointer when read out of our storage.
  153.     auto InitialPtr = get<HelperT::MinTag>();
  154.     // Now update the active member of the union to be the actual pointer-typed
  155.     // member so that accessing it indirectly through the returned address is
  156.     // valid.
  157.     Storage.MinTagPointer = InitialPtr;
  158.     // Finally, validate that this was a no-op as expected by reading it back
  159.     // out using the same underlying-storage read as above.
  160.     assert(InitialPtr == get<HelperT::MinTag>() &&
  161.            "Switching to typed storage changed the pointer returned!");
  162.     // Now we can correctly return an address to typed storage.
  163.     return &Storage.MinTagPointer;
  164.   }
  165.  
  166.   explicit operator bool() const {
  167.     return getOpaqueValue() & HelperT::PointerMask;
  168.   }
  169.   bool operator==(const PointerSumType &R) const {
  170.     return getOpaqueValue() == R.getOpaqueValue();
  171.   }
  172.   bool operator!=(const PointerSumType &R) const {
  173.     return getOpaqueValue() != R.getOpaqueValue();
  174.   }
  175.   bool operator<(const PointerSumType &R) const {
  176.     return getOpaqueValue() < R.getOpaqueValue();
  177.   }
  178.   bool operator>(const PointerSumType &R) const {
  179.     return getOpaqueValue() > R.getOpaqueValue();
  180.   }
  181.   bool operator<=(const PointerSumType &R) const {
  182.     return getOpaqueValue() <= R.getOpaqueValue();
  183.   }
  184.   bool operator>=(const PointerSumType &R) const {
  185.     return getOpaqueValue() >= R.getOpaqueValue();
  186.   }
  187.  
  188.   uintptr_t getOpaqueValue() const {
  189.     // Read the underlying storage of the union, regardless of the active
  190.     // member.
  191.     return bit_cast<uintptr_t>(Storage);
  192.   }
  193.  
  194. protected:
  195.   void *getVoidPtr() const {
  196.     return reinterpret_cast<void *>(getOpaqueValue() & HelperT::PointerMask);
  197.   }
  198. };
  199.  
  200. namespace detail {
  201.  
  202. /// A helper template for implementing \c PointerSumType. It provides fast
  203. /// compile-time lookup of the member from a particular tag value, along with
  204. /// useful constants and compile time checking infrastructure..
  205. template <typename TagT, typename... MemberTs>
  206. struct PointerSumTypeHelper : MemberTs... {
  207.   // First we use a trick to allow quickly looking up information about
  208.   // a particular member of the sum type. This works because we arranged to
  209.   // have this type derive from all of the member type templates. We can select
  210.   // the matching member for a tag using type deduction during overload
  211.   // resolution.
  212.   template <TagT N, typename PointerT, typename TraitsT>
  213.   static PointerSumTypeMember<N, PointerT, TraitsT>
  214.   LookupOverload(PointerSumTypeMember<N, PointerT, TraitsT> *);
  215.   template <TagT N> static void LookupOverload(...);
  216.   template <TagT N> struct Lookup {
  217.     // Compute a particular member type by resolving the lookup helper overload.
  218.     using MemberT = decltype(
  219.         LookupOverload<N>(static_cast<PointerSumTypeHelper *>(nullptr)));
  220.  
  221.     /// The Nth member's pointer type.
  222.     using PointerT = typename MemberT::PointerT;
  223.  
  224.     /// The Nth member's traits type.
  225.     using TraitsT = typename MemberT::TraitsT;
  226.   };
  227.  
  228.   // Next we need to compute the number of bits available for the discriminant
  229.   // by taking the min of the bits available for each member. Much of this
  230.   // would be amazingly easier with good constexpr support.
  231.   template <uintptr_t V, uintptr_t... Vs>
  232.   struct Min : std::integral_constant<
  233.                    uintptr_t, (V < Min<Vs...>::value ? V : Min<Vs...>::value)> {
  234.   };
  235.   template <uintptr_t V>
  236.   struct Min<V> : std::integral_constant<uintptr_t, V> {};
  237.   enum { NumTagBits = Min<MemberTs::TraitsT::NumLowBitsAvailable...>::value };
  238.  
  239.   // Also compute the smallest discriminant and various masks for convenience.
  240.   constexpr static TagT MinTag =
  241.       static_cast<TagT>(Min<MemberTs::Tag...>::value);
  242.   enum : uint64_t {
  243.     PointerMask = static_cast<uint64_t>(-1) << NumTagBits,
  244.     TagMask = ~PointerMask
  245.   };
  246.  
  247.   // Finally we need a recursive template to do static checks of each
  248.   // member.
  249.   template <typename MemberT, typename... InnerMemberTs>
  250.   struct Checker : Checker<InnerMemberTs...> {
  251.     static_assert(MemberT::Tag < (1 << NumTagBits),
  252.                   "This discriminant value requires too many bits!");
  253.   };
  254.   template <typename MemberT> struct Checker<MemberT> : std::true_type {
  255.     static_assert(MemberT::Tag < (1 << NumTagBits),
  256.                   "This discriminant value requires too many bits!");
  257.   };
  258.   static_assert(Checker<MemberTs...>::value,
  259.                 "Each member must pass the checker.");
  260. };
  261.  
  262. } // end namespace detail
  263.  
  264. // Teach DenseMap how to use PointerSumTypes as keys.
  265. template <typename TagT, typename... MemberTs>
  266. struct DenseMapInfo<PointerSumType<TagT, MemberTs...>> {
  267.   using SumType = PointerSumType<TagT, MemberTs...>;
  268.   using HelperT = detail::PointerSumTypeHelper<TagT, MemberTs...>;
  269.   enum { SomeTag = HelperT::MinTag };
  270.   using SomePointerT =
  271.       typename HelperT::template Lookup<HelperT::MinTag>::PointerT;
  272.   using SomePointerInfo = DenseMapInfo<SomePointerT>;
  273.  
  274.   static inline SumType getEmptyKey() {
  275.     return SumType::template create<SomeTag>(SomePointerInfo::getEmptyKey());
  276.   }
  277.  
  278.   static inline SumType getTombstoneKey() {
  279.     return SumType::template create<SomeTag>(
  280.         SomePointerInfo::getTombstoneKey());
  281.   }
  282.  
  283.   static unsigned getHashValue(const SumType &Arg) {
  284.     uintptr_t OpaqueValue = Arg.getOpaqueValue();
  285.     return DenseMapInfo<uintptr_t>::getHashValue(OpaqueValue);
  286.   }
  287.  
  288.   static bool isEqual(const SumType &LHS, const SumType &RHS) {
  289.     return LHS == RHS;
  290.   }
  291. };
  292.  
  293. } // end namespace llvm
  294.  
  295. #endif // LLVM_ADT_POINTERSUMTYPE_H
  296.