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
5 #include "src/utils/identity-map.h"
6
7 #include "src/base/functional.h"
8 #include "src/base/logging.h"
9 #include "src/heap/heap.h"
10 #include "src/roots/roots-inl.h"
11
12 namespace v8 {
13 namespace internal {
14
15 static const int kInitialIdentityMapSize = 4;
16 static const int kResizeFactor = 2;
17
~IdentityMapBase()18 IdentityMapBase::~IdentityMapBase() {
19 // Clear must be called by the subclass to avoid calling the virtual
20 // DeleteArray function from the destructor.
21 DCHECK_NULL(keys_);
22 }
23
Clear()24 void IdentityMapBase::Clear() {
25 if (keys_) {
26 DCHECK(!is_iterable());
27 DCHECK_NOT_NULL(strong_roots_entry_);
28 heap_->UnregisterStrongRoots(strong_roots_entry_);
29 DeletePointerArray(reinterpret_cast<uintptr_t*>(keys_), capacity_);
30 DeletePointerArray(values_, capacity_);
31 keys_ = nullptr;
32 strong_roots_entry_ = nullptr;
33 values_ = nullptr;
34 size_ = 0;
35 capacity_ = 0;
36 mask_ = 0;
37 }
38 }
39
EnableIteration()40 void IdentityMapBase::EnableIteration() {
41 CHECK(!is_iterable());
42 is_iterable_ = true;
43 }
44
DisableIteration()45 void IdentityMapBase::DisableIteration() {
46 CHECK(is_iterable());
47 is_iterable_ = false;
48 }
49
ScanKeysFor(Address address,uint32_t hash) const50 int IdentityMapBase::ScanKeysFor(Address address, uint32_t hash) const {
51 int start = hash & mask_;
52 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
53 for (int index = start; index < capacity_; index++) {
54 if (keys_[index] == address) return index; // Found.
55 if (keys_[index] == not_mapped) return -1; // Not found.
56 }
57 for (int index = 0; index < start; index++) {
58 if (keys_[index] == address) return index; // Found.
59 if (keys_[index] == not_mapped) return -1; // Not found.
60 }
61 return -1;
62 }
63
InsertKey(Address address,uint32_t hash)64 std::pair<int, bool> IdentityMapBase::InsertKey(Address address,
65 uint32_t hash) {
66 DCHECK_EQ(gc_counter_, heap_->gc_count());
67
68 // Grow the map if we reached >= 80% occupancy.
69 if (size_ + size_ / 4 >= capacity_) {
70 Resize(capacity_ * kResizeFactor);
71 }
72
73 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
74
75 int start = hash & mask_;
76 // Guaranteed to terminate since size_ < capacity_, there must be at least
77 // one empty slot.
78 int index = start;
79 while (true) {
80 if (keys_[index] == address) return {index, true}; // Found.
81 if (keys_[index] == not_mapped) { // Free entry.
82 size_++;
83 DCHECK_LE(size_, capacity_);
84 keys_[index] = address;
85 return {index, false};
86 }
87 index = (index + 1) & mask_;
88 // We should never loop back to the start.
89 DCHECK_NE(index, start);
90 }
91 }
92
DeleteIndex(int index,uintptr_t * deleted_value)93 bool IdentityMapBase::DeleteIndex(int index, uintptr_t* deleted_value) {
94 if (deleted_value != nullptr) *deleted_value = values_[index];
95 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
96 DCHECK_NE(keys_[index], not_mapped);
97 keys_[index] = not_mapped;
98 values_[index] = 0;
99 size_--;
100 DCHECK_GE(size_, 0);
101
102 if (capacity_ > kInitialIdentityMapSize &&
103 size_ * kResizeFactor < capacity_ / kResizeFactor) {
104 Resize(capacity_ / kResizeFactor);
105 return true; // No need to fix collisions as resize reinserts keys.
106 }
107
108 // Move any collisions to their new correct location.
109 int next_index = index;
110 for (;;) {
111 next_index = (next_index + 1) & mask_;
112 Address key = keys_[next_index];
113 if (key == not_mapped) break;
114
115 int expected_index = Hash(key) & mask_;
116 if (index < next_index) {
117 if (index < expected_index && expected_index <= next_index) continue;
118 } else {
119 DCHECK_GT(index, next_index);
120 if (index < expected_index || expected_index <= next_index) continue;
121 }
122
123 DCHECK_EQ(not_mapped, keys_[index]);
124 DCHECK_EQ(values_[index], 0);
125 std::swap(keys_[index], keys_[next_index]);
126 std::swap(values_[index], values_[next_index]);
127 index = next_index;
128 }
129
130 return true;
131 }
132
Lookup(Address key) const133 int IdentityMapBase::Lookup(Address key) const {
134 uint32_t hash = Hash(key);
135 int index = ScanKeysFor(key, hash);
136 if (index < 0 && gc_counter_ != heap_->gc_count()) {
137 // Miss; rehash if there was a GC, then lookup again.
138 const_cast<IdentityMapBase*>(this)->Rehash();
139 index = ScanKeysFor(key, hash);
140 }
141 return index;
142 }
143
LookupOrInsert(Address key)144 std::pair<int, bool> IdentityMapBase::LookupOrInsert(Address key) {
145 uint32_t hash = Hash(key);
146 // Perform an optimistic lookup.
147 int index = ScanKeysFor(key, hash);
148 bool already_exists;
149 if (index < 0) {
150 // Miss; rehash if there was a GC, then insert.
151 if (gc_counter_ != heap_->gc_count()) Rehash();
152 std::tie(index, already_exists) = InsertKey(key, hash);
153 } else {
154 already_exists = true;
155 }
156 DCHECK_GE(index, 0);
157 return {index, already_exists};
158 }
159
Hash(Address address) const160 uint32_t IdentityMapBase::Hash(Address address) const {
161 CHECK_NE(address, ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
162 return static_cast<uint32_t>(hasher_(address));
163 }
164
165 // Searches this map for the given key using the object's address
166 // as the identity, returning:
167 // found => a pointer to the storage location for the value, true
168 // not found => a pointer to a new storage location for the value, false
FindOrInsertEntry(Address key)169 IdentityMapFindResult<uintptr_t> IdentityMapBase::FindOrInsertEntry(
170 Address key) {
171 CHECK(!is_iterable()); // Don't allow insertion while iterable.
172 if (capacity_ == 0) {
173 return {InsertEntry(key), false};
174 }
175 auto lookup_result = LookupOrInsert(key);
176 return {&values_[lookup_result.first], lookup_result.second};
177 }
178
179 // Searches this map for the given key using the object's address
180 // as the identity, returning:
181 // found => a pointer to the storage location for the value
182 // not found => {nullptr}
FindEntry(Address key) const183 IdentityMapBase::RawEntry IdentityMapBase::FindEntry(Address key) const {
184 // Don't allow find by key while iterable (might rehash).
185 CHECK(!is_iterable());
186 if (size_ == 0) return nullptr;
187 int index = Lookup(key);
188 return index >= 0 ? &values_[index] : nullptr;
189 }
190
191 // Inserts the given key using the object's address as the identity, returning
192 // a pointer to the new storage location for the value.
InsertEntry(Address key)193 IdentityMapBase::RawEntry IdentityMapBase::InsertEntry(Address key) {
194 // Don't allow find by key while iterable (might rehash).
195 CHECK(!is_iterable());
196 if (capacity_ == 0) {
197 // Allocate the initial storage for keys and values.
198 capacity_ = kInitialIdentityMapSize;
199 mask_ = kInitialIdentityMapSize - 1;
200 gc_counter_ = heap_->gc_count();
201
202 keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_));
203 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
204 for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
205 values_ = NewPointerArray(capacity_);
206 memset(values_, 0, sizeof(uintptr_t) * capacity_);
207
208 strong_roots_entry_ =
209 heap_->RegisterStrongRoots("IdentityMapBase", FullObjectSlot(keys_),
210 FullObjectSlot(keys_ + capacity_));
211 } else {
212 // Rehash if there was a GC, then insert.
213 if (gc_counter_ != heap_->gc_count()) Rehash();
214 }
215
216 int index;
217 bool already_exists;
218 std::tie(index, already_exists) = InsertKey(key, Hash(key));
219 DCHECK(!already_exists);
220 return &values_[index];
221 }
222
223 // Deletes the given key from the map using the object's address as the
224 // identity, returning true iff the key was found (in which case, the value
225 // argument will be set to the deleted entry's value).
DeleteEntry(Address key,uintptr_t * deleted_value)226 bool IdentityMapBase::DeleteEntry(Address key, uintptr_t* deleted_value) {
227 CHECK(!is_iterable()); // Don't allow deletion by key while iterable.
228 if (size_ == 0) return false;
229 int index = Lookup(key);
230 if (index < 0) return false; // No entry found.
231 return DeleteIndex(index, deleted_value);
232 }
233
KeyAtIndex(int index) const234 Address IdentityMapBase::KeyAtIndex(int index) const {
235 DCHECK_LE(0, index);
236 DCHECK_LT(index, capacity_);
237 DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
238 CHECK(is_iterable()); // Must be iterable to access by index;
239 return keys_[index];
240 }
241
EntryAtIndex(int index) const242 IdentityMapBase::RawEntry IdentityMapBase::EntryAtIndex(int index) const {
243 DCHECK_LE(0, index);
244 DCHECK_LT(index, capacity_);
245 DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
246 CHECK(is_iterable()); // Must be iterable to access by index;
247 return &values_[index];
248 }
249
NextIndex(int index) const250 int IdentityMapBase::NextIndex(int index) const {
251 DCHECK_LE(-1, index);
252 DCHECK_LE(index, capacity_);
253 CHECK(is_iterable()); // Must be iterable to access by index;
254 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
255 for (++index; index < capacity_; ++index) {
256 if (keys_[index] != not_mapped) {
257 return index;
258 }
259 }
260 return capacity_;
261 }
262
Rehash()263 void IdentityMapBase::Rehash() {
264 CHECK(!is_iterable()); // Can't rehash while iterating.
265 // Record the current GC counter.
266 gc_counter_ = heap_->gc_count();
267 // Assume that most objects won't be moved.
268 std::vector<std::pair<Address, uintptr_t>> reinsert;
269 // Search the table looking for keys that wouldn't be found with their
270 // current hashcode and evacuate them.
271 int last_empty = -1;
272 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
273 for (int i = 0; i < capacity_; i++) {
274 if (keys_[i] == not_mapped) {
275 last_empty = i;
276 } else {
277 int pos = Hash(keys_[i]) & mask_;
278 if (pos <= last_empty || pos > i) {
279 // Evacuate an entry that is in the wrong place.
280 reinsert.push_back(std::pair<Address, uintptr_t>(keys_[i], values_[i]));
281 keys_[i] = not_mapped;
282 values_[i] = 0;
283 last_empty = i;
284 size_--;
285 }
286 }
287 }
288 // Reinsert all the key/value pairs that were in the wrong place.
289 for (auto pair : reinsert) {
290 int index = InsertKey(pair.first, Hash(pair.first)).first;
291 DCHECK_GE(index, 0);
292 values_[index] = pair.second;
293 }
294 }
295
Resize(int new_capacity)296 void IdentityMapBase::Resize(int new_capacity) {
297 CHECK(!is_iterable()); // Can't resize while iterating.
298 // Resize the internal storage and reinsert all the key/value pairs.
299 DCHECK_GT(new_capacity, size_);
300 int old_capacity = capacity_;
301 Address* old_keys = keys_;
302 uintptr_t* old_values = values_;
303
304 capacity_ = new_capacity;
305 mask_ = capacity_ - 1;
306 gc_counter_ = heap_->gc_count();
307 size_ = 0;
308
309 keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_));
310 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
311 for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
312 values_ = NewPointerArray(capacity_);
313 memset(values_, 0, sizeof(uintptr_t) * capacity_);
314
315 for (int i = 0; i < old_capacity; i++) {
316 if (old_keys[i] == not_mapped) continue;
317 int index = InsertKey(old_keys[i], Hash(old_keys[i])).first;
318 DCHECK_GE(index, 0);
319 values_[index] = old_values[i];
320 }
321
322 // Unregister old keys and register new keys.
323 DCHECK_NOT_NULL(strong_roots_entry_);
324 heap_->UpdateStrongRoots(strong_roots_entry_, FullObjectSlot(keys_),
325 FullObjectSlot(keys_ + capacity_));
326
327 // Delete old storage;
328 DeletePointerArray(reinterpret_cast<uintptr_t*>(old_keys), old_capacity);
329 DeletePointerArray(old_values, old_capacity);
330 }
331
332 } // namespace internal
333 } // namespace v8
334