• 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/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