v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
identity-map.cc
Go to the documentation of this file.
1// Copyright 2015 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
6
7#include "src/base/hashing.h"
8#include "src/base/logging.h"
9#include "src/heap/heap.h"
10#include "src/roots/roots-inl.h"
11
12namespace v8 {
13namespace internal {
14
15static const int kInitialIdentityMapSize = 4;
16static const int kResizeFactor = 2;
17
19 // Clear must be called by the subclass to avoid calling the virtual
20 // DeleteArray function from the destructor.
22}
23
25 if (keys_) {
29 DeletePointerArray(reinterpret_cast<uintptr_t*>(keys_), capacity_);
31 keys_ = nullptr;
32 strong_roots_entry_ = nullptr;
33 values_ = nullptr;
34 size_ = 0;
35 capacity_ = 0;
36 mask_ = 0;
37 }
38}
39
44
49
50std::pair<int, bool> IdentityMapBase::ScanKeysFor(Address address,
51 uint32_t hash) const {
52 int start = hash & mask_;
53 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
54 for (int index = start; index < capacity_; index++) {
55 if (keys_[index] == address) return {index, true}; // Found.
56 if (keys_[index] == not_mapped) return {index, false}; // Not found.
57 }
58 for (int index = 0; index < start; index++) {
59 if (keys_[index] == address) return {index, true}; // Found.
60 if (keys_[index] == not_mapped) return {index, false}; // Not found.
61 }
62 return {-1, false};
63}
64
66 // Grow the map if we reached >= 80% occupancy.
67 return size_ + size_ / 4 >= capacity_;
68}
69
70std::pair<int, bool> IdentityMapBase::InsertKey(Address address,
71 uint32_t hash) {
72 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
74
75 if (ShouldGrow()) {
77 }
78
79 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
80
81 int start = hash & mask_;
82 // Guaranteed to terminate since size_ < capacity_, there must be at least
83 // one empty slot.
84 int index = start;
85 while (true) {
86 if (keys_[index] == address) return {index, true}; // Found.
87 if (keys_[index] == not_mapped) { // Free entry.
88 size_++;
90 keys_[index] = address;
91 return {index, false};
92 }
93 index = (index + 1) & mask_;
94 // We should never loop back to the start.
95 DCHECK_NE(index, start);
96 }
97}
98
99bool IdentityMapBase::DeleteIndex(int index, uintptr_t* deleted_value) {
100 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
101 if (deleted_value != nullptr) *deleted_value = values_[index];
102 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
103 DCHECK_NE(keys_[index], not_mapped);
104 keys_[index] = not_mapped;
105 values_[index] = 0;
106 size_--;
107 DCHECK_GE(size_, 0);
108
112 return true; // No need to fix collisions as resize reinserts keys.
113 }
114
115 // Move any collisions to their new correct location.
116 int next_index = index;
117 for (;;) {
118 next_index = (next_index + 1) & mask_;
119 Address key = keys_[next_index];
120 if (key == not_mapped) break;
121
122 int expected_index = Hash(key) & mask_;
123 if (index < next_index) {
124 if (index < expected_index && expected_index <= next_index) continue;
125 } else {
126 DCHECK_GT(index, next_index);
127 if (index < expected_index || expected_index <= next_index) continue;
128 }
129
130 DCHECK_EQ(not_mapped, keys_[index]);
131 DCHECK_EQ(values_[index], 0);
132 std::swap(keys_[index], keys_[next_index]);
133 std::swap(values_[index], values_[next_index]);
134 index = next_index;
135 }
136
137 return true;
138}
139
141 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
142 uint32_t hash = Hash(key);
143 int index;
144 bool found;
145 std::tie(index, found) = ScanKeysFor(key, hash);
146 if (!found && gc_counter_ != heap_->gc_count()) {
147 // Miss; rehash if there was a GC, then lookup again.
148 const_cast<IdentityMapBase*>(this)->Rehash();
149 std::tie(index, found) = ScanKeysFor(key, hash);
150 }
151 return found ? index : -1;
152}
153
155 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
156 uint32_t hash = Hash(key);
157 // Perform an optimistic lookup.
158 int index;
159 bool already_exists;
160 std::tie(index, already_exists) = ScanKeysFor(key, hash);
161 if (!already_exists) {
162 // Miss; rehash if there was a GC, then insert.
163 if (gc_counter_ != heap_->gc_count()) {
164 Rehash();
165 index = -1;
166 }
167 if (index < 0 || ShouldGrow()) {
168 std::tie(index, already_exists) = InsertKey(key, hash);
169 } else {
170 // If rehashing is not necessary, and the table is already big enough,
171 // then avoid calling InsertKey because it would search the table again
172 // and we already found an adequate location to insert the new key.
173 size_++;
175 DCHECK_EQ(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
176 keys_[index] = key;
177 }
178 }
179 DCHECK_GE(index, 0);
180 return {index, already_exists};
181}
182
183uint32_t IdentityMapBase::Hash(Address address) const {
184 CHECK_NE(address, ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
185 return static_cast<uint32_t>(hasher_(address));
186}
187
188// Searches this map for the given key using the object's address
189// as the identity, returning:
190// found => a pointer to the storage location for the value, true
191// not found => a pointer to a new storage location for the value, false
193 Address key) {
194 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
195 CHECK(!is_iterable()); // Don't allow insertion while iterable.
196 if (capacity_ == 0) {
197 return {InsertEntry(key), false};
198 }
199 auto lookup_result = LookupOrInsert(key);
200 return {&values_[lookup_result.first], lookup_result.second};
201}
202
203// Searches this map for the given key using the object's address
204// as the identity, returning:
205// found => a pointer to the storage location for the value
206// not found => {nullptr}
208 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
209 // Don't allow find by key while iterable (might rehash).
210 CHECK(!is_iterable());
211 if (size_ == 0) return nullptr;
212 int index = Lookup(key);
213 return index >= 0 ? &values_[index] : nullptr;
214}
215
216// Inserts the given key using the object's address as the identity, returning
217// a pointer to the new storage location for the value.
219 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
220 // Don't allow find by key while iterable (might rehash).
221 CHECK(!is_iterable());
222 if (capacity_ == 0) {
223 // Allocate the initial storage for keys and values.
227
228 uintptr_t not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
229 keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_, not_mapped));
230 for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
232
234 heap_->RegisterStrongRoots("IdentityMapBase", FullObjectSlot(keys_),
236 } else {
237 // Rehash if there was a GC, then insert.
238 if (gc_counter_ != heap_->gc_count()) Rehash();
239 }
240
241 int index;
242 bool already_exists;
243 std::tie(index, already_exists) = InsertKey(key, Hash(key));
244 DCHECK(!already_exists);
245 return &values_[index];
246}
247
248// Deletes the given key from the map using the object's address as the
249// identity, returning true iff the key was found (in which case, the value
250// argument will be set to the deleted entry's value).
251bool IdentityMapBase::DeleteEntry(Address key, uintptr_t* deleted_value) {
252 CHECK(!is_iterable()); // Don't allow deletion by key while iterable.
253 if (size_ == 0) return false;
254 int index = Lookup(key);
255 if (index < 0) return false; // No entry found.
256 return DeleteIndex(index, deleted_value);
257}
258
260 DCHECK_LE(0, index);
261 DCHECK_LT(index, capacity_);
262 DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
263 CHECK(is_iterable()); // Must be iterable to access by index;
264 return keys_[index];
265}
266
268 DCHECK_LE(0, index);
269 DCHECK_LT(index, capacity_);
270 DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
271 CHECK(is_iterable()); // Must be iterable to access by index;
272 return &values_[index];
273}
274
275int IdentityMapBase::NextIndex(int index) const {
276 DCHECK_LE(-1, index);
277 DCHECK_LE(index, capacity_);
278 CHECK(is_iterable()); // Must be iterable to access by index;
279 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
280 for (++index; index < capacity_; ++index) {
281 if (keys_[index] != not_mapped) {
282 return index;
283 }
284 }
285 return capacity_;
286}
287
289 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
290 CHECK(!is_iterable()); // Can't rehash while iterating.
291 // Record the current GC counter.
293 // Assume that most objects won't be moved.
294 std::vector<std::pair<Address, uintptr_t>> reinsert;
295 // Search the table looking for keys that wouldn't be found with their
296 // current hashcode and evacuate them.
297 int last_empty = -1;
298 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
299 for (int i = 0; i < capacity_; i++) {
300 if (keys_[i] == not_mapped) {
301 last_empty = i;
302 } else {
303 int pos = Hash(keys_[i]) & mask_;
305 // Evacuate an entry that is in the wrong place.
306 reinsert.push_back(std::pair<Address, uintptr_t>(keys_[i], values_[i]));
307 keys_[i] = not_mapped;
308 values_[i] = 0;
309 last_empty = i;
310 size_--;
311 }
312 }
313 }
314 // Reinsert all the key/value pairs that were in the wrong place.
315 for (auto pair : reinsert) {
316 int index = InsertKey(pair.first, Hash(pair.first)).first;
317 DCHECK_GE(index, 0);
318 values_[index] = pair.second;
319 }
320}
321
322void IdentityMapBase::Resize(int new_capacity) {
323 DCHECK_NE(heap_->gc_state(), Heap::MARK_COMPACT);
324 CHECK(!is_iterable()); // Can't resize while iterating.
325 // Resize the internal storage and reinsert all the key/value pairs.
326 DCHECK_GT(new_capacity, size_);
327 int old_capacity = capacity_;
328 Address* old_keys = keys_;
329 uintptr_t* old_values = values_;
330
331 capacity_ = new_capacity;
332 mask_ = capacity_ - 1;
334 size_ = 0;
335
336 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
337 keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_, not_mapped));
339
340 for (int i = 0; i < old_capacity; i++) {
341 if (old_keys[i] == not_mapped) continue;
342 int index = InsertKey(old_keys[i], Hash(old_keys[i])).first;
343 DCHECK_GE(index, 0);
344 values_[index] = old_values[i];
345 }
346
347 // Unregister old keys and register new keys.
351
352 // Delete old storage;
353 DeletePointerArray(reinterpret_cast<uintptr_t*>(old_keys), old_capacity);
354 DeletePointerArray(old_values, old_capacity);
355}
356
357} // namespace internal
358} // namespace v8
SourcePosition pos
HeapState gc_state() const
Definition heap.h:521
void UpdateStrongRoots(StrongRootsEntry *entry, FullObjectSlot start, FullObjectSlot end)
Definition heap.cc:6801
StrongRootsEntry * RegisterStrongRoots(const char *label, FullObjectSlot start, FullObjectSlot end)
Definition heap.cc:6777
int gc_count() const
Definition heap.h:1351
void UnregisterStrongRoots(StrongRootsEntry *entry)
Definition heap.cc:6807
bool DeleteEntry(Address key, uintptr_t *deleted_value)
uint32_t Hash(Address address) const
int Lookup(Address key) const
virtual void DeletePointerArray(uintptr_t *array, size_t length)=0
void Resize(int new_capacity)
virtual uintptr_t * NewPointerArray(size_t length, uintptr_t value)=0
RawEntry FindEntry(Address key) const
std::pair< int, bool > LookupOrInsert(Address key)
IdentityMapFindResult< uintptr_t > FindOrInsertEntry(Address key)
int NextIndex(int index) const
Address KeyAtIndex(int index) const
std::pair< int, bool > InsertKey(Address address, uint32_t hash)
bool DeleteIndex(int index, uintptr_t *deleted_value)
RawEntry EntryAtIndex(int index) const
std::pair< int, bool > ScanKeysFor(Address address, uint32_t hash) const
base::hash< uintptr_t > hasher_
StrongRootsEntry * strong_roots_entry_
RawEntry InsertEntry(Address key)
int start
static const int kResizeFactor
static const int kInitialIdentityMapSize
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NULL(val)
Definition logging.h:491
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define CHECK_NE(lhs, rhs)
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487