llvm.org GIT mirror llvm / 8a9a016
Convert PointerUnion to a variadic template. Summary: Rather than duplicating code between PointerUnion, PointerUnion3, and PointerUnion4 (and missing things from the latter cases, such as some of the DenseMap support and operator==), convert PointerUnion to a variadic template that can be used as a union of any number of pointers. (This doesn't support PointerUnion<> right now. Adding a special case for that would be possible, and perhaps even useful in some situations, but it doesn't seem worthwhile until we have a concrete use case.) Reviewers: dblaikie Subscribers: dexonsmith, kristina, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D62027 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@360962 91177308-0d34-0410-b5e6-96231b3b80d8 Richard Smith 3 months ago
2 changed file(s) with 199 addition(s) and 333 deletion(s). Raw diff Collapse all Expand all
5252 typename PointerUnionTypeSelector::Return;
5353 };
5454
55 /// Provide PointerLikeTypeTraits for void* that is used by PointerUnion
56 /// for the two template arguments.
57 template class PointerUnionUIntTraits {
58 public:
59 static inline void *getAsVoidPointer(void *P) { return P; }
60 static inline void *getFromVoidPointer(void *P) { return P; }
61
62 enum {
63 PT1BitsAv = (int)(PointerLikeTypeTraits::NumLowBitsAvailable),
64 PT2BitsAv = (int)(PointerLikeTypeTraits::NumLowBitsAvailable),
65 NumLowBitsAvailable = PT1BitsAv < PT2BitsAv ? PT1BitsAv : PT2BitsAv
66 };
67 };
68
69 /// A discriminated union of two pointer types, with the discriminator in the
70 /// low bit of the pointer.
55 namespace pointer_union_detail {
56 constexpr int constexprMin(int a, int b) { return a < b ? a : b; }
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 }
62
63 // FIXME: In C++14, replace this with
64 // std::min({PointerLikeTypeTraits::NumLowBitsAvailable...})
65 template constexpr int lowBitsAvailable() {
66 return PointerLikeTypeTraits::NumLowBitsAvailable;
67 }
68 template
69 constexpr int lowBitsAvailable() {
70 return constexprMin(lowBitsAvailable(), lowBitsAvailable());
71 }
72
73 /// Find the index of a type in a list of types. TypeIndex::Index
74 /// is the index of T in Us, or sizeof...(Us) if T does not appear in the
75 /// list.
76 template struct TypeIndex;
77 template struct TypeIndex {
78 static constexpr int Index = 0;
79 };
80 template
81 struct TypeIndex {
82 static constexpr int Index = 1 + TypeIndex::Index;
83 };
84 template struct TypeIndex {
85 static constexpr int Index = 0;
86 };
87
88 /// Find the first type in a list of types.
89 template struct GetFirstType {
90 using type = T;
91 };
92
93 /// Provide PointerLikeTypeTraits for void* that is used by PointerUnion
94 /// for the template arguments.
95 template class PointerUnionUIntTraits {
96 public:
97 static inline void *getAsVoidPointer(void *P) { return P; }
98 static inline void *getFromVoidPointer(void *P) { return P; }
99 static constexpr int NumLowBitsAvailable = lowBitsAvailable();
100 };
101
102 /// Implement assigment in terms of construction.
103 template struct AssignableFrom {
104 Derived &operator=(T t) {
105 return static_cast(*this) = Derived(t);
106 }
107 };
108
109 template
110 class PointerUnionMembers;
111
112 template
113 class PointerUnionMembers {
114 protected:
115 ValTy Val;
116 PointerUnionMembers() = default;
117 PointerUnionMembers(ValTy Val) : Val(Val) {}
118
119 friend struct PointerLikeTypeTraits;
120 };
121
122 template
123 typename ...Types>
124 class PointerUnionMembers
125 : public PointerUnionMembers {
126 using Base = PointerUnionMembers;
127 public:
128 using Base::Base;
129 PointerUnionMembers() = default;
130 PointerUnionMembers(Type V)
131 : Base(ValTy(const_cast(
132 PointerLikeTypeTraits::getAsVoidPointer(V)),
133 I)) {}
134
135 using Base::operator=;
136 Derived &operator=(Type V) {
137 this->Val = ValTy(
138 const_cast(PointerLikeTypeTraits::getAsVoidPointer(V)),
139 I);
140 return static_cast(*this);
141 };
142 };
143 }
144
145 /// A discriminated union of two or more pointer types, with the discriminator
146 /// in the low bit of the pointer.
71147 ///
72148 /// This implementation is extremely efficient in space due to leveraging the
73149 /// low bits of the pointer, while exposing a natural and type-safe API.
82158 /// P = (float*)0;
83159 /// Y = P.get(); // ok.
84160 /// X = P.get(); // runtime assertion failure.
85 template class PointerUnion {
86 public:
87 using ValTy =
88 PointerIntPair>;
89
90 private:
91 ValTy Val;
92
93 struct IsPT1 {
94 static const int Num = 0;
95 };
96 struct IsPT2 {
97 static const int Num = 1;
98 };
99 template struct UNION_DOESNT_CONTAIN_TYPE {};
161 template
162 class PointerUnion
163 : public pointer_union_detail::PointerUnionMembers<
164 PointerUnion,
165 PointerIntPair<
166 void *, pointer_union_detail::bitsRequired(sizeof...(PTs)), int,
167 pointer_union_detail::PointerUnionUIntTraits>,
168 0, PTs...> {
169 // The first type is special in some ways, but we don't want PointerUnion to
170 // be a 'template ' because it's much more
171 // convenient to have a name for the whole pack. So split off the first type
172 // here.
173 using First = typename pointer_union_detail::GetFirstType::type;
174 using Base = typename PointerUnion::PointerUnionMembers;
100175
101176 public:
102177 PointerUnion() = default;
103 PointerUnion(PT1 V)
104 : Val(const_cast(
105 PointerLikeTypeTraits::getAsVoidPointer(V))) {}
106 PointerUnion(PT2 V)
107 : Val(const_cast(PointerLikeTypeTraits::getAsVoidPointer(V)),
108 1) {}
178
179 PointerUnion(std::nullptr_t) : PointerUnion() {}
180 using Base::Base;
109181
110182 /// Test if the pointer held in the union is null, regardless of
111183 /// which type it is.
112184 bool isNull() const {
113185 // Convert from the void* to one of the pointer types, to make sure that
114186 // we recursively strip off low bits if we have a nested PointerUnion.
115 return !PointerLikeTypeTraits<PT1>::getFromVoidPointer(Val.getPointer());
187 return !PointerLikeTypeTraits<First>::getFromVoidPointer(
188 this->Val.getPointer());
116189 }
117190
118191 explicit operator bool() const { return !isNull(); }
119192
120193 /// Test if the Union currently holds the type matching T.
121194 template int is() const {
122 using Ty = typename ::llvm::PointerUnionTypeSelector<
123 PT1, T, IsPT1,
124 ::llvm::PointerUnionTypeSelector
125 UNION_DOESNT_CONTAIN_TYPE>>::Return;
126 int TyNo = Ty::Num;
127 return static_cast(Val.getInt()) == TyNo;
195 constexpr int Index = pointer_union_detail::TypeIndex::Index;
196 static_assert(Index < sizeof...(PTs),
197 "PointerUnion::is given type not in the union");
198 return this->Val.getInt() == Index;
128199 }
129200
130201 /// Returns the value of the specified pointer type.
132203 /// If the specified pointer type is incorrect, assert.
133204 template T get() const {
134205 assert(is() && "Invalid accessor called");
135 return PointerLikeTypeTraits::getFromVoidPointer(Val.getPointer());
206 return PointerLikeTypeTraits::getFromVoidPointer(this->Val.getPointer());
136207 }
137208
138209 /// Returns the current pointer if it is of the specified pointer type,
145216
146217 /// If the union is set to the first pointer type get an address pointing to
147218 /// it.
148 PT1 const *getAddrOfPtr1() const {
219 First const *getAddrOfPtr1() const {
149220 return const_cast(this)->getAddrOfPtr1();
150221 }
151222
152223 /// If the union is set to the first pointer type get an address pointing to
153224 /// it.
154 PT1 *getAddrOfPtr1() {
155 assert(is() && "Val is not the first pointer");
225 First *getAddrOfPtr1() {
226 assert(is() && "Val is not the first pointer");
156227 assert(
157 get<PT1>() == Val.getPointer() &&
228 get<First>() == this->Val.getPointer() &&
158229 "Can't get the address because PointerLikeTypeTraits changes the ptr");
159 return const_cast(
160 reinterpret_cast(Val.getAddrOfPointer()));
230 return const_cast(
231 reinterpret_cast(this->Val.getAddrOfPointer()));
161232 }
162233
163234 /// Assignment from nullptr which just clears the union.
164235 const PointerUnion &operator=(std::nullptr_t) {
165 Val.initWithPointer(nullptr);
236 this->Val.initWithPointer(nullptr);
166237 return *this;
167238 }
168239
169 /// Assignment operators - Allow assigning into this union from either
170 /// pointer type, setting the discriminator to remember what it came from.
171 const PointerUnion &operator=(const PT1 &RHS) {
172 Val.initWithPointer(
173 const_cast(PointerLikeTypeTraits::getAsVoidPointer(RHS)));
174 return *this;
175 }
176 const PointerUnion &operator=(const PT2 &RHS) {
177 Val.setPointerAndInt(
178 const_cast(PointerLikeTypeTraits::getAsVoidPointer(RHS)),
179 1);
180 return *this;
181 }
182
183 void *getOpaqueValue() const { return Val.getOpaqueValue(); }
240 /// Assignment from elements of the union.
241 using Base::operator=;
242
243 void *getOpaqueValue() const { return this->Val.getOpaqueValue(); }
184244 static inline PointerUnion getFromOpaqueValue(void *VP) {
185245 PointerUnion V;
186 V.Val = ValTy::getFromOpaqueValue(VP);
246 V.Val = decltype(V.Val)::getFromOpaqueValue(VP);
187247 return V;
188248 }
189249 };
190250
191 template
192 bool operator==(PointerUnion lhs, PointerUnion rhs) {
251 template
252 bool operator==(PointerUnion lhs, PointerUnion rhs) {
193253 return lhs.getOpaqueValue() == rhs.getOpaqueValue();
194254 }
195255
196 template
197 bool operator!=(PointerUnion lhs, PointerUnion rhs) {
256 template
257 bool operator!=(PointerUnion lhs, PointerUnion rhs) {
198258 return lhs.getOpaqueValue() != rhs.getOpaqueValue();
199259 }
200260
201 template
202 bool operator<(PointerUnion lhs, PointerUnion rhs) {
261 template
262 bool operator<(PointerUnion lhs, PointerUnion rhs) {
203263 return lhs.getOpaqueValue() < rhs.getOpaqueValue();
204264 }
205265
206266 // Teach SmallPtrSet that PointerUnion is "basically a pointer", that has
207267 // # low bits available = min(PT1bits,PT2bits)-1.
208 template
209 struct PointerLikeTypeTraits> {
210 static inline void *getAsVoidPointer(const PointerUnion &P) {
268 template
269 struct PointerLikeTypeTraits> {
270 static inline void *getAsVoidPointer(const PointerUnion &P) {
211271 return P.getOpaqueValue();
212272 }
213273
214 static inline PointerUnion getFromVoidPointer(void *P) {
215 return PointerUnion::getFromOpaqueValue(P);
216 }
217
218 // The number of bits available are the min of the two pointer types.
219 enum {
220 NumLowBitsAvailable = PointerLikeTypeTraits<
221 typename PointerUnion::ValTy>::NumLowBitsAvailable
222 };
274 static inline PointerUnion getFromVoidPointer(void *P) {
275 return PointerUnion::getFromOpaqueValue(P);
276 }
277
278 // The number of bits available are the min of the pointer types minus the
279 // bits needed for the discriminator.
280 static constexpr int NumLowBitsAvailable = PointerLikeTypeTraits
281 PointerUnion::Val)>::NumLowBitsAvailable;
223282 };
224283
225284 /// A pointer union of three pointer types. See documentation for PointerUnion
226285 /// for usage.
227 template class PointerUnion3 {
228 public:
229 using InnerUnion = PointerUnion;
230 using ValTy = PointerUnion;
231
232 private:
233 ValTy Val;
234
235 struct IsInnerUnion {
236 ValTy Val;
237
238 IsInnerUnion(ValTy val) : Val(val) {}
239
240 template int is() const {
241 return Val.template is() &&
242 Val.template get().template is();
243 }
244
245 template T get() const {
246 return Val.template get().template get();
247 }
248 };
249
250 struct IsPT3 {
251 ValTy Val;
252
253 IsPT3(ValTy val) : Val(val) {}
254
255 template int is() const { return Val.template is(); }
256 template T get() const { return Val.template get(); }
257 };
258
259 public:
260 PointerUnion3() = default;
261 PointerUnion3(PT1 V) { Val = InnerUnion(V); }
262 PointerUnion3(PT2 V) { Val = InnerUnion(V); }
263 PointerUnion3(PT3 V) { Val = V; }
264
265 /// Test if the pointer held in the union is null, regardless of
266 /// which type it is.
267 bool isNull() const { return Val.isNull(); }
268 explicit operator bool() const { return !isNull(); }
269
270 /// Test if the Union currently holds the type matching T.
271 template int is() const {
272 // If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3.
273 using Ty = typename ::llvm::PointerUnionTypeSelector<
274 PT1, T, IsInnerUnion,
275 ::llvm::PointerUnionTypeSelector>::Return;
276 return Ty(Val).template is();
277 }
278
279 /// Returns the value of the specified pointer type.
280 ///
281 /// If the specified pointer type is incorrect, assert.
282 template T get() const {
283 assert(is() && "Invalid accessor called");
284 // If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3.
285 using Ty = typename ::llvm::PointerUnionTypeSelector<
286 PT1, T, IsInnerUnion,
287 ::llvm::PointerUnionTypeSelector>::Return;
288 return Ty(Val).template get();
289 }
290
291 /// Returns the current pointer if it is of the specified pointer type,
292 /// otherwises returns null.
293 template T dyn_cast() const {
294 if (is())
295 return get();
296 return T();
297 }
298
299 /// Assignment from nullptr which just clears the union.
300 const PointerUnion3 &operator=(std::nullptr_t) {
301 Val = nullptr;
302 return *this;
303 }
304
305 /// Assignment operators - Allow assigning into this union from either
306 /// pointer type, setting the discriminator to remember what it came from.
307 const PointerUnion3 &operator=(const PT1 &RHS) {
308 Val = InnerUnion(RHS);
309 return *this;
310 }
311 const PointerUnion3 &operator=(const PT2 &RHS) {
312 Val = InnerUnion(RHS);
313 return *this;
314 }
315 const PointerUnion3 &operator=(const PT3 &RHS) {
316 Val = RHS;
317 return *this;
318 }
319
320 void *getOpaqueValue() const { return Val.getOpaqueValue(); }
321 static inline PointerUnion3 getFromOpaqueValue(void *VP) {
322 PointerUnion3 V;
323 V.Val = ValTy::getFromOpaqueValue(VP);
324 return V;
325 }
326 };
327
328 // Teach SmallPtrSet that PointerUnion3 is "basically a pointer", that has
329 // # low bits available = min(PT1bits,PT2bits,PT2bits)-2.
330286 template
331 struct PointerLikeTypeTraits> {
332 static inline void *getAsVoidPointer(const PointerUnion3 &P) {
333 return P.getOpaqueValue();
334 }
335
336 static inline PointerUnion3 getFromVoidPointer(void *P) {
337 return PointerUnion3::getFromOpaqueValue(P);
338 }
339
340 // The number of bits available are the min of the two pointer types.
341 enum {
342 NumLowBitsAvailable = PointerLikeTypeTraits<
343 typename PointerUnion3::ValTy>::NumLowBitsAvailable
344 };
345 };
346
347 template
348 bool operator<(PointerUnion3 lhs,
349 PointerUnion3 rhs) {
350 return lhs.getOpaqueValue() < rhs.getOpaqueValue();
351 }
287 using PointerUnion3 = PointerUnion;
352288
353289 /// A pointer union of four pointer types. See documentation for PointerUnion
354290 /// for usage.
355291 template
356 class PointerUnion4 {
357 public:
358 using InnerUnion1 = PointerUnion;
359 using InnerUnion2 = PointerUnion;
360 using ValTy = PointerUnion;
361
362 private:
363 ValTy Val;
364
365 public:
366 PointerUnion4() = default;
367 PointerUnion4(PT1 V) { Val = InnerUnion1(V); }
368 PointerUnion4(PT2 V) { Val = InnerUnion1(V); }
369 PointerUnion4(PT3 V) { Val = InnerUnion2(V); }
370 PointerUnion4(PT4 V) { Val = InnerUnion2(V); }
371
372 /// Test if the pointer held in the union is null, regardless of
373 /// which type it is.
374 bool isNull() const { return Val.isNull(); }
375 explicit operator bool() const { return !isNull(); }
376
377 /// Test if the Union currently holds the type matching T.
378 template int is() const {
379 // If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2.
380 using Ty = typename ::llvm::PointerUnionTypeSelector<
381 PT1, T, InnerUnion1,
382 ::llvm::PointerUnionTypeSelector
383 InnerUnion2>>::Return;
384 return Val.template is() && Val.template get().template is();
385 }
386
387 /// Returns the value of the specified pointer type.
388 ///
389 /// If the specified pointer type is incorrect, assert.
390 template T get() const {
391 assert(is() && "Invalid accessor called");
392 // If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2.
393 using Ty = typename ::llvm::PointerUnionTypeSelector<
394 PT1, T, InnerUnion1,
395 ::llvm::PointerUnionTypeSelector
396 InnerUnion2>>::Return;
397 return Val.template get().template get();
398 }
399
400 /// Returns the current pointer if it is of the specified pointer type,
401 /// otherwises returns null.
402 template T dyn_cast() const {
403 if (is())
404 return get();
405 return T();
406 }
407
408 /// Assignment from nullptr which just clears the union.
409 const PointerUnion4 &operator=(std::nullptr_t) {
410 Val = nullptr;
411 return *this;
412 }
413
414 /// Assignment operators - Allow assigning into this union from either
415 /// pointer type, setting the discriminator to remember what it came from.
416 const PointerUnion4 &operator=(const PT1 &RHS) {
417 Val = InnerUnion1(RHS);
418 return *this;
419 }
420 const PointerUnion4 &operator=(const PT2 &RHS) {
421 Val = InnerUnion1(RHS);
422 return *this;
423 }
424 const PointerUnion4 &operator=(const PT3 &RHS) {
425 Val = InnerUnion2(RHS);
426 return *this;
427 }
428 const PointerUnion4 &operator=(const PT4 &RHS) {
429 Val = InnerUnion2(RHS);
430 return *this;
431 }
432
433 void *getOpaqueValue() const { return Val.getOpaqueValue(); }
434 static inline PointerUnion4 getFromOpaqueValue(void *VP) {
435 PointerUnion4 V;
436 V.Val = ValTy::getFromOpaqueValue(VP);
437 return V;
438 }
439 };
440
441 // Teach SmallPtrSet that PointerUnion4 is "basically a pointer", that has
442 // # low bits available = min(PT1bits,PT2bits,PT2bits)-2.
443 template
444 struct PointerLikeTypeTraits> {
445 static inline void *
446 getAsVoidPointer(const PointerUnion4 &P) {
447 return P.getOpaqueValue();
448 }
449
450 static inline PointerUnion4 getFromVoidPointer(void *P) {
451 return PointerUnion4::getFromOpaqueValue(P);
452 }
453
454 // The number of bits available are the min of the two pointer types.
455 enum {
456 NumLowBitsAvailable = PointerLikeTypeTraits<
457 typename PointerUnion4::ValTy>::NumLowBitsAvailable
458 };
459 };
292 using PointerUnion4 = PointerUnion;
460293
461294 // Teach DenseMap how to use PointerUnions as keys.
462 template struct DenseMapInfo> {
463 using Pair = PointerUnion;
464 using FirstInfo = DenseMapInfo;
465 using SecondInfo = DenseMapInfo;
466
467 static inline Pair getEmptyKey() { return Pair(FirstInfo::getEmptyKey()); }
468
469 static inline Pair getTombstoneKey() {
470 return Pair(FirstInfo::getTombstoneKey());
471 }
472
473 static unsigned getHashValue(const Pair &PairVal) {
474 intptr_t key = (intptr_t)PairVal.getOpaqueValue();
295 template struct DenseMapInfo> {
296 using Union = PointerUnion;
297 using FirstInfo =
298 DenseMapInfo::type>;
299
300 static inline Union getEmptyKey() { return Union(FirstInfo::getEmptyKey()); }
301
302 static inline Union getTombstoneKey() {
303 return Union(FirstInfo::getTombstoneKey());
304 }
305
306 static unsigned getHashValue(const Union &UnionVal) {
307 intptr_t key = (intptr_t)UnionVal.getOpaqueValue();
475308 return DenseMapInfo::getHashValue(key);
476309 }
477310
478 static bool isEqual(const Pair &LHS, const Pair &RHS) {
479 return LHS.template is() == RHS.template is() &&
480 (LHS.template is() ? FirstInfo::isEqual(LHS.template get(),
481 RHS.template get())
482 : SecondInfo::isEqual(LHS.template get(),
483 RHS.template get()));
311 static bool isEqual(const Union &LHS, const Union &RHS) {
312 return LHS == RHS;
484313 }
485314 };
486315
6767 EXPECT_EQ(n.get(), (int *)nullptr);
6868 }
6969
70 template struct alignas(8) Aligned {};
71
72 typedef PointerUnion *, Aligned<1> *, Aligned<2> *, Aligned<3> *,
73 Aligned<4> *, Aligned<5> *, Aligned<6> *, Aligned<7> *>
74 PU8;
75
76 TEST_F(PointerUnionTest, ManyElements) {
77 Aligned<0> a0;
78 Aligned<7> a7;
79
80 PU8 a = &a0;
81 EXPECT_TRUE(a.is*>());
82 EXPECT_FALSE(a.is*>());
83 EXPECT_FALSE(a.is*>());
84 EXPECT_FALSE(a.is*>());
85 EXPECT_FALSE(a.is*>());
86 EXPECT_FALSE(a.is*>());
87 EXPECT_FALSE(a.is*>());
88 EXPECT_FALSE(a.is*>());
89 EXPECT_EQ(a.dyn_cast*>() == &a0);
90 EXPECT_EQ(*a.getAddrOfPtr1() == &a0);
91
92 a = &a7;
93 EXPECT_FALSE(a.is*>());
94 EXPECT_FALSE(a.is*>());
95 EXPECT_FALSE(a.is*>());
96 EXPECT_FALSE(a.is*>());
97 EXPECT_FALSE(a.is*>());
98 EXPECT_FALSE(a.is*>());
99 EXPECT_FALSE(a.is*>());
100 EXPECT_TRUE(a.is*>());
101 EXPECT_EQ(a.dyn_cast*>() == &a7);
102
103 EXPECT_TRUE(a == PU8(&a7));
104 EXPECT_TRUE(a != PU8(&a0));
105 }
106
70107 } // end anonymous namespace