• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_BASE_POINTER_WITH_PAYLOAD_H_
6 #define V8_BASE_POINTER_WITH_PAYLOAD_H_
7 
8 #include <cstdint>
9 #include <type_traits>
10 
11 #include "src/base/logging.h"
12 
13 namespace v8 {
14 namespace base {
15 
16 template <typename PointerType>
17 struct PointerWithPayloadTraits {
18   static constexpr int kAvailableBits =
19       alignof(PointerType) >= 8 ? 3 : alignof(PointerType) >= 4 ? 2 : 1;
20 };
21 
22 // Assume void* has the same payloads as void**, under the assumption that it's
23 // used for classes that contain at least one pointer.
24 template <>
25 struct PointerWithPayloadTraits<void> : public PointerWithPayloadTraits<void*> {
26 };
27 
28 // PointerWithPayload combines a PointerType* an a small PayloadType into
29 // one. The bits of the storage type get packed into the lower bits of the
30 // pointer that are free due to alignment. The user needs to specify how many
31 // bits are needed to store the PayloadType, allowing Types that by default are
32 // larger to be stored.
33 //
34 // Example:
35 //   PointerWithPayload<int *, bool, 1> data_and_flag;
36 //
37 //   Here we store a bool that needs 1 bit of storage state into the lower bits
38 //   of int *, which points to some int data;
39 template <typename PointerType, typename PayloadType, int NumPayloadBits>
40 class PointerWithPayload {
41  public:
42   PointerWithPayload() = default;
43 
44   explicit PointerWithPayload(PointerType* pointer)
45       : pointer_with_payload_(reinterpret_cast<uintptr_t>(pointer)) {
46     DCHECK_EQ(GetPointer(), pointer);
47     DCHECK_EQ(GetPayload(), static_cast<PayloadType>(0));
48   }
49 
50   explicit PointerWithPayload(PayloadType payload)
51       : pointer_with_payload_(static_cast<uintptr_t>(payload)) {
52     DCHECK_EQ(GetPointer(), nullptr);
53     DCHECK_EQ(GetPayload(), payload);
54   }
55 
56   PointerWithPayload(PointerType* pointer, PayloadType payload) {
57     Update(pointer, payload);
58   }
59 
60   V8_INLINE PointerType* GetPointer() const {
61     return reinterpret_cast<PointerType*>(pointer_with_payload_ & kPointerMask);
62   }
63 
64   // An optimized version of GetPointer for when we know the payload value.
65   V8_INLINE PointerType* GetPointerWithKnownPayload(PayloadType payload) const {
66     DCHECK_EQ(GetPayload(), payload);
67     return reinterpret_cast<PointerType*>(pointer_with_payload_ -
68                                           static_cast<uintptr_t>(payload));
69   }
70 
71   V8_INLINE PointerType* operator->() const { return GetPointer(); }
72 
73   V8_INLINE void Update(PointerType* new_pointer, PayloadType new_payload) {
74     pointer_with_payload_ = reinterpret_cast<uintptr_t>(new_pointer) |
75                             static_cast<uintptr_t>(new_payload);
76     DCHECK_EQ(GetPayload(), new_payload);
77     DCHECK_EQ(GetPointer(), new_pointer);
78   }
79 
80   V8_INLINE void SetPointer(PointerType* newptr) {
81     DCHECK_EQ(reinterpret_cast<uintptr_t>(newptr) & kPayloadMask, 0);
82     pointer_with_payload_ = reinterpret_cast<uintptr_t>(newptr) |
83                             (pointer_with_payload_ & kPayloadMask);
84     DCHECK_EQ(GetPointer(), newptr);
85   }
86 
87   V8_INLINE PayloadType GetPayload() const {
88     return static_cast<PayloadType>(pointer_with_payload_ & kPayloadMask);
89   }
90 
91   V8_INLINE void SetPayload(PayloadType new_payload) {
92     uintptr_t new_payload_ptr = static_cast<uintptr_t>(new_payload);
93     DCHECK_EQ(new_payload_ptr & kPayloadMask, new_payload_ptr);
94     pointer_with_payload_ =
95         (pointer_with_payload_ & kPointerMask) | new_payload_ptr;
96     DCHECK_EQ(GetPayload(), new_payload);
97   }
98 
99  private:
100   static constexpr int kAvailableBits = PointerWithPayloadTraits<
101       typename std::remove_const<PointerType>::type>::kAvailableBits;
102   static_assert(
103       kAvailableBits >= NumPayloadBits,
104       "Ptr does not have sufficient alignment for the selected amount of "
105       "storage bits. Override PointerWithPayloadTraits to guarantee available "
106       "bits manually.");
107 
108   static constexpr uintptr_t kPayloadMask =
109       (uintptr_t{1} << NumPayloadBits) - 1;
110   static constexpr uintptr_t kPointerMask = ~kPayloadMask;
111 
112   uintptr_t pointer_with_payload_ = 0;
113 };
114 
115 }  // namespace base
116 }  // namespace v8
117 
118 #endif  // V8_BASE_POINTER_WITH_PAYLOAD_H_
119