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