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-inl.h"
9
10 namespace v8 {
11 namespace internal {
12
13 static const int kInitialIdentityMapSize = 4;
14 static const int kResizeFactor = 4;
15
~IdentityMapBase()16 IdentityMapBase::~IdentityMapBase() {
17 // Clear must be called by the subclass to avoid calling the virtual
18 // DeleteArray function from the destructor.
19 DCHECK_NULL(keys_);
20 }
21
Clear()22 void IdentityMapBase::Clear() {
23 if (keys_) {
24 DCHECK(!is_iterable());
25 heap_->UnregisterStrongRoots(keys_);
26 DeleteArray(keys_);
27 DeleteArray(values_);
28 keys_ = nullptr;
29 values_ = nullptr;
30 size_ = 0;
31 capacity_ = 0;
32 mask_ = 0;
33 }
34 }
35
EnableIteration()36 void IdentityMapBase::EnableIteration() {
37 CHECK(!is_iterable());
38 is_iterable_ = true;
39 }
40
DisableIteration()41 void IdentityMapBase::DisableIteration() {
42 CHECK(is_iterable());
43 is_iterable_ = false;
44
45 // We might need to resize due to iterator deletion - do this now.
46 if (size_ * kResizeFactor < capacity_ / kResizeFactor) {
47 Resize(capacity_ / kResizeFactor);
48 }
49 }
50
ScanKeysFor(Object * address) const51 int IdentityMapBase::ScanKeysFor(Object* address) const {
52 int start = Hash(address) & mask_;
53 Object* not_mapped = heap_->not_mapped_symbol();
54 for (int index = start; index < capacity_; index++) {
55 if (keys_[index] == address) return index; // Found.
56 if (keys_[index] == not_mapped) return -1; // Not found.
57 }
58 for (int index = 0; index < start; index++) {
59 if (keys_[index] == address) return index; // Found.
60 if (keys_[index] == not_mapped) return -1; // Not found.
61 }
62 return -1;
63 }
64
InsertKey(Object * address)65 int IdentityMapBase::InsertKey(Object* address) {
66 Object* not_mapped = heap_->not_mapped_symbol();
67 while (true) {
68 int start = Hash(address) & mask_;
69 int limit = capacity_ / 2;
70 // Search up to {limit} entries.
71 for (int index = start; --limit > 0; index = (index + 1) & mask_) {
72 if (keys_[index] == address) return index; // Found.
73 if (keys_[index] == not_mapped) { // Free entry.
74 keys_[index] = address;
75 return index;
76 }
77 }
78 // Should only have to resize once, since we grow 4x.
79 Resize(capacity_ * kResizeFactor);
80 }
81 UNREACHABLE();
82 return -1;
83 }
84
DeleteIndex(int index)85 void* IdentityMapBase::DeleteIndex(int index) {
86 void* ret_value = values_[index];
87 Object* not_mapped = heap_->not_mapped_symbol();
88 DCHECK_NE(keys_[index], not_mapped);
89 keys_[index] = not_mapped;
90 values_[index] = nullptr;
91 size_--;
92 DCHECK_GE(size_, 0);
93
94 if (!is_iterable() && (size_ * kResizeFactor < capacity_ / kResizeFactor)) {
95 Resize(capacity_ / kResizeFactor);
96 return ret_value; // No need to fix collisions as resize reinserts keys.
97 }
98
99 // Move any collisions to their new correct location.
100 int next_index = index;
101 for (;;) {
102 next_index = (next_index + 1) & mask_;
103 Object* key = keys_[next_index];
104 if (key == not_mapped) break;
105
106 int expected_index = Hash(key) & mask_;
107 if (index < next_index) {
108 if (index < expected_index && expected_index <= next_index) continue;
109 } else {
110 DCHECK_GT(index, next_index);
111 if (index < expected_index || expected_index <= next_index) continue;
112 }
113 DCHECK_EQ(not_mapped, keys_[index]);
114 DCHECK_NULL(values_[index]);
115 std::swap(keys_[index], keys_[next_index]);
116 std::swap(values_[index], values_[next_index]);
117 index = next_index;
118 }
119
120 return ret_value;
121 }
122
Lookup(Object * key) const123 int IdentityMapBase::Lookup(Object* key) const {
124 int index = ScanKeysFor(key);
125 if (index < 0 && gc_counter_ != heap_->gc_count()) {
126 // Miss; rehash if there was a GC, then lookup again.
127 const_cast<IdentityMapBase*>(this)->Rehash();
128 index = ScanKeysFor(key);
129 }
130 return index;
131 }
132
LookupOrInsert(Object * key)133 int IdentityMapBase::LookupOrInsert(Object* key) {
134 // Perform an optimistic lookup.
135 int index = ScanKeysFor(key);
136 if (index < 0) {
137 // Miss; rehash if there was a GC, then insert.
138 if (gc_counter_ != heap_->gc_count()) Rehash();
139 index = InsertKey(key);
140 size_++;
141 DCHECK_LE(size_, capacity_);
142 }
143 DCHECK_GE(index, 0);
144 return index;
145 }
146
Hash(Object * address) const147 int IdentityMapBase::Hash(Object* address) const {
148 CHECK_NE(address, heap_->not_mapped_symbol());
149 uintptr_t raw_address = reinterpret_cast<uintptr_t>(address);
150 return static_cast<int>(hasher_(raw_address));
151 }
152
153 // Searches this map for the given key using the object's address
154 // as the identity, returning:
155 // found => a pointer to the storage location for the value
156 // not found => a pointer to a new storage location for the value
GetEntry(Object * key)157 IdentityMapBase::RawEntry IdentityMapBase::GetEntry(Object* key) {
158 CHECK(!is_iterable()); // Don't allow insertion while iterable.
159 if (capacity_ == 0) {
160 // Allocate the initial storage for keys and values.
161 capacity_ = kInitialIdentityMapSize;
162 mask_ = kInitialIdentityMapSize - 1;
163 gc_counter_ = heap_->gc_count();
164
165 keys_ = reinterpret_cast<Object**>(NewPointerArray(capacity_));
166 Object* not_mapped = heap_->not_mapped_symbol();
167 for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
168 values_ = NewPointerArray(capacity_);
169 memset(values_, 0, sizeof(void*) * capacity_);
170
171 heap_->RegisterStrongRoots(keys_, keys_ + capacity_);
172 }
173 int index = LookupOrInsert(key);
174 return &values_[index];
175 }
176
177 // Searches this map for the given key using the object's address
178 // as the identity, returning:
179 // found => a pointer to the storage location for the value
180 // not found => {nullptr}
FindEntry(Object * key) const181 IdentityMapBase::RawEntry IdentityMapBase::FindEntry(Object* key) const {
182 // Don't allow find by key while iterable (might rehash).
183 CHECK(!is_iterable());
184 if (size_ == 0) return nullptr;
185 // Remove constness since lookup might have to rehash.
186 int index = Lookup(key);
187 return index >= 0 ? &values_[index] : nullptr;
188 }
189
190 // Deletes the given key from the map using the object's address as the
191 // identity, returning:
192 // found => the value
193 // not found => {nullptr}
DeleteEntry(Object * key)194 void* IdentityMapBase::DeleteEntry(Object* key) {
195 CHECK(!is_iterable()); // Don't allow deletion by key while iterable.
196 if (size_ == 0) return nullptr;
197 int index = Lookup(key);
198 if (index < 0) return nullptr; // No entry found.
199 return DeleteIndex(index);
200 }
201
EntryAtIndex(int index) const202 IdentityMapBase::RawEntry IdentityMapBase::EntryAtIndex(int index) const {
203 DCHECK_LE(0, index);
204 DCHECK_LT(index, capacity_);
205 DCHECK_NE(keys_[index], heap_->not_mapped_symbol());
206 CHECK(is_iterable()); // Must be iterable to access by index;
207 return &values_[index];
208 }
209
NextIndex(int index) const210 int IdentityMapBase::NextIndex(int index) const {
211 DCHECK_LE(-1, index);
212 DCHECK_LE(index, capacity_);
213 CHECK(is_iterable()); // Must be iterable to access by index;
214 Object* not_mapped = heap_->not_mapped_symbol();
215 for (index++; index < capacity_; index++) {
216 if (keys_[index] != not_mapped) {
217 return index;
218 }
219 }
220 return capacity_;
221 }
222
Rehash()223 void IdentityMapBase::Rehash() {
224 CHECK(!is_iterable()); // Can't rehash while iterating.
225 // Record the current GC counter.
226 gc_counter_ = heap_->gc_count();
227 // Assume that most objects won't be moved.
228 std::vector<std::pair<Object*, void*>> reinsert;
229 // Search the table looking for keys that wouldn't be found with their
230 // current hashcode and evacuate them.
231 int last_empty = -1;
232 Object* not_mapped = heap_->not_mapped_symbol();
233 for (int i = 0; i < capacity_; i++) {
234 if (keys_[i] == not_mapped) {
235 last_empty = i;
236 } else {
237 int pos = Hash(keys_[i]) & mask_;
238 if (pos <= last_empty || pos > i) {
239 // Evacuate an entry that is in the wrong place.
240 reinsert.push_back(std::pair<Object*, void*>(keys_[i], values_[i]));
241 keys_[i] = not_mapped;
242 values_[i] = nullptr;
243 last_empty = i;
244 }
245 }
246 }
247 // Reinsert all the key/value pairs that were in the wrong place.
248 for (auto pair : reinsert) {
249 int index = InsertKey(pair.first);
250 DCHECK_GE(index, 0);
251 values_[index] = pair.second;
252 }
253 }
254
Resize(int new_capacity)255 void IdentityMapBase::Resize(int new_capacity) {
256 CHECK(!is_iterable()); // Can't resize while iterating.
257 // Resize the internal storage and reinsert all the key/value pairs.
258 DCHECK_GT(new_capacity, size_);
259 int old_capacity = capacity_;
260 Object** old_keys = keys_;
261 void** old_values = values_;
262
263 capacity_ = new_capacity;
264 mask_ = capacity_ - 1;
265 gc_counter_ = heap_->gc_count();
266
267 keys_ = reinterpret_cast<Object**>(NewPointerArray(capacity_));
268 Object* not_mapped = heap_->not_mapped_symbol();
269 for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
270 values_ = NewPointerArray(capacity_);
271 memset(values_, 0, sizeof(void*) * capacity_);
272
273 for (int i = 0; i < old_capacity; i++) {
274 if (old_keys[i] == not_mapped) continue;
275 int index = InsertKey(old_keys[i]);
276 DCHECK_GE(index, 0);
277 values_[index] = old_values[i];
278 }
279
280 // Unregister old keys and register new keys.
281 heap_->UnregisterStrongRoots(old_keys);
282 heap_->RegisterStrongRoots(keys_, keys_ + capacity_);
283
284 // Delete old storage;
285 DeleteArray(old_keys);
286 DeleteArray(old_values);
287 }
288
289 } // namespace internal
290 } // namespace v8
291