LCOV - code coverage report
Current view: top level - include/llvm/ADT - PointerSumType.h (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 18 34 52.9 %
Date: 2018-10-20 13:21:21 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13