v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
cppheap-pointer-table-inl.h
Go to the documentation of this file.
1// Copyright 2024 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_SANDBOX_CPPHEAP_POINTER_TABLE_INL_H_
6#define V8_SANDBOX_CPPHEAP_POINTER_TABLE_INL_H_
7
9// Include the non-inl header before the rest of the headers.
10
12
13#ifdef V8_COMPRESS_POINTERS
14
15namespace v8 {
16namespace internal {
17
18void CppHeapPointerTableEntry::MakePointerEntry(Address value,
20 bool mark_as_alive) {
21 // Top bits must be zero, otherwise we'd loose information when shifting.
25
26 Payload new_payload(value, tag);
27 DCHECK(!new_payload.HasMarkBitSet());
28 if (V8_UNLIKELY(mark_as_alive)) {
29 new_payload.SetMarkBit();
30 }
31 payload_.store(new_payload, std::memory_order_relaxed);
32}
33
34Address CppHeapPointerTableEntry::GetPointer(
35 CppHeapPointerTagRange tag_range) const {
36 auto payload = payload_.load(std::memory_order_relaxed);
37 DCHECK(payload.ContainsPointer());
38 return payload.Untag(tag_range);
39}
40
41void CppHeapPointerTableEntry::SetPointer(Address value,
43 // Top bits must be zero, otherwise we'd loose information when shifting.
47 DCHECK(payload_.load(std::memory_order_relaxed).ContainsPointer());
48
49 Payload new_payload(value, tag);
50 payload_.store(new_payload, std::memory_order_relaxed);
51}
52
53bool CppHeapPointerTableEntry::HasPointer(
54 CppHeapPointerTagRange tag_range) const {
55 auto payload = payload_.load(std::memory_order_relaxed);
56 return payload.IsTaggedWithTagIn(tag_range);
57}
58
59void CppHeapPointerTableEntry::MakeZappedEntry() {
61 payload_.store(new_payload, std::memory_order_relaxed);
62}
63
64void CppHeapPointerTableEntry::MakeFreelistEntry(uint32_t next_entry_index) {
65 static_assert(kMaxCppHeapPointers <= std::numeric_limits<uint32_t>::max());
66 Payload new_payload(next_entry_index, CppHeapPointerTag::kFreeEntryTag);
67 payload_.store(new_payload, std::memory_order_relaxed);
68}
69
70uint32_t CppHeapPointerTableEntry::GetNextFreelistEntryIndex() const {
71 auto payload = payload_.load(std::memory_order_relaxed);
72 return payload.ExtractFreelistLink();
73}
74
75void CppHeapPointerTableEntry::Mark() {
76 auto old_payload = payload_.load(std::memory_order_relaxed);
77 DCHECK(old_payload.ContainsPointer());
78
79 auto new_payload = old_payload;
80 new_payload.SetMarkBit();
81
82 // We don't need to perform the CAS in a loop: if the new value is not equal
83 // to the old value, then the mutator must've just written a new value into
84 // the entry. The mutator will also set the markbit through the write barrier.
85 payload_.compare_exchange_strong(old_payload, new_payload,
86 std::memory_order_relaxed);
87}
88
89void CppHeapPointerTableEntry::MakeEvacuationEntry(Address handle_location) {
90 Payload new_payload(handle_location, CppHeapPointerTag::kEvacuationEntryTag);
91 payload_.store(new_payload, std::memory_order_relaxed);
92}
93
94bool CppHeapPointerTableEntry::HasEvacuationEntry() const {
95 auto payload = payload_.load(std::memory_order_relaxed);
96 return payload.ContainsEvacuationEntry();
97}
98
99void CppHeapPointerTableEntry::Evacuate(CppHeapPointerTableEntry& dest) {
100 auto payload = payload_.load(std::memory_order_relaxed);
101 // We expect to only evacuate entries containing external pointers.
102 DCHECK(payload.ContainsPointer());
103 // Currently, evacuation only happens during table compaction. In that case,
104 // the marking bit must be unset as the entry has already been visited by the
105 // sweeper (which clears the marking bit). If this ever changes, we'll need
106 // to let the caller specify what to do with the marking bit during
107 // evacuation.
108 DCHECK(!payload.HasMarkBitSet());
109
110 dest.payload_.store(payload, std::memory_order_relaxed);
111
112 // The destination entry takes ownership of the pointer.
113 MakeZappedEntry();
114}
115
116Address CppHeapPointerTable::Get(CppHeapPointerHandle handle,
117 CppHeapPointerTagRange tag_range) const {
118 uint32_t index = HandleToIndex(handle);
119 DCHECK(index == 0 || at(index).HasPointer(tag_range));
120 return at(index).GetPointer(tag_range);
121}
122
123void CppHeapPointerTable::Set(CppHeapPointerHandle handle, Address value,
124 CppHeapPointerTag tag) {
126 uint32_t index = HandleToIndex(handle);
127 at(index).SetPointer(value, tag);
128}
129
130CppHeapPointerHandle CppHeapPointerTable::AllocateAndInitializeEntry(
131 Space* space, Address initial_value, CppHeapPointerTag tag) {
132 DCHECK(space->BelongsTo(this));
133 uint32_t index = AllocateEntry(space);
134 at(index).MakePointerEntry(initial_value, tag, space->allocate_black());
135
136 CppHeapPointerHandle handle = IndexToHandle(index);
137
138 return handle;
139}
140
141void CppHeapPointerTable::Mark(Space* space, CppHeapPointerHandle handle,
142 Address handle_location) {
143 DCHECK(space->BelongsTo(this));
144
145 // The handle_location must always contain the given handle. Except if the
146 // slot is lazily-initialized. In that case, the handle may transition from
147 // the null handle to a valid handle. However, in that case the
148 // newly-allocated entry will already have been marked as alive during
149 // allocation, and so we don't need to do anything here.
150#ifdef DEBUG
152 reinterpret_cast<CppHeapPointerHandle*>(handle_location));
153 DCHECK(handle == kNullCppHeapPointerHandle || handle == current_handle);
154#endif
155
156 // If the handle is null, it doesn't have an EPT entry; no mark is needed.
157 if (handle == kNullCppHeapPointerHandle) return;
158
159 uint32_t index = HandleToIndex(handle);
160 DCHECK(space->Contains(index));
161
162 // If the table is being compacted and the entry is inside the evacuation
163 // area, then allocate and set up an evacuation entry for it.
164 MaybeCreateEvacuationEntry(space, index, handle_location);
165
166 // Even if the entry is marked for evacuation, it still needs to be marked as
167 // alive as it may be visited during sweeping before being evacuation.
168 at(index).Mark();
169}
170
171// static
172bool CppHeapPointerTable::IsValidHandle(CppHeapPointerHandle handle) {
173 uint32_t index = handle >> kCppHeapPointerIndexShift;
174 return handle == index << kCppHeapPointerIndexShift;
175}
176
177// static
178uint32_t CppHeapPointerTable::HandleToIndex(CppHeapPointerHandle handle) {
179 DCHECK(IsValidHandle(handle));
180 uint32_t index = handle >> kCppHeapPointerIndexShift;
182 return index;
183}
184
185// static
186CppHeapPointerHandle CppHeapPointerTable::IndexToHandle(uint32_t index) {
188 CppHeapPointerHandle handle = index << kCppHeapPointerIndexShift;
190 return handle;
191}
192
193bool CppHeapPointerTable::Contains(Space* space,
195 DCHECK(space->BelongsTo(this));
196 return space->Contains(HandleToIndex(handle));
197}
198
199} // namespace internal
200} // namespace v8
201
202#endif // V8_COMPRESS_POINTERS
203
204#endif // V8_SANDBOX_CPPHEAP_POINTER_TABLE_INL_H_
static T Acquire_Load(T *addr)
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr size_t kMaxCppHeapPointers
constexpr uint64_t kCppHeapPointerPayloadShift
constexpr int kBitsPerSystemPointer
Definition globals.h:684
uint32_t CppHeapPointerHandle
static constexpr Address kNullAddress
Definition v8-internal.h:53
constexpr CppHeapPointerHandle kNullCppHeapPointerHandle
CppHeapPointerTag
Definition v8-sandbox.h:28
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_UNLIKELY(condition)
Definition v8config.h:660