Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 1 | //===- llvm/ADT/PointerUnion.h - Discriminated Union of 2 Ptrs --*- C++ -*-===// |
| 2 | // |
Andrew Walbran | 16937d0 | 2019-10-22 13:54:20 +0100 | [diff] [blame] | 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 |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file defines the PointerUnion class, which is a discriminated union of |
| 10 | // pointer types. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #ifndef LLVM_ADT_POINTERUNION_H |
| 15 | #define LLVM_ADT_POINTERUNION_H |
| 16 | |
| 17 | #include "llvm/ADT/DenseMapInfo.h" |
| 18 | #include "llvm/ADT/PointerIntPair.h" |
| 19 | #include "llvm/Support/PointerLikeTypeTraits.h" |
| 20 | #include <cassert> |
| 21 | #include <cstddef> |
| 22 | #include <cstdint> |
| 23 | |
| 24 | namespace llvm { |
| 25 | |
| 26 | template <typename T> struct PointerUnionTypeSelectorReturn { |
| 27 | using Return = T; |
| 28 | }; |
| 29 | |
| 30 | /// Get a type based on whether two types are the same or not. |
| 31 | /// |
| 32 | /// For: |
| 33 | /// |
| 34 | /// \code |
| 35 | /// using Ret = typename PointerUnionTypeSelector<T1, T2, EQ, NE>::Return; |
| 36 | /// \endcode |
| 37 | /// |
| 38 | /// Ret will be EQ type if T1 is same as T2 or NE type otherwise. |
| 39 | template <typename T1, typename T2, typename RET_EQ, typename RET_NE> |
| 40 | struct PointerUnionTypeSelector { |
| 41 | using Return = typename PointerUnionTypeSelectorReturn<RET_NE>::Return; |
| 42 | }; |
| 43 | |
| 44 | template <typename T, typename RET_EQ, typename RET_NE> |
| 45 | struct PointerUnionTypeSelector<T, T, RET_EQ, RET_NE> { |
| 46 | using Return = typename PointerUnionTypeSelectorReturn<RET_EQ>::Return; |
| 47 | }; |
| 48 | |
| 49 | template <typename T1, typename T2, typename RET_EQ, typename RET_NE> |
| 50 | struct PointerUnionTypeSelectorReturn< |
| 51 | PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>> { |
| 52 | using Return = |
| 53 | typename PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>::Return; |
| 54 | }; |
| 55 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 56 | namespace pointer_union_detail { |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 57 | /// Determine the number of bits required to store integers with values < n. |
| 58 | /// This is ceil(log2(n)). |
| 59 | constexpr int bitsRequired(unsigned n) { |
| 60 | return n > 1 ? 1 + bitsRequired((n + 1) / 2) : 0; |
| 61 | } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 62 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 63 | template <typename... Ts> constexpr int lowBitsAvailable() { |
| 64 | return std::min<int>({PointerLikeTypeTraits<Ts>::NumLowBitsAvailable...}); |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | /// Find the index of a type in a list of types. TypeIndex<T, Us...>::Index |
| 68 | /// is the index of T in Us, or sizeof...(Us) if T does not appear in the |
| 69 | /// list. |
| 70 | template <typename T, typename ...Us> struct TypeIndex; |
| 71 | template <typename T, typename ...Us> struct TypeIndex<T, T, Us...> { |
| 72 | static constexpr int Index = 0; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 73 | }; |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 74 | template <typename T, typename U, typename... Us> |
| 75 | struct TypeIndex<T, U, Us...> { |
| 76 | static constexpr int Index = 1 + TypeIndex<T, Us...>::Index; |
| 77 | }; |
| 78 | template <typename T> struct TypeIndex<T> { |
| 79 | static constexpr int Index = 0; |
| 80 | }; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 81 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 82 | /// Find the first type in a list of types. |
| 83 | template <typename T, typename...> struct GetFirstType { |
| 84 | using type = T; |
| 85 | }; |
| 86 | |
| 87 | /// Provide PointerLikeTypeTraits for void* that is used by PointerUnion |
| 88 | /// for the template arguments. |
| 89 | template <typename ...PTs> class PointerUnionUIntTraits { |
| 90 | public: |
| 91 | static inline void *getAsVoidPointer(void *P) { return P; } |
| 92 | static inline void *getFromVoidPointer(void *P) { return P; } |
| 93 | static constexpr int NumLowBitsAvailable = lowBitsAvailable<PTs...>(); |
| 94 | }; |
| 95 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 96 | template <typename Derived, typename ValTy, int I, typename ...Types> |
| 97 | class PointerUnionMembers; |
| 98 | |
| 99 | template <typename Derived, typename ValTy, int I> |
| 100 | class PointerUnionMembers<Derived, ValTy, I> { |
| 101 | protected: |
| 102 | ValTy Val; |
| 103 | PointerUnionMembers() = default; |
| 104 | PointerUnionMembers(ValTy Val) : Val(Val) {} |
| 105 | |
| 106 | friend struct PointerLikeTypeTraits<Derived>; |
| 107 | }; |
| 108 | |
| 109 | template <typename Derived, typename ValTy, int I, typename Type, |
| 110 | typename ...Types> |
| 111 | class PointerUnionMembers<Derived, ValTy, I, Type, Types...> |
| 112 | : public PointerUnionMembers<Derived, ValTy, I + 1, Types...> { |
| 113 | using Base = PointerUnionMembers<Derived, ValTy, I + 1, Types...>; |
| 114 | public: |
| 115 | using Base::Base; |
| 116 | PointerUnionMembers() = default; |
| 117 | PointerUnionMembers(Type V) |
| 118 | : Base(ValTy(const_cast<void *>( |
| 119 | PointerLikeTypeTraits<Type>::getAsVoidPointer(V)), |
| 120 | I)) {} |
| 121 | |
| 122 | using Base::operator=; |
| 123 | Derived &operator=(Type V) { |
| 124 | this->Val = ValTy( |
| 125 | const_cast<void *>(PointerLikeTypeTraits<Type>::getAsVoidPointer(V)), |
| 126 | I); |
| 127 | return static_cast<Derived &>(*this); |
| 128 | }; |
| 129 | }; |
| 130 | } |
| 131 | |
| 132 | /// A discriminated union of two or more pointer types, with the discriminator |
| 133 | /// in the low bit of the pointer. |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 134 | /// |
| 135 | /// This implementation is extremely efficient in space due to leveraging the |
| 136 | /// low bits of the pointer, while exposing a natural and type-safe API. |
| 137 | /// |
| 138 | /// Common use patterns would be something like this: |
| 139 | /// PointerUnion<int*, float*> P; |
| 140 | /// P = (int*)0; |
| 141 | /// printf("%d %d", P.is<int*>(), P.is<float*>()); // prints "1 0" |
| 142 | /// X = P.get<int*>(); // ok. |
| 143 | /// Y = P.get<float*>(); // runtime assertion failure. |
| 144 | /// Z = P.get<double*>(); // compile time failure. |
| 145 | /// P = (float*)0; |
| 146 | /// Y = P.get<float*>(); // ok. |
| 147 | /// X = P.get<int*>(); // runtime assertion failure. |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 148 | template <typename... PTs> |
| 149 | class PointerUnion |
| 150 | : public pointer_union_detail::PointerUnionMembers< |
| 151 | PointerUnion<PTs...>, |
| 152 | PointerIntPair< |
| 153 | void *, pointer_union_detail::bitsRequired(sizeof...(PTs)), int, |
| 154 | pointer_union_detail::PointerUnionUIntTraits<PTs...>>, |
| 155 | 0, PTs...> { |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 156 | // The first type is special because we want to directly cast a pointer to a |
| 157 | // default-initialized union to a pointer to the first type. But we don't |
| 158 | // want PointerUnion to be a 'template <typename First, typename ...Rest>' |
| 159 | // because it's much more convenient to have a name for the whole pack. So |
| 160 | // split off the first type here. |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 161 | using First = typename pointer_union_detail::GetFirstType<PTs...>::type; |
| 162 | using Base = typename PointerUnion::PointerUnionMembers; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 163 | |
| 164 | public: |
| 165 | PointerUnion() = default; |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 166 | |
| 167 | PointerUnion(std::nullptr_t) : PointerUnion() {} |
| 168 | using Base::Base; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 169 | |
| 170 | /// Test if the pointer held in the union is null, regardless of |
| 171 | /// which type it is. |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 172 | bool isNull() const { return !this->Val.getPointer(); } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 173 | |
| 174 | explicit operator bool() const { return !isNull(); } |
| 175 | |
| 176 | /// Test if the Union currently holds the type matching T. |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 177 | template <typename T> bool is() const { |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 178 | constexpr int Index = pointer_union_detail::TypeIndex<T, PTs...>::Index; |
| 179 | static_assert(Index < sizeof...(PTs), |
| 180 | "PointerUnion::is<T> given type not in the union"); |
| 181 | return this->Val.getInt() == Index; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 182 | } |
| 183 | |
| 184 | /// Returns the value of the specified pointer type. |
| 185 | /// |
| 186 | /// If the specified pointer type is incorrect, assert. |
| 187 | template <typename T> T get() const { |
| 188 | assert(is<T>() && "Invalid accessor called"); |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 189 | return PointerLikeTypeTraits<T>::getFromVoidPointer(this->Val.getPointer()); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | /// Returns the current pointer if it is of the specified pointer type, |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 193 | /// otherwise returns null. |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 194 | template <typename T> T dyn_cast() const { |
| 195 | if (is<T>()) |
| 196 | return get<T>(); |
| 197 | return T(); |
| 198 | } |
| 199 | |
| 200 | /// If the union is set to the first pointer type get an address pointing to |
| 201 | /// it. |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 202 | First const *getAddrOfPtr1() const { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 203 | return const_cast<PointerUnion *>(this)->getAddrOfPtr1(); |
| 204 | } |
| 205 | |
| 206 | /// If the union is set to the first pointer type get an address pointing to |
| 207 | /// it. |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 208 | First *getAddrOfPtr1() { |
| 209 | assert(is<First>() && "Val is not the first pointer"); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 210 | assert( |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 211 | PointerLikeTypeTraits<First>::getAsVoidPointer(get<First>()) == |
| 212 | this->Val.getPointer() && |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 213 | "Can't get the address because PointerLikeTypeTraits changes the ptr"); |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 214 | return const_cast<First *>( |
| 215 | reinterpret_cast<const First *>(this->Val.getAddrOfPointer())); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | /// Assignment from nullptr which just clears the union. |
| 219 | const PointerUnion &operator=(std::nullptr_t) { |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 220 | this->Val.initWithPointer(nullptr); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 221 | return *this; |
| 222 | } |
| 223 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 224 | /// Assignment from elements of the union. |
| 225 | using Base::operator=; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 226 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 227 | void *getOpaqueValue() const { return this->Val.getOpaqueValue(); } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 228 | static inline PointerUnion getFromOpaqueValue(void *VP) { |
| 229 | PointerUnion V; |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 230 | V.Val = decltype(V.Val)::getFromOpaqueValue(VP); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 231 | return V; |
| 232 | } |
| 233 | }; |
| 234 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 235 | template <typename ...PTs> |
| 236 | bool operator==(PointerUnion<PTs...> lhs, PointerUnion<PTs...> rhs) { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 237 | return lhs.getOpaqueValue() == rhs.getOpaqueValue(); |
| 238 | } |
| 239 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 240 | template <typename ...PTs> |
| 241 | bool operator!=(PointerUnion<PTs...> lhs, PointerUnion<PTs...> rhs) { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 242 | return lhs.getOpaqueValue() != rhs.getOpaqueValue(); |
| 243 | } |
| 244 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 245 | template <typename ...PTs> |
| 246 | bool operator<(PointerUnion<PTs...> lhs, PointerUnion<PTs...> rhs) { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 247 | return lhs.getOpaqueValue() < rhs.getOpaqueValue(); |
| 248 | } |
| 249 | |
| 250 | // Teach SmallPtrSet that PointerUnion is "basically a pointer", that has |
| 251 | // # low bits available = min(PT1bits,PT2bits)-1. |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 252 | template <typename ...PTs> |
| 253 | struct PointerLikeTypeTraits<PointerUnion<PTs...>> { |
| 254 | static inline void *getAsVoidPointer(const PointerUnion<PTs...> &P) { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 255 | return P.getOpaqueValue(); |
| 256 | } |
| 257 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 258 | static inline PointerUnion<PTs...> getFromVoidPointer(void *P) { |
| 259 | return PointerUnion<PTs...>::getFromOpaqueValue(P); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 260 | } |
| 261 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 262 | // The number of bits available are the min of the pointer types minus the |
| 263 | // bits needed for the discriminator. |
| 264 | static constexpr int NumLowBitsAvailable = PointerLikeTypeTraits<decltype( |
| 265 | PointerUnion<PTs...>::Val)>::NumLowBitsAvailable; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 266 | }; |
| 267 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 268 | // Teach DenseMap how to use PointerUnions as keys. |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 269 | template <typename ...PTs> struct DenseMapInfo<PointerUnion<PTs...>> { |
| 270 | using Union = PointerUnion<PTs...>; |
| 271 | using FirstInfo = |
| 272 | DenseMapInfo<typename pointer_union_detail::GetFirstType<PTs...>::type>; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 273 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 274 | static inline Union getEmptyKey() { return Union(FirstInfo::getEmptyKey()); } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 275 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 276 | static inline Union getTombstoneKey() { |
| 277 | return Union(FirstInfo::getTombstoneKey()); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 278 | } |
| 279 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 280 | static unsigned getHashValue(const Union &UnionVal) { |
| 281 | intptr_t key = (intptr_t)UnionVal.getOpaqueValue(); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 282 | return DenseMapInfo<intptr_t>::getHashValue(key); |
| 283 | } |
| 284 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 285 | static bool isEqual(const Union &LHS, const Union &RHS) { |
| 286 | return LHS == RHS; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 287 | } |
| 288 | }; |
| 289 | |
| 290 | } // end namespace llvm |
| 291 | |
| 292 | #endif // LLVM_ADT_POINTERUNION_H |