• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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