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_ = heap_->RegisterStrongRoots(
209 FullObjectSlot(keys_), FullObjectSlot(keys_ + capacity_));
210 } else {
211 // Rehash if there was a GC, then insert.
212 if (gc_counter_ != heap_->gc_count()) Rehash();
213 }
214
215 int index;
216 bool already_exists;
217 std::tie(index, already_exists) = InsertKey(key, Hash(key));
218 DCHECK(!already_exists);
219 return &values_[index];
220 }
221
222 // Deletes the given key from the map using the object's address as the
223 // identity, returning true iff the key was found (in which case, the value
224 // argument will be set to the deleted entry's value).
DeleteEntry(Address key,uintptr_t * deleted_value)225 bool IdentityMapBase::DeleteEntry(Address key, uintptr_t* deleted_value) {
226 CHECK(!is_iterable()); // Don't allow deletion by key while iterable.
227 if (size_ == 0) return false;
228 int index = Lookup(key);
229 if (index < 0) return false; // No entry found.
230 return DeleteIndex(index, deleted_value);
231 }
232
KeyAtIndex(int index) const233 Address IdentityMapBase::KeyAtIndex(int index) const {
234 DCHECK_LE(0, index);
235 DCHECK_LT(index, capacity_);
236 DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
237 CHECK(is_iterable()); // Must be iterable to access by index;
238 return keys_[index];
239 }
240
EntryAtIndex(int index) const241 IdentityMapBase::RawEntry IdentityMapBase::EntryAtIndex(int index) const {
242 DCHECK_LE(0, index);
243 DCHECK_LT(index, capacity_);
244 DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
245 CHECK(is_iterable()); // Must be iterable to access by index;
246 return &values_[index];
247 }
248
NextIndex(int index) const249 int IdentityMapBase::NextIndex(int index) const {
250 DCHECK_LE(-1, index);
251 DCHECK_LE(index, capacity_);
252 CHECK(is_iterable()); // Must be iterable to access by index;
253 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
254 for (++index; index < capacity_; ++index) {
255 if (keys_[index] != not_mapped) {
256 return index;
257 }
258 }
259 return capacity_;
260 }
261
Rehash()262 void IdentityMapBase::Rehash() {
263 CHECK(!is_iterable()); // Can't rehash while iterating.
264 // Record the current GC counter.
265 gc_counter_ = heap_->gc_count();
266 // Assume that most objects won't be moved.
267 std::vector<std::pair<Address, uintptr_t>> reinsert;
268 // Search the table looking for keys that wouldn't be found with their
269 // current hashcode and evacuate them.
270 int last_empty = -1;
271 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
272 for (int i = 0; i < capacity_; i++) {
273 if (keys_[i] == not_mapped) {
274 last_empty = i;
275 } else {
276 int pos = Hash(keys_[i]) & mask_;
277 if (pos <= last_empty || pos > i) {
278 // Evacuate an entry that is in the wrong place.
279 reinsert.push_back(std::pair<Address, uintptr_t>(keys_[i], values_[i]));
280 keys_[i] = not_mapped;
281 values_[i] = 0;
282 last_empty = i;
283 size_--;
284 }
285 }
286 }
287 // Reinsert all the key/value pairs that were in the wrong place.
288 for (auto pair : reinsert) {
289 int index = InsertKey(pair.first, Hash(pair.first)).first;
290 DCHECK_GE(index, 0);
291 values_[index] = pair.second;
292 }
293 }
294
Resize(int new_capacity)295 void IdentityMapBase::Resize(int new_capacity) {
296 CHECK(!is_iterable()); // Can't resize while iterating.
297 // Resize the internal storage and reinsert all the key/value pairs.
298 DCHECK_GT(new_capacity, size_);
299 int old_capacity = capacity_;
300 Address* old_keys = keys_;
301 uintptr_t* old_values = values_;
302
303 capacity_ = new_capacity;
304 mask_ = capacity_ - 1;
305 gc_counter_ = heap_->gc_count();
306 size_ = 0;
307
308 keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_));
309 Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
310 for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
311 values_ = NewPointerArray(capacity_);
312 memset(values_, 0, sizeof(uintptr_t) * capacity_);
313
314 for (int i = 0; i < old_capacity; i++) {
315 if (old_keys[i] == not_mapped) continue;
316 int index = InsertKey(old_keys[i], Hash(old_keys[i])).first;
317 DCHECK_GE(index, 0);
318 values_[index] = old_values[i];
319 }
320
321 // Unregister old keys and register new keys.
322 DCHECK_NOT_NULL(strong_roots_entry_);
323 heap_->UpdateStrongRoots(strong_roots_entry_, FullObjectSlot(keys_),
324 FullObjectSlot(keys_ + capacity_));
325
326 // Delete old storage;
327 DeletePointerArray(reinterpret_cast<uintptr_t*>(old_keys), old_capacity);
328 DeletePointerArray(old_values, old_capacity);
329 }
330
331 } // namespace internal
332 } // namespace v8
333