1 //===- llvm/ADT/PointerSumType.h --------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_ADT_POINTERSUMTYPE_H
10 #define LLVM_ADT_POINTERSUMTYPE_H
12 #include "llvm/ADT/bit.h"
13 #include "llvm/ADT/DenseMapInfo.h"
14 #include "llvm/Support/PointerLikeTypeTraits.h"
17 #include <type_traits>
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
25 template <uintptr_t N
, typename PointerArgT
,
26 typename TraitsArgT
= PointerLikeTypeTraits
<PointerArgT
>>
27 struct PointerSumTypeMember
{
29 using PointerT
= PointerArgT
;
30 using TraitsT
= TraitsArgT
;
35 template <typename TagT
, typename
... MemberTs
> struct PointerSumTypeHelper
;
37 } // end namespace detail
39 /// A sum type over pointer-like types.
41 /// This is a normal tagged union across pointer-like types that uses the low
42 /// bits of the pointers to store the tag.
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.
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.
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.
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.
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
...>;
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.
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
86 StorageT() : Value(0) {}
90 typename
HelperT::template Lookup
<HelperT::MinTag
>::PointerT MinTagPointer
;
96 constexpr PointerSumType() = default;
98 /// A typed setter to a given tagged member of the sum type.
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
;
107 /// A typed constructor for a specific tagged member of the sum type.
109 static PointerSumType
110 create(typename
HelperT::template Lookup
<N
>::PointerT Pointer
) {
111 PointerSumType Result
;
112 Result
.set
<N
>(Pointer
);
116 /// Clear the value to null with the min tag type.
117 void clear() { set
<HelperT::MinTag
>(nullptr); }
119 TagT
getTag() const {
120 return static_cast<TagT
>(getOpaqueValue() & HelperT::TagMask
);
123 template <TagT N
> bool is() const { return N
== getTag(); }
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
);
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(
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
140 typename
HelperT::template Lookup
<HelperT::MinTag
>::PointerT
const *
141 getAddrOfZeroTagPointer() const {
142 return const_cast<PointerSumType
*>(this)->getAddrOfZeroTagPointer();
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
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
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
;
166 explicit operator bool() const {
167 return getOpaqueValue() & HelperT::PointerMask
;
169 bool operator==(const PointerSumType
&R
) const {
170 return getOpaqueValue() == R
.getOpaqueValue();
172 bool operator!=(const PointerSumType
&R
) const {
173 return getOpaqueValue() != R
.getOpaqueValue();
175 bool operator<(const PointerSumType
&R
) const {
176 return getOpaqueValue() < R
.getOpaqueValue();
178 bool operator>(const PointerSumType
&R
) const {
179 return getOpaqueValue() > R
.getOpaqueValue();
181 bool operator<=(const PointerSumType
&R
) const {
182 return getOpaqueValue() <= R
.getOpaqueValue();
184 bool operator>=(const PointerSumType
&R
) const {
185 return getOpaqueValue() >= R
.getOpaqueValue();
188 uintptr_t getOpaqueValue() const {
189 // Read the underlying storage of the union, regardless of the active
191 return bit_cast
<uintptr_t>(Storage
);
195 void *getVoidPtr() const {
196 return reinterpret_cast<void *>(getOpaqueValue() & HelperT::PointerMask
);
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
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 ovorload.
218 using MemberT
= decltype(
219 LookupOverload
<N
>(static_cast<PointerSumTypeHelper
*>(nullptr)));
221 /// The Nth member's pointer type.
222 using PointerT
= typename
MemberT::PointerT
;
224 /// The Nth member's traits type.
225 using TraitsT
= typename
MemberT::TraitsT
;
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
)> {
235 template <uintptr_t V
>
236 struct Min
<V
> : std::integral_constant
<uintptr_t, V
> {};
237 enum { NumTagBits
= Min
<MemberTs::TraitsT::NumLowBitsAvailable
...>::value
};
239 // Also compute the smallest discriminant and various masks for convenience.
240 constexpr static TagT MinTag
=
241 static_cast<TagT
>(Min
<MemberTs::Tag
...>::value
);
243 PointerMask
= static_cast<uint64_t>(-1) << NumTagBits
,
244 TagMask
= ~PointerMask
247 // Finally we need a recursive template to do static checks of each
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!");
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!");
258 static_assert(Checker
<MemberTs
...>::value
,
259 "Each member must pass the checker.");
262 } // end namespace detail
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
};
271 typename
HelperT::template Lookup
<HelperT::MinTag
>::PointerT
;
272 using SomePointerInfo
= DenseMapInfo
<SomePointerT
>;
274 static inline SumType
getEmptyKey() {
275 return SumType::create
<SomeTag
>(SomePointerInfo::getEmptyKey());
278 static inline SumType
getTombstoneKey() {
279 return SumType::create
<SomeTag
>(SomePointerInfo::getTombstoneKey());
282 static unsigned getHashValue(const SumType
&Arg
) {
283 uintptr_t OpaqueValue
= Arg
.getOpaqueValue();
284 return DenseMapInfo
<uintptr_t>::getHashValue(OpaqueValue
);
287 static bool isEqual(const SumType
&LHS
, const SumType
&RHS
) {
292 } // end namespace llvm
294 #endif // LLVM_ADT_POINTERSUMTYPE_H