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