LLVM 22.0.0git
PointerSumType.h
Go to the documentation of this file.
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
13#include "llvm/ADT/bit.h"
15#include <algorithm>
16#include <cassert>
17#include <cstdint>
18#include <type_traits>
19
20namespace 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.
26template <uintptr_t N, typename PointerArgT,
27 typename TraitsArgT = PointerLikeTypeTraits<PointerArgT>>
29 enum { Tag = N };
30 using PointerT = PointerArgT;
31 using TraitsT = TraitsArgT;
32};
33
34namespace detail {
35
36template <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.
71template <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 StorageT() : Value(0) {}
88
89 uintptr_t Value;
90
91 typename HelperT::template Lookup<HelperT::MinTag>::PointerT MinTagPointer;
92 };
93
94 StorageT Storage;
95
96public:
97 constexpr PointerSumType() = default;
98
99 /// A typed setter to a given tagged member of the sum type.
100 template <TagT N>
101 void set(typename HelperT::template Lookup<N>::PointerT Pointer) {
102 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 Storage.Value = reinterpret_cast<uintptr_t>(V) | N;
106 }
107
108 /// A typed constructor for a specific tagged member of the sum type.
109 template <TagT N>
110 static PointerSumType
111 create(typename HelperT::template Lookup<N>::PointerT Pointer) {
112 PointerSumType Result;
113 Result.set<N>(Pointer);
114 return Result;
115 }
116
117 /// Clear the value to null with the min tag type.
118 void clear() { set<HelperT::MinTag>(nullptr); }
119
120 TagT getTag() const {
121 return static_cast<TagT>(getOpaqueValue() & HelperT::TagMask);
122 }
123
124 template <TagT N> bool is() const { return N == getTag(); }
125
126 template <TagT N> typename HelperT::template Lookup<N>::PointerT get() const {
127 void *P = is<N>() ? getVoidPtr() : nullptr;
128 return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(P);
129 }
130
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 /// 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 *
143 return const_cast<PointerSumType *>(this)->getAddrOfZeroTagPointer();
144 }
145
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 *
151 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 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 return &Storage.MinTagPointer;
165 }
166
167 explicit operator bool() const {
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 bool operator<=(const PointerSumType &R) const {
183 return getOpaqueValue() <= R.getOpaqueValue();
184 }
185 bool operator>=(const PointerSumType &R) const {
186 return getOpaqueValue() >= R.getOpaqueValue();
187 }
188
189 uintptr_t getOpaqueValue() const {
190 // Read the underlying storage of the union, regardless of the active
191 // member.
192 return bit_cast<uintptr_t>(Storage);
193 }
194
195protected:
196 void *getVoidPtr() const {
197 return reinterpret_cast<void *>(getOpaqueValue() & HelperT::PointerMask);
198 }
199};
200
201namespace 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..
206template <typename TagT, typename... MemberTs>
207struct 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>
216 template <TagT N> static void LookupOverload(...);
217 template <TagT N> struct Lookup {
218 // Compute a particular member type by resolving the lookup helper overload.
219 using MemberT = decltype(
220 LookupOverload<N>(static_cast<PointerSumTypeHelper *>(nullptr)));
221
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.
231 static constexpr int NumTagBits =
232 std::min({MemberTs::TraitsT::NumLowBitsAvailable...});
233
234 // Also compute the smallest discriminant and various masks for convenience.
235 constexpr static TagT MinTag =
236 static_cast<TagT>(std::min({static_cast<TagT>(MemberTs::Tag)...}));
237 enum : uint64_t {
238 PointerMask = static_cast<uint64_t>(-1) << NumTagBits,
240 };
241
242 // Finally we need a recursive template to do static checks of each
243 // member.
244 template <typename MemberT, typename... InnerMemberTs>
245 struct Checker : Checker<InnerMemberTs...> {
246 static_assert(MemberT::Tag < (1 << NumTagBits),
247 "This discriminant value requires too many bits!");
248 };
249 template <typename MemberT> struct Checker<MemberT> : std::true_type {
250 static_assert(MemberT::Tag < (1 << NumTagBits),
251 "This discriminant value requires too many bits!");
252 };
253 static_assert(Checker<MemberTs...>::value,
254 "Each member must pass the checker.");
255};
256
257} // end namespace detail
258
259// Teach DenseMap how to use PointerSumTypes as keys.
260template <typename TagT, typename... MemberTs>
261struct DenseMapInfo<PointerSumType<TagT, MemberTs...>> {
262 using SumType = PointerSumType<TagT, MemberTs...>;
263 using HelperT = detail::PointerSumTypeHelper<TagT, MemberTs...>;
266 typename HelperT::template Lookup<HelperT::MinTag>::PointerT;
268
269 static inline SumType getEmptyKey() {
270 return SumType::template create<SomeTag>(SomePointerInfo::getEmptyKey());
271 }
272
273 static inline SumType getTombstoneKey() {
274 return SumType::template create<SomeTag>(
275 SomePointerInfo::getTombstoneKey());
276 }
277
278 static unsigned getHashValue(const SumType &Arg) {
279 uintptr_t OpaqueValue = Arg.getOpaqueValue();
280 return DenseMapInfo<uintptr_t>::getHashValue(OpaqueValue);
281 }
282
283 static bool isEqual(const SumType &LHS, const SumType &RHS) {
284 return LHS == RHS;
285 }
286};
287
288} // end namespace llvm
289
290#endif // LLVM_ADT_POINTERSUMTYPE_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file defines DenseMapInfo traits for DenseMap.
#define P(N)
static int Lookup(ArrayRef< TableEntry > Table, unsigned Opcode)
Value * RHS
Value * LHS
This file implements the C++20 <bit> header.
A sum type over pointer-like types.
bool operator<=(const PointerSumType &R) const
HelperT::template Lookup< N >::PointerT cast() const
bool operator==(const PointerSumType &R) const
bool operator>=(const PointerSumType &R) const
static PointerSumType create(typename HelperT::template Lookup< N >::PointerT Pointer)
A typed constructor for a specific tagged member of the sum type.
bool operator!=(const PointerSumType &R) const
HelperT::template Lookup< HelperT::MinTag >::PointerT * getAddrOfZeroTagPointer()
If the tag is zero and the pointer's value isn't changed when being stored, get the address of the st...
void clear()
Clear the value to null with the min tag type.
HelperT::template Lookup< HelperT::MinTag >::PointerT const * getAddrOfZeroTagPointer() const
If the tag is zero and the pointer's value isn't changed when being stored, get the address of the st...
uintptr_t getOpaqueValue() const
HelperT::template Lookup< N >::PointerT get() const
bool operator>(const PointerSumType &R) const
bool operator<(const PointerSumType &R) const
constexpr PointerSumType()=default
void * getVoidPtr() const
void set(typename HelperT::template Lookup< N >::PointerT Pointer)
A typed setter to a given tagged member of the sum type.
LLVM Value Representation.
Definition Value.h:75
const char unit< Period >::value[]
Definition Chrono.h:104
This is an optimization pass for GlobalISel generic memory operations.
FunctionAddr VTableAddr Value
Definition InstrProf.h:137
To bit_cast(const From &from) noexcept
Definition bit.h:90
#define N
detail::PointerSumTypeHelper< TagT, MemberTs... > HelperT
typename HelperT::template Lookup< HelperT::MinTag >::PointerT SomePointerT
static bool isEqual(const SumType &LHS, const SumType &RHS)
An information struct used to provide DenseMap with the various necessary components for a given valu...
A traits type that is used to handle pointer types and things that are just wrappers for pointers as ...
A compile time pair of an integer tag and the pointer-like type which it indexes within a sum type.
typename MemberT::PointerT PointerT
The Nth member's pointer type.
decltype( LookupOverload< N >(static_cast< PointerSumTypeHelper * >(nullptr))) MemberT
typename MemberT::TraitsT TraitsT
The Nth member's traits type.
A helper template for implementing PointerSumType.
static PointerSumTypeMember< N, PointerT, TraitsT > LookupOverload(PointerSumTypeMember< N, PointerT, TraitsT > *)