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/DenseMapInfo.h" 14 #include "llvm/Support/Compiler.h" 15 #include "llvm/Support/PointerLikeTypeTraits.h" 16 17 namespace llvm { 18 19 /// A compile time pair of an integer tag and the pointer-like type which it 20 /// indexes within a sum type. Also allows the user to specify a particular 21 /// traits class for pointer types with custom behavior such as over-aligned 22 /// allocation. 23 template <uintptr_t N, typename PointerArgT, 24 typename TraitsArgT = PointerLikeTypeTraits<PointerArgT>> 25 struct PointerSumTypeMember { 26 enum { Tag = N }; 27 typedef PointerArgT PointerT; 28 typedef TraitsArgT TraitsT; 29 }; 30 31 namespace detail { 32 33 template <typename TagT, typename... MemberTs> 34 struct PointerSumTypeHelper; 35 36 } 37 38 /// A sum type over pointer-like types. 39 /// 40 /// This is a normal tagged union across pointer-like types that uses the low 41 /// bits of the pointers to store the tag. 42 /// 43 /// Each member of the sum type is specified by passing a \c 44 /// PointerSumTypeMember specialization in the variadic member argument list. 45 /// This allows the user to control the particular tag value associated with 46 /// a particular type, use the same type for multiple different tags, and 47 /// customize the pointer-like traits used for a particular member. Note that 48 /// these *must* be specializations of \c PointerSumTypeMember, no other type 49 /// will suffice, even if it provides a compatible interface. 50 /// 51 /// This type implements all of the comparison operators and even hash table 52 /// support by comparing the underlying storage of the pointer values. It 53 /// doesn't support delegating to particular members for comparisons. 54 /// 55 /// It also default constructs to a zero tag with a null pointer, whatever that 56 /// would be. This means that the zero value for the tag type is significant 57 /// and may be desireable to set to a state that is particularly desirable to 58 /// default construct. 59 /// 60 /// There is no support for constructing or accessing with a dynamic tag as 61 /// that would fundamentally violate the type safety provided by the sum type. 62 template <typename TagT, typename... MemberTs> class PointerSumType { 63 uintptr_t Value; 64 65 typedef detail::PointerSumTypeHelper<TagT, MemberTs...> HelperT; 66 67 public: PointerSumType()68 PointerSumType() : Value(0) {} 69 70 /// A typed constructor for a specific tagged member of the sum type. 71 template <TagT N> 72 static PointerSumType create(typename HelperT::template Lookup<N>::PointerT Pointer)73 create(typename HelperT::template Lookup<N>::PointerT Pointer) { 74 PointerSumType Result; 75 void *V = HelperT::template Lookup<N>::TraitsT::getAsVoidPointer(Pointer); 76 assert((reinterpret_cast<uintptr_t>(V) & HelperT::TagMask) == 0 && 77 "Pointer is insufficiently aligned to store the discriminant!"); 78 Result.Value = reinterpret_cast<uintptr_t>(V) | N; 79 return Result; 80 } 81 getTag()82 TagT getTag() const { return static_cast<TagT>(Value & HelperT::TagMask); } 83 is()84 template <TagT N> bool is() const { return N == getTag(); } 85 get()86 template <TagT N> typename HelperT::template Lookup<N>::PointerT get() const { 87 void *P = is<N>() ? getImpl() : nullptr; 88 return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(P); 89 } 90 91 template <TagT N> cast()92 typename HelperT::template Lookup<N>::PointerT cast() const { 93 assert(is<N>() && "This instance has a different active member."); 94 return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(getImpl()); 95 } 96 97 operator bool() const { return Value & HelperT::PointerMask; } 98 bool operator==(const PointerSumType &R) const { return Value == R.Value; } 99 bool operator!=(const PointerSumType &R) const { return Value != R.Value; } 100 bool operator<(const PointerSumType &R) const { return Value < R.Value; } 101 bool operator>(const PointerSumType &R) const { return Value > R.Value; } 102 bool operator<=(const PointerSumType &R) const { return Value <= R.Value; } 103 bool operator>=(const PointerSumType &R) const { return Value >= R.Value; } 104 getOpaqueValue()105 uintptr_t getOpaqueValue() const { return Value; } 106 107 protected: getImpl()108 void *getImpl() const { 109 return reinterpret_cast<void *>(Value & HelperT::PointerMask); 110 } 111 }; 112 113 namespace detail { 114 115 /// A helper template for implementing \c PointerSumType. It provides fast 116 /// compile-time lookup of the member from a particular tag value, along with 117 /// useful constants and compile time checking infrastructure.. 118 template <typename TagT, typename... MemberTs> 119 struct PointerSumTypeHelper : MemberTs... { 120 // First we use a trick to allow quickly looking up information about 121 // a particular member of the sum type. This works because we arranged to 122 // have this type derive from all of the member type templates. We can select 123 // the matching member for a tag using type deduction during overload 124 // resolution. 125 template <TagT N, typename PointerT, typename TraitsT> 126 static PointerSumTypeMember<N, PointerT, TraitsT> 127 LookupOverload(PointerSumTypeMember<N, PointerT, TraitsT> *); 128 template <TagT N> static void LookupOverload(...); 129 template <TagT N> struct Lookup { 130 // Compute a particular member type by resolving the lookup helper ovorload. 131 typedef decltype(LookupOverload<N>( 132 static_cast<PointerSumTypeHelper *>(nullptr))) MemberT; 133 134 /// The Nth member's pointer type. 135 typedef typename MemberT::PointerT PointerT; 136 137 /// The Nth member's traits type. 138 typedef typename MemberT::TraitsT TraitsT; 139 }; 140 141 // Next we need to compute the number of bits available for the discriminant 142 // by taking the min of the bits available for each member. Much of this 143 // would be amazingly easier with good constexpr support. 144 template <uintptr_t V, uintptr_t... Vs> 145 struct Min : std::integral_constant< 146 uintptr_t, (V < Min<Vs...>::value ? V : Min<Vs...>::value)> { 147 }; 148 template <uintptr_t V> 149 struct Min<V> : std::integral_constant<uintptr_t, V> {}; 150 enum { NumTagBits = Min<MemberTs::TraitsT::NumLowBitsAvailable...>::value }; 151 152 // Also compute the smallest discriminant and various masks for convenience. 153 enum : uint64_t { 154 MinTag = Min<MemberTs::Tag...>::value, 155 PointerMask = static_cast<uint64_t>(-1) << NumTagBits, 156 TagMask = ~PointerMask 157 }; 158 159 // Finally we need a recursive template to do static checks of each 160 // member. 161 template <typename MemberT, typename... InnerMemberTs> 162 struct Checker : Checker<InnerMemberTs...> { 163 static_assert(MemberT::Tag < (1 << NumTagBits), 164 "This discriminant value requires too many bits!"); 165 }; 166 template <typename MemberT> struct Checker<MemberT> : std::true_type { 167 static_assert(MemberT::Tag < (1 << NumTagBits), 168 "This discriminant value requires too many bits!"); 169 }; 170 static_assert(Checker<MemberTs...>::value, 171 "Each member must pass the checker."); 172 }; 173 174 } 175 176 // Teach DenseMap how to use PointerSumTypes as keys. 177 template <typename TagT, typename... MemberTs> 178 struct DenseMapInfo<PointerSumType<TagT, MemberTs...>> { 179 typedef PointerSumType<TagT, MemberTs...> SumType; 180 181 typedef detail::PointerSumTypeHelper<TagT, MemberTs...> HelperT; 182 enum { SomeTag = HelperT::MinTag }; 183 typedef typename HelperT::template Lookup<HelperT::MinTag>::PointerT 184 SomePointerT; 185 typedef DenseMapInfo<SomePointerT> SomePointerInfo; 186 187 static inline SumType getEmptyKey() { 188 return SumType::create<SomeTag>(SomePointerInfo::getEmptyKey()); 189 } 190 static inline SumType getTombstoneKey() { 191 return SumType::create<SomeTag>( 192 SomePointerInfo::getTombstoneKey()); 193 } 194 static unsigned getHashValue(const SumType &Arg) { 195 uintptr_t OpaqueValue = Arg.getOpaqueValue(); 196 return DenseMapInfo<uintptr_t>::getHashValue(OpaqueValue); 197 } 198 static bool isEqual(const SumType &LHS, const SumType &RHS) { 199 return LHS == RHS; 200 } 201 }; 202 203 } 204 205 #endif 206