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