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/heap/heap.h"
8 #include "src/heap/heap-inl.h"
9 #include "src/zone-containers.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 static const int kInitialIdentityMapSize = 4;
15 static const int kResizeFactor = 4;
16 
~IdentityMapBase()17 IdentityMapBase::~IdentityMapBase() {
18   if (keys_) heap_->UnregisterStrongRoots(keys_);
19 }
20 
21 
Lookup(Object * key)22 IdentityMapBase::RawEntry IdentityMapBase::Lookup(Object* key) {
23   int index = LookupIndex(key);
24   return index >= 0 ? &values_[index] : nullptr;
25 }
26 
27 
Insert(Object * key)28 IdentityMapBase::RawEntry IdentityMapBase::Insert(Object* key) {
29   int index = InsertIndex(key);
30   DCHECK_GE(index, 0);
31   return &values_[index];
32 }
33 
34 
Hash(Object * address)35 int IdentityMapBase::Hash(Object* address) {
36   CHECK_NE(address, heap_->not_mapped_symbol());
37   uintptr_t raw_address = reinterpret_cast<uintptr_t>(address);
38   // Xor some of the upper bits, since the lower 2 or 3 are usually aligned.
39   return static_cast<int>((raw_address >> 11) ^ raw_address);
40 }
41 
42 
LookupIndex(Object * address)43 int IdentityMapBase::LookupIndex(Object* address) {
44   int start = Hash(address) & mask_;
45   Object* not_mapped = heap_->not_mapped_symbol();
46   for (int index = start; index < size_; index++) {
47     if (keys_[index] == address) return index;  // Found.
48     if (keys_[index] == not_mapped) return -1;  // Not found.
49   }
50   for (int index = 0; index < start; index++) {
51     if (keys_[index] == address) return index;  // Found.
52     if (keys_[index] == not_mapped) return -1;  // Not found.
53   }
54   return -1;
55 }
56 
57 
InsertIndex(Object * address)58 int IdentityMapBase::InsertIndex(Object* address) {
59   Object* not_mapped = heap_->not_mapped_symbol();
60   while (true) {
61     int start = Hash(address) & mask_;
62     int limit = size_ / 2;
63     // Search up to {limit} entries.
64     for (int index = start; --limit > 0; index = (index + 1) & mask_) {
65       if (keys_[index] == address) return index;  // Found.
66       if (keys_[index] == not_mapped) {           // Free entry.
67         keys_[index] = address;
68         return index;
69       }
70     }
71     Resize();  // Should only have to resize once, since we grow 4x.
72   }
73   UNREACHABLE();
74   return -1;
75 }
76 
77 
78 // Searches this map for the given key using the object's address
79 // as the identity, returning:
80 //    found => a pointer to the storage location for the value
81 //    not found => a pointer to a new storage location for the value
GetEntry(Object * key)82 IdentityMapBase::RawEntry IdentityMapBase::GetEntry(Object* key) {
83   RawEntry result;
84   if (size_ == 0) {
85     // Allocate the initial storage for keys and values.
86     size_ = kInitialIdentityMapSize;
87     mask_ = kInitialIdentityMapSize - 1;
88     gc_counter_ = heap_->gc_count();
89 
90     keys_ = zone_->NewArray<Object*>(size_);
91     Object* not_mapped = heap_->not_mapped_symbol();
92     for (int i = 0; i < size_; i++) keys_[i] = not_mapped;
93     values_ = zone_->NewArray<void*>(size_);
94     memset(values_, 0, sizeof(void*) * size_);
95 
96     heap_->RegisterStrongRoots(keys_, keys_ + size_);
97     result = Insert(key);
98   } else {
99     // Perform an optimistic lookup.
100     result = Lookup(key);
101     if (result == nullptr) {
102       // Miss; rehash if there was a GC, then insert.
103       if (gc_counter_ != heap_->gc_count()) Rehash();
104       result = Insert(key);
105     }
106   }
107   return result;
108 }
109 
110 
111 // Searches this map for the given key using the object's address
112 // as the identity, returning:
113 //    found => a pointer to the storage location for the value
114 //    not found => {nullptr}
FindEntry(Object * key)115 IdentityMapBase::RawEntry IdentityMapBase::FindEntry(Object* key) {
116   if (size_ == 0) return nullptr;
117 
118   RawEntry result = Lookup(key);
119   if (result == nullptr && gc_counter_ != heap_->gc_count()) {
120     Rehash();  // Rehash is expensive, so only do it in case of a miss.
121     result = Lookup(key);
122   }
123   return result;
124 }
125 
126 
Rehash()127 void IdentityMapBase::Rehash() {
128   // Record the current GC counter.
129   gc_counter_ = heap_->gc_count();
130   // Assume that most objects won't be moved.
131   ZoneVector<std::pair<Object*, void*>> reinsert(zone_);
132   // Search the table looking for keys that wouldn't be found with their
133   // current hashcode and evacuate them.
134   int last_empty = -1;
135   Object* not_mapped = heap_->not_mapped_symbol();
136   for (int i = 0; i < size_; i++) {
137     if (keys_[i] == not_mapped) {
138       last_empty = i;
139     } else {
140       int pos = Hash(keys_[i]) & mask_;
141       if (pos <= last_empty || pos > i) {
142         // Evacuate an entry that is in the wrong place.
143         reinsert.push_back(std::pair<Object*, void*>(keys_[i], values_[i]));
144         keys_[i] = not_mapped;
145         values_[i] = nullptr;
146         last_empty = i;
147       }
148     }
149   }
150   // Reinsert all the key/value pairs that were in the wrong place.
151   for (auto pair : reinsert) {
152     int index = InsertIndex(pair.first);
153     DCHECK_GE(index, 0);
154     DCHECK_NE(heap_->not_mapped_symbol(), values_[index]);
155     values_[index] = pair.second;
156   }
157 }
158 
159 
Resize()160 void IdentityMapBase::Resize() {
161   // Grow the internal storage and reinsert all the key/value pairs.
162   int old_size = size_;
163   Object** old_keys = keys_;
164   void** old_values = values_;
165 
166   size_ = size_ * kResizeFactor;
167   mask_ = size_ - 1;
168   gc_counter_ = heap_->gc_count();
169 
170   CHECK_LE(size_, (1024 * 1024 * 16));  // that would be extreme...
171 
172   keys_ = zone_->NewArray<Object*>(size_);
173   Object* not_mapped = heap_->not_mapped_symbol();
174   for (int i = 0; i < size_; i++) keys_[i] = not_mapped;
175   values_ = zone_->NewArray<void*>(size_);
176   memset(values_, 0, sizeof(void*) * size_);
177 
178   for (int i = 0; i < old_size; i++) {
179     if (old_keys[i] == not_mapped) continue;
180     int index = InsertIndex(old_keys[i]);
181     DCHECK_GE(index, 0);
182     values_[index] = old_values[i];
183   }
184 
185   // Unregister old keys and register new keys.
186   heap_->UnregisterStrongRoots(old_keys);
187   heap_->RegisterStrongRoots(keys_, keys_ + size_);
188 }
189 }  // namespace internal
190 }  // namespace v8
191