Line data Source code
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/identity-map.h"
6 :
7 : #include "src/base/functional.h"
8 : #include "src/heap/heap.h"
9 : #include "src/roots-inl.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : static const int kInitialIdentityMapSize = 4;
15 : static const int kResizeFactor = 2;
16 :
17 734045 : IdentityMapBase::~IdentityMapBase() {
18 : // Clear must be called by the subclass to avoid calling the virtual
19 : // DeleteArray function from the destructor.
20 : DCHECK_NULL(keys_);
21 734045 : }
22 :
23 796543 : void IdentityMapBase::Clear() {
24 796543 : if (keys_) {
25 : DCHECK(!is_iterable());
26 526120 : heap_->UnregisterStrongRoots(FullObjectSlot(keys_));
27 526120 : DeleteArray(keys_);
28 526119 : DeleteArray(values_);
29 526120 : keys_ = nullptr;
30 526120 : values_ = nullptr;
31 526120 : size_ = 0;
32 526120 : capacity_ = 0;
33 526120 : mask_ = 0;
34 : }
35 796543 : }
36 :
37 166 : void IdentityMapBase::EnableIteration() {
38 166 : CHECK(!is_iterable());
39 166 : is_iterable_ = true;
40 166 : }
41 :
42 166 : void IdentityMapBase::DisableIteration() {
43 166 : CHECK(is_iterable());
44 166 : is_iterable_ = false;
45 166 : }
46 :
47 166220211 : int IdentityMapBase::ScanKeysFor(Address address) const {
48 166220211 : int start = Hash(address) & mask_;
49 166211359 : Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
50 1098529353 : for (int index = start; index < capacity_; index++) {
51 625299357 : if (keys_[index] == address) return index; // Found.
52 525186568 : if (keys_[index] == not_mapped) return -1; // Not found.
53 : }
54 126873435 : for (int index = 0; index < start; index++) {
55 66640575 : if (keys_[index] == address) return index; // Found.
56 65483070 : if (keys_[index] == not_mapped) return -1; // Not found.
57 : }
58 : return -1;
59 : }
60 :
61 172613520 : int IdentityMapBase::InsertKey(Address address) {
62 172613520 : Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
63 2821914 : while (true) {
64 175435434 : int start = Hash(address) & mask_;
65 175430486 : int limit = capacity_ / 2;
66 : // Search up to {limit} entries.
67 1075020646 : for (int index = start; --limit > 0; index = (index + 1) & mask_) {
68 622403655 : if (keys_[index] == address) return index; // Found.
69 622408474 : if (keys_[index] == not_mapped) { // Free entry.
70 172613394 : size_++;
71 : DCHECK_LE(size_, capacity_);
72 172613394 : keys_[index] = address;
73 172613394 : return index;
74 : }
75 : }
76 : // Should only have to resize once, since we grow 4x.
77 2821911 : Resize(capacity_ * kResizeFactor);
78 : }
79 : UNREACHABLE();
80 : }
81 :
82 10320 : bool IdentityMapBase::DeleteIndex(int index, void** deleted_value) {
83 10320 : if (deleted_value != nullptr) *deleted_value = values_[index];
84 10320 : Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
85 : DCHECK_NE(keys_[index], not_mapped);
86 10320 : keys_[index] = not_mapped;
87 10320 : values_[index] = nullptr;
88 10320 : size_--;
89 : DCHECK_GE(size_, 0);
90 :
91 20589 : if (capacity_ > kInitialIdentityMapSize &&
92 10269 : size_ * kResizeFactor < capacity_ / kResizeFactor) {
93 120 : Resize(capacity_ / kResizeFactor);
94 120 : return true; // No need to fix collisions as resize reinserts keys.
95 : }
96 :
97 : // Move any collisions to their new correct location.
98 : int next_index = index;
99 : for (;;) {
100 46858 : next_index = (next_index + 1) & mask_;
101 46858 : Address key = keys_[next_index];
102 46858 : if (key == not_mapped) break;
103 :
104 36658 : int expected_index = Hash(key) & mask_;
105 36658 : if (index < next_index) {
106 35784 : if (index < expected_index && expected_index <= next_index) continue;
107 : } else {
108 : DCHECK_GT(index, next_index);
109 874 : if (index < expected_index || expected_index <= next_index) continue;
110 : }
111 :
112 : DCHECK_EQ(not_mapped, keys_[index]);
113 : DCHECK_NULL(values_[index]);
114 8275 : std::swap(keys_[index], keys_[next_index]);
115 8275 : std::swap(values_[index], values_[next_index]);
116 : index = next_index;
117 : }
118 :
119 : return true;
120 : }
121 :
122 186598 : int IdentityMapBase::Lookup(Address key) const {
123 186598 : int index = ScanKeysFor(key);
124 259734 : if (index < 0 && gc_counter_ != heap_->gc_count()) {
125 : // Miss; rehash if there was a GC, then lookup again.
126 45 : const_cast<IdentityMapBase*>(this)->Rehash();
127 45 : index = ScanKeysFor(key);
128 : }
129 186598 : return index;
130 : }
131 :
132 166033832 : int IdentityMapBase::LookupOrInsert(Address key) {
133 : // Perform an optimistic lookup.
134 166033832 : int index = ScanKeysFor(key);
135 166033056 : if (index < 0) {
136 : // Miss; rehash if there was a GC, then insert.
137 129740432 : if (gc_counter_ != heap_->gc_count()) Rehash();
138 64870216 : index = InsertKey(key);
139 : }
140 : DCHECK_GE(index, 0);
141 166033023 : return index;
142 : }
143 :
144 341693916 : int IdentityMapBase::Hash(Address address) const {
145 683387832 : CHECK_NE(address, ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
146 341693840 : return static_cast<int>(hasher_(address));
147 : }
148 :
149 : // Searches this map for the given key using the object's address
150 : // as the identity, returning:
151 : // found => a pointer to the storage location for the value
152 : // not found => a pointer to a new storage location for the value
153 166033831 : IdentityMapBase::RawEntry IdentityMapBase::GetEntry(Address key) {
154 166033831 : CHECK(!is_iterable()); // Don't allow insertion while iterable.
155 166033831 : if (capacity_ == 0) {
156 : // Allocate the initial storage for keys and values.
157 526114 : capacity_ = kInitialIdentityMapSize;
158 526114 : mask_ = kInitialIdentityMapSize - 1;
159 1052228 : gc_counter_ = heap_->gc_count();
160 :
161 526114 : keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_));
162 526118 : Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
163 2630582 : for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
164 526118 : values_ = NewPointerArray(capacity_);
165 526118 : memset(values_, 0, sizeof(void*) * capacity_);
166 :
167 1578355 : heap_->RegisterStrongRoots(FullObjectSlot(keys_),
168 1052236 : FullObjectSlot(keys_ + capacity_));
169 : }
170 166033836 : int index = LookupOrInsert(key);
171 166032973 : return &values_[index];
172 : }
173 :
174 : // Searches this map for the given key using the object's address
175 : // as the identity, returning:
176 : // found => a pointer to the storage location for the value
177 : // not found => {nullptr}
178 165847 : IdentityMapBase::RawEntry IdentityMapBase::FindEntry(Address key) const {
179 : // Don't allow find by key while iterable (might rehash).
180 165847 : CHECK(!is_iterable());
181 165847 : if (size_ == 0) return nullptr;
182 : // Remove constness since lookup might have to rehash.
183 159534 : int index = Lookup(key);
184 159534 : return index >= 0 ? &values_[index] : nullptr;
185 : }
186 :
187 : // Deletes the given key from the map using the object's address as the
188 : // identity, returning true iff the key was found (in which case, the value
189 : // argument will be set to the deleted entry's value).
190 115984 : bool IdentityMapBase::DeleteEntry(Address key, void** deleted_value) {
191 115984 : CHECK(!is_iterable()); // Don't allow deletion by key while iterable.
192 115984 : if (size_ == 0) return false;
193 27064 : int index = Lookup(key);
194 27064 : if (index < 0) return false; // No entry found.
195 10320 : return DeleteIndex(index, deleted_value);
196 : }
197 :
198 12992 : Address IdentityMapBase::KeyAtIndex(int index) const {
199 : DCHECK_LE(0, index);
200 : DCHECK_LT(index, capacity_);
201 : DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
202 12992 : CHECK(is_iterable()); // Must be iterable to access by index;
203 12992 : return keys_[index];
204 : }
205 :
206 24542 : IdentityMapBase::RawEntry IdentityMapBase::EntryAtIndex(int index) const {
207 : DCHECK_LE(0, index);
208 : DCHECK_LT(index, capacity_);
209 : DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
210 24542 : CHECK(is_iterable()); // Must be iterable to access by index;
211 24542 : return &values_[index];
212 : }
213 :
214 18933 : int IdentityMapBase::NextIndex(int index) const {
215 : DCHECK_LE(-1, index);
216 : DCHECK_LE(index, capacity_);
217 18933 : CHECK(is_iterable()); // Must be iterable to access by index;
218 18933 : Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
219 25174 : for (++index; index < capacity_; ++index) {
220 25008 : if (keys_[index] != not_mapped) {
221 : return index;
222 : }
223 : }
224 : return capacity_;
225 : }
226 :
227 197 : void IdentityMapBase::Rehash() {
228 197 : CHECK(!is_iterable()); // Can't rehash while iterating.
229 : // Record the current GC counter.
230 394 : gc_counter_ = heap_->gc_count();
231 : // Assume that most objects won't be moved.
232 : std::vector<std::pair<Address, void*>> reinsert;
233 : // Search the table looking for keys that wouldn't be found with their
234 : // current hashcode and evacuate them.
235 : int last_empty = -1;
236 : Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
237 17677 : for (int i = 0; i < capacity_; i++) {
238 8740 : if (keys_[i] == not_mapped) {
239 : last_empty = i;
240 : } else {
241 5580 : int pos = Hash(keys_[i]) & mask_;
242 5580 : if (pos <= last_empty || pos > i) {
243 : // Evacuate an entry that is in the wrong place.
244 1264 : reinsert.push_back(std::pair<Address, void*>(keys_[i], values_[i]));
245 632 : keys_[i] = not_mapped;
246 632 : values_[i] = nullptr;
247 : last_empty = i;
248 632 : size_--;
249 : }
250 : }
251 : }
252 : // Reinsert all the key/value pairs that were in the wrong place.
253 829 : for (auto pair : reinsert) {
254 632 : int index = InsertKey(pair.first);
255 : DCHECK_GE(index, 0);
256 632 : values_[index] = pair.second;
257 : }
258 197 : }
259 :
260 2822050 : void IdentityMapBase::Resize(int new_capacity) {
261 2822050 : CHECK(!is_iterable()); // Can't resize while iterating.
262 : // Resize the internal storage and reinsert all the key/value pairs.
263 : DCHECK_GT(new_capacity, size_);
264 2822050 : int old_capacity = capacity_;
265 2822050 : Address* old_keys = keys_;
266 2822050 : void** old_values = values_;
267 :
268 2822050 : capacity_ = new_capacity;
269 2822050 : mask_ = capacity_ - 1;
270 5644100 : gc_counter_ = heap_->gc_count();
271 2822050 : size_ = 0;
272 :
273 2822050 : keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_));
274 2822050 : Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
275 238015746 : for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
276 2822050 : values_ = NewPointerArray(capacity_);
277 2822033 : memset(values_, 0, sizeof(void*) * capacity_);
278 :
279 238058589 : for (int i = 0; i < old_capacity; i++) {
280 117618256 : if (old_keys[i] == not_mapped) continue;
281 107743374 : int index = InsertKey(old_keys[i]);
282 : DCHECK_GE(index, 0);
283 107743396 : values_[index] = old_values[i];
284 : }
285 :
286 : // Unregister old keys and register new keys.
287 2822055 : heap_->UnregisterStrongRoots(FullObjectSlot(old_keys));
288 8466165 : heap_->RegisterStrongRoots(FullObjectSlot(keys_),
289 5644110 : FullObjectSlot(keys_ + capacity_));
290 :
291 : // Delete old storage;
292 2822055 : DeleteArray(old_keys);
293 2822055 : DeleteArray(old_values);
294 2822054 : }
295 :
296 : } // namespace internal
297 121996 : } // namespace v8
|