// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_POINTER_WITH_PAYLOAD_H_ #define V8_POINTER_WITH_PAYLOAD_H_ #include #include #include "include/v8config.h" #include "src/base/logging.h" namespace v8 { namespace internal { template struct PointerWithPayloadTraits { static constexpr int value = alignof(PointerType) >= 8 ? 3 : alignof(PointerType) >= 4 ? 2 : 1; }; // PointerWithPayload combines a PointerType* an a small PayloadType into // one. The bits of the storage type get packed into the lower bits of the // pointer that are free due to alignment. The user needs to specify how many // bits are needed to store the PayloadType, allowing Types that by default are // larger to be stored. // // Example: // PointerWithPayload data_and_flag; // // Here we store a bool that needs 1 bit of storage state into the lower bits // of int *, which points to some int data; template class PointerWithPayload { // We have log2(ptr alignment) kAvailBits free to use static constexpr int kAvailBits = PointerWithPayloadTraits< typename std::remove_const::type>::value; static_assert( kAvailBits >= NumPayloadBits, "Ptr does not have sufficient alignment for the selected amount of " "storage bits."); static constexpr uintptr_t kPayloadMask = (uintptr_t{1} << kAvailBits) - 1; static constexpr uintptr_t kPointerMask = ~kPayloadMask; public: PointerWithPayload() {} explicit PointerWithPayload(PointerType* pointer) : pointer_(reinterpret_cast(pointer)) { DCHECK_EQ(GetPointer(), pointer); DCHECK_EQ(GetPayload(), static_cast(0)); } explicit PointerWithPayload(PayloadType payload) : pointer_(static_cast(payload)) { DCHECK_EQ(GetPointer(), nullptr); DCHECK_EQ(GetPayload(), payload); } PointerWithPayload(PointerType* pointer, PayloadType payload) { update(pointer, payload); } V8_INLINE PointerType* GetPointer() const { return reinterpret_cast(pointer_ & kPointerMask); } V8_INLINE PointerType* operator->() const { return GetPointer(); } V8_INLINE void update(PointerType* new_pointer, PayloadType new_payload) { pointer_ = reinterpret_cast(new_pointer) | static_cast(new_payload); DCHECK_EQ(GetPayload(), new_payload); DCHECK_EQ(GetPointer(), new_pointer); } V8_INLINE void SetPointer(PointerType* newptr) { DCHECK_EQ(reinterpret_cast(newptr) & kPayloadMask, 0); pointer_ = reinterpret_cast(newptr) | (pointer_ & kPayloadMask); DCHECK_EQ(GetPointer(), newptr); } V8_INLINE PayloadType GetPayload() const { return static_cast(pointer_ & kPayloadMask); } V8_INLINE void SetPayload(PayloadType new_payload) { uintptr_t new_payload_ptr = static_cast(new_payload); DCHECK_EQ(new_payload_ptr & kPayloadMask, new_payload_ptr); pointer_ = (pointer_ & kPointerMask) | new_payload_ptr; DCHECK_EQ(GetPayload(), new_payload); } private: uintptr_t pointer_ = 0; }; } // namespace internal } // namespace v8 #endif // V8_POINTER_WITH_PAYLOAD_H_