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_POINTER_WITH_PAYLOAD_H_
6#define V8_POINTER_WITH_PAYLOAD_H_
7
8#include <cstdint>
9#include <type_traits>
10
11#include "include/v8config.h"
12#include "src/base/logging.h"
13
14namespace v8 {
15namespace internal {
16
17template <typename PointerType>
18struct PointerWithPayloadTraits {
19 static constexpr int value =
20 alignof(PointerType) >= 8 ? 3 : alignof(PointerType) >= 4 ? 2 : 1;
21};
22
23// PointerWithPayload combines a PointerType* an a small PayloadType into
24// one. The bits of the storage type get packed into the lower bits of the
25// pointer that are free due to alignment. The user needs to specify how many
26// bits are needed to store the PayloadType, allowing Types that by default are
27// larger to be stored.
28//
29// Example:
30// PointerWithPayload<int *, bool, 1> data_and_flag;
31//
32// Here we store a bool that needs 1 bit of storage state into the lower bits
33// of int *, which points to some int data;
34
35template <typename PointerType, typename PayloadType, int NumPayloadBits>
36class PointerWithPayload {
37 // We have log2(ptr alignment) kAvailBits free to use
38 static constexpr int kAvailBits = PointerWithPayloadTraits<
39 typename std::remove_const<PointerType>::type>::value;
40 static_assert(
41 kAvailBits >= NumPayloadBits,
42 "Ptr does not have sufficient alignment for the selected amount of "
43 "storage bits.");
44
45 static constexpr uintptr_t kPayloadMask = (uintptr_t{1} << kAvailBits) - 1;
46 static constexpr uintptr_t kPointerMask = ~kPayloadMask;
47
48 public:
49 PointerWithPayload() {}
50
51 explicit PointerWithPayload(PointerType* pointer)
52 : pointer_(reinterpret_cast<uintptr_t>(pointer)) {
53 DCHECK_EQ(GetPointer(), pointer);
54 DCHECK_EQ(GetPayload(), static_cast<PayloadType>(0));
55 }
56
57 explicit PointerWithPayload(PayloadType payload)
58 : pointer_(static_cast<uintptr_t>(payload)) {
59 DCHECK_EQ(GetPointer(), nullptr);
60 DCHECK_EQ(GetPayload(), payload);
61 }
62
63 PointerWithPayload(PointerType* pointer, PayloadType payload) {
64 update(pointer, payload);
65 }
66
67 V8_INLINE PointerType* GetPointer() const {
68 return reinterpret_cast<PointerType*>(pointer_ & kPointerMask);
69 }
70
71 V8_INLINE PointerType* operator->() const { return GetPointer(); }
72
73 V8_INLINE void update(PointerType* new_pointer, PayloadType new_payload) {
74 pointer_ = 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_ = reinterpret_cast<uintptr_t>(newptr) | (pointer_ & kPayloadMask);
83 DCHECK_EQ(GetPointer(), newptr);
84 }
85
86 V8_INLINE PayloadType GetPayload() const {
87 return static_cast<PayloadType>(pointer_ & kPayloadMask);
88 }
89
90 V8_INLINE void SetPayload(PayloadType new_payload) {
91 uintptr_t new_payload_ptr = static_cast<uintptr_t>(new_payload);
92 DCHECK_EQ(new_payload_ptr & kPayloadMask, new_payload_ptr);
93 pointer_ = (pointer_ & kPointerMask) | new_payload_ptr;
94 DCHECK_EQ(GetPayload(), new_payload);
95 }
96
97 private:
98 uintptr_t pointer_ = 0;
99};
100
101} // namespace internal
102} // namespace v8
103
104#endif // V8_POINTER_WITH_PAYLOAD_H_
105