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