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 13namespace v8 { 14namespace base { 15 16template <typename PointerType> 17struct 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. 24template <> 25struct 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; 39template <typename PointerType, typename PayloadType, int NumPayloadBits> 40class 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