PageRenderTime 73ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/src/utils/identity-map.cc

https://github.com/v8/v8
C++ | 333 lines | 254 code | 40 blank | 39 comment | 63 complexity | b9be8b280625a3ad47f3b2c61851fe28 MD5 | raw file
Possible License(s): BSD-3-Clause, CC0-1.0, Apache-2.0
  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. #include "src/utils/identity-map.h"
  5. #include "src/base/functional.h"
  6. #include "src/base/logging.h"
  7. #include "src/heap/heap.h"
  8. #include "src/roots/roots-inl.h"
  9. namespace v8 {
  10. namespace internal {
  11. static const int kInitialIdentityMapSize = 4;
  12. static const int kResizeFactor = 2;
  13. IdentityMapBase::~IdentityMapBase() {
  14. // Clear must be called by the subclass to avoid calling the virtual
  15. // DeleteArray function from the destructor.
  16. DCHECK_NULL(keys_);
  17. }
  18. void IdentityMapBase::Clear() {
  19. if (keys_) {
  20. DCHECK(!is_iterable());
  21. DCHECK_NOT_NULL(strong_roots_entry_);
  22. heap_->UnregisterStrongRoots(strong_roots_entry_);
  23. DeletePointerArray(reinterpret_cast<uintptr_t*>(keys_), capacity_);
  24. DeletePointerArray(values_, capacity_);
  25. keys_ = nullptr;
  26. strong_roots_entry_ = nullptr;
  27. values_ = nullptr;
  28. size_ = 0;
  29. capacity_ = 0;
  30. mask_ = 0;
  31. }
  32. }
  33. void IdentityMapBase::EnableIteration() {
  34. CHECK(!is_iterable());
  35. is_iterable_ = true;
  36. }
  37. void IdentityMapBase::DisableIteration() {
  38. CHECK(is_iterable());
  39. is_iterable_ = false;
  40. }
  41. int IdentityMapBase::ScanKeysFor(Address address, uint32_t hash) const {
  42. int start = hash & mask_;
  43. Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
  44. for (int index = start; index < capacity_; index++) {
  45. if (keys_[index] == address) return index; // Found.
  46. if (keys_[index] == not_mapped) return -1; // Not found.
  47. }
  48. for (int index = 0; index < start; index++) {
  49. if (keys_[index] == address) return index; // Found.
  50. if (keys_[index] == not_mapped) return -1; // Not found.
  51. }
  52. return -1;
  53. }
  54. std::pair<int, bool> IdentityMapBase::InsertKey(Address address,
  55. uint32_t hash) {
  56. DCHECK_EQ(gc_counter_, heap_->gc_count());
  57. // Grow the map if we reached >= 80% occupancy.
  58. if (size_ + size_ / 4 >= capacity_) {
  59. Resize(capacity_ * kResizeFactor);
  60. }
  61. Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
  62. int start = hash & mask_;
  63. // Guaranteed to terminate since size_ < capacity_, there must be at least
  64. // one empty slot.
  65. int index = start;
  66. while (true) {
  67. if (keys_[index] == address) return {index, true}; // Found.
  68. if (keys_[index] == not_mapped) { // Free entry.
  69. size_++;
  70. DCHECK_LE(size_, capacity_);
  71. keys_[index] = address;
  72. return {index, false};
  73. }
  74. index = (index + 1) & mask_;
  75. // We should never loop back to the start.
  76. DCHECK_NE(index, start);
  77. }
  78. }
  79. bool IdentityMapBase::DeleteIndex(int index, uintptr_t* deleted_value) {
  80. if (deleted_value != nullptr) *deleted_value = values_[index];
  81. Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
  82. DCHECK_NE(keys_[index], not_mapped);
  83. keys_[index] = not_mapped;
  84. values_[index] = 0;
  85. size_--;
  86. DCHECK_GE(size_, 0);
  87. if (capacity_ > kInitialIdentityMapSize &&
  88. size_ * kResizeFactor < capacity_ / kResizeFactor) {
  89. Resize(capacity_ / kResizeFactor);
  90. return true; // No need to fix collisions as resize reinserts keys.
  91. }
  92. // Move any collisions to their new correct location.
  93. int next_index = index;
  94. for (;;) {
  95. next_index = (next_index + 1) & mask_;
  96. Address key = keys_[next_index];
  97. if (key == not_mapped) break;
  98. int expected_index = Hash(key) & mask_;
  99. if (index < next_index) {
  100. if (index < expected_index && expected_index <= next_index) continue;
  101. } else {
  102. DCHECK_GT(index, next_index);
  103. if (index < expected_index || expected_index <= next_index) continue;
  104. }
  105. DCHECK_EQ(not_mapped, keys_[index]);
  106. DCHECK_EQ(values_[index], 0);
  107. std::swap(keys_[index], keys_[next_index]);
  108. std::swap(values_[index], values_[next_index]);
  109. index = next_index;
  110. }
  111. return true;
  112. }
  113. int IdentityMapBase::Lookup(Address key) const {
  114. uint32_t hash = Hash(key);
  115. int index = ScanKeysFor(key, hash);
  116. if (index < 0 && gc_counter_ != heap_->gc_count()) {
  117. // Miss; rehash if there was a GC, then lookup again.
  118. const_cast<IdentityMapBase*>(this)->Rehash();
  119. index = ScanKeysFor(key, hash);
  120. }
  121. return index;
  122. }
  123. std::pair<int, bool> IdentityMapBase::LookupOrInsert(Address key) {
  124. uint32_t hash = Hash(key);
  125. // Perform an optimistic lookup.
  126. int index = ScanKeysFor(key, hash);
  127. bool already_exists;
  128. if (index < 0) {
  129. // Miss; rehash if there was a GC, then insert.
  130. if (gc_counter_ != heap_->gc_count()) Rehash();
  131. std::tie(index, already_exists) = InsertKey(key, hash);
  132. } else {
  133. already_exists = true;
  134. }
  135. DCHECK_GE(index, 0);
  136. return {index, already_exists};
  137. }
  138. uint32_t IdentityMapBase::Hash(Address address) const {
  139. CHECK_NE(address, ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
  140. return static_cast<uint32_t>(hasher_(address));
  141. }
  142. // Searches this map for the given key using the object's address
  143. // as the identity, returning:
  144. // found => a pointer to the storage location for the value, true
  145. // not found => a pointer to a new storage location for the value, false
  146. IdentityMapFindResult<uintptr_t> IdentityMapBase::FindOrInsertEntry(
  147. Address key) {
  148. CHECK(!is_iterable()); // Don't allow insertion while iterable.
  149. if (capacity_ == 0) {
  150. return {InsertEntry(key), false};
  151. }
  152. auto lookup_result = LookupOrInsert(key);
  153. return {&values_[lookup_result.first], lookup_result.second};
  154. }
  155. // Searches this map for the given key using the object's address
  156. // as the identity, returning:
  157. // found => a pointer to the storage location for the value
  158. // not found => {nullptr}
  159. IdentityMapBase::RawEntry IdentityMapBase::FindEntry(Address key) const {
  160. // Don't allow find by key while iterable (might rehash).
  161. CHECK(!is_iterable());
  162. if (size_ == 0) return nullptr;
  163. int index = Lookup(key);
  164. return index >= 0 ? &values_[index] : nullptr;
  165. }
  166. // Inserts the given key using the object's address as the identity, returning
  167. // a pointer to the new storage location for the value.
  168. IdentityMapBase::RawEntry IdentityMapBase::InsertEntry(Address key) {
  169. // Don't allow find by key while iterable (might rehash).
  170. CHECK(!is_iterable());
  171. if (capacity_ == 0) {
  172. // Allocate the initial storage for keys and values.
  173. capacity_ = kInitialIdentityMapSize;
  174. mask_ = kInitialIdentityMapSize - 1;
  175. gc_counter_ = heap_->gc_count();
  176. keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_));
  177. Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
  178. for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
  179. values_ = NewPointerArray(capacity_);
  180. memset(values_, 0, sizeof(uintptr_t) * capacity_);
  181. strong_roots_entry_ =
  182. heap_->RegisterStrongRoots("IdentityMapBase", FullObjectSlot(keys_),
  183. FullObjectSlot(keys_ + capacity_));
  184. } else {
  185. // Rehash if there was a GC, then insert.
  186. if (gc_counter_ != heap_->gc_count()) Rehash();
  187. }
  188. int index;
  189. bool already_exists;
  190. std::tie(index, already_exists) = InsertKey(key, Hash(key));
  191. DCHECK(!already_exists);
  192. return &values_[index];
  193. }
  194. // Deletes the given key from the map using the object's address as the
  195. // identity, returning true iff the key was found (in which case, the value
  196. // argument will be set to the deleted entry's value).
  197. bool IdentityMapBase::DeleteEntry(Address key, uintptr_t* deleted_value) {
  198. CHECK(!is_iterable()); // Don't allow deletion by key while iterable.
  199. if (size_ == 0) return false;
  200. int index = Lookup(key);
  201. if (index < 0) return false; // No entry found.
  202. return DeleteIndex(index, deleted_value);
  203. }
  204. Address IdentityMapBase::KeyAtIndex(int index) const {
  205. DCHECK_LE(0, index);
  206. DCHECK_LT(index, capacity_);
  207. DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
  208. CHECK(is_iterable()); // Must be iterable to access by index;
  209. return keys_[index];
  210. }
  211. IdentityMapBase::RawEntry IdentityMapBase::EntryAtIndex(int index) const {
  212. DCHECK_LE(0, index);
  213. DCHECK_LT(index, capacity_);
  214. DCHECK_NE(keys_[index], ReadOnlyRoots(heap_).not_mapped_symbol().ptr());
  215. CHECK(is_iterable()); // Must be iterable to access by index;
  216. return &values_[index];
  217. }
  218. int IdentityMapBase::NextIndex(int index) const {
  219. DCHECK_LE(-1, index);
  220. DCHECK_LE(index, capacity_);
  221. CHECK(is_iterable()); // Must be iterable to access by index;
  222. Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
  223. for (++index; index < capacity_; ++index) {
  224. if (keys_[index] != not_mapped) {
  225. return index;
  226. }
  227. }
  228. return capacity_;
  229. }
  230. void IdentityMapBase::Rehash() {
  231. CHECK(!is_iterable()); // Can't rehash while iterating.
  232. // Record the current GC counter.
  233. gc_counter_ = heap_->gc_count();
  234. // Assume that most objects won't be moved.
  235. std::vector<std::pair<Address, uintptr_t>> reinsert;
  236. // Search the table looking for keys that wouldn't be found with their
  237. // current hashcode and evacuate them.
  238. int last_empty = -1;
  239. Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
  240. for (int i = 0; i < capacity_; i++) {
  241. if (keys_[i] == not_mapped) {
  242. last_empty = i;
  243. } else {
  244. int pos = Hash(keys_[i]) & mask_;
  245. if (pos <= last_empty || pos > i) {
  246. // Evacuate an entry that is in the wrong place.
  247. reinsert.push_back(std::pair<Address, uintptr_t>(keys_[i], values_[i]));
  248. keys_[i] = not_mapped;
  249. values_[i] = 0;
  250. last_empty = i;
  251. size_--;
  252. }
  253. }
  254. }
  255. // Reinsert all the key/value pairs that were in the wrong place.
  256. for (auto pair : reinsert) {
  257. int index = InsertKey(pair.first, Hash(pair.first)).first;
  258. DCHECK_GE(index, 0);
  259. values_[index] = pair.second;
  260. }
  261. }
  262. void IdentityMapBase::Resize(int new_capacity) {
  263. CHECK(!is_iterable()); // Can't resize while iterating.
  264. // Resize the internal storage and reinsert all the key/value pairs.
  265. DCHECK_GT(new_capacity, size_);
  266. int old_capacity = capacity_;
  267. Address* old_keys = keys_;
  268. uintptr_t* old_values = values_;
  269. capacity_ = new_capacity;
  270. mask_ = capacity_ - 1;
  271. gc_counter_ = heap_->gc_count();
  272. size_ = 0;
  273. keys_ = reinterpret_cast<Address*>(NewPointerArray(capacity_));
  274. Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
  275. for (int i = 0; i < capacity_; i++) keys_[i] = not_mapped;
  276. values_ = NewPointerArray(capacity_);
  277. memset(values_, 0, sizeof(uintptr_t) * capacity_);
  278. for (int i = 0; i < old_capacity; i++) {
  279. if (old_keys[i] == not_mapped) continue;
  280. int index = InsertKey(old_keys[i], Hash(old_keys[i])).first;
  281. DCHECK_GE(index, 0);
  282. values_[index] = old_values[i];
  283. }
  284. // Unregister old keys and register new keys.
  285. DCHECK_NOT_NULL(strong_roots_entry_);
  286. heap_->UpdateStrongRoots(strong_roots_entry_, FullObjectSlot(keys_),
  287. FullObjectSlot(keys_ + capacity_));
  288. // Delete old storage;
  289. DeletePointerArray(reinterpret_cast<uintptr_t*>(old_keys), old_capacity);
  290. DeletePointerArray(old_values, old_capacity);
  291. }
  292. } // namespace internal
  293. } // namespace v8