From: titzer Date: Wed, 6 May 2015 12:40:21 +0000 (-0700) Subject: Implement IdentityMap, a robust, GC-safe object-identity HashMap. X-Git-Tag: upstream/4.7.83~2783 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6d26ec0b4c74b77a3c8079291e9bbc4bb8998aed;p=platform%2Fupstream%2Fv8.git Implement IdentityMap, a robust, GC-safe object-identity HashMap. R=hpayer@chromium.org, erikcorry@chromium.org BUG= Review URL: https://codereview.chromium.org/1105693002 Cr-Commit-Position: refs/heads/master@{#28257} --- diff --git a/BUILD.gn b/BUILD.gn index 9b52661..a26f708 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -783,6 +783,8 @@ source_set("v8_base") { "src/heap/heap-inl.h", "src/heap/heap.cc", "src/heap/heap.h", + "src/heap/identity-map.cc", + "src/heap/identity-map.h", "src/heap/incremental-marking.cc", "src/heap/incremental-marking.h", "src/heap/mark-compact-inl.h", diff --git a/src/heap/heap.cc b/src/heap/heap.cc index e5095c8..6a1aa33 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -54,6 +54,13 @@ namespace v8 { namespace internal { +struct Heap::StrongRootsList { + Object** start; + Object** end; + StrongRootsList* next; +}; + + Heap::Heap() : amount_of_external_allocated_memory_(0), amount_of_external_allocated_memory_at_last_global_gc_(0), @@ -141,7 +148,8 @@ Heap::Heap() chunks_queued_for_free_(NULL), gc_callbacks_depth_(0), deserialization_complete_(false), - concurrent_sweeping_enabled_(false) { + concurrent_sweeping_enabled_(false), + strong_roots_list_(NULL) { // Allow build-time customization of the max semispace size. Building // V8 with snapshots and a non-default max semispace size is much // easier if you can define it as part of the build environment. @@ -5027,6 +5035,12 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { isolate_->thread_manager()->Iterate(v); v->Synchronize(VisitorSynchronization::kThreadManager); + // Iterate over other strong roots (currently only identity maps). + for (StrongRootsList* list = strong_roots_list_; list; list = list->next) { + v->VisitPointers(list->start, list->end); + } + v->Synchronize(VisitorSynchronization::kStrongRoots); + // Iterate over the pointers the Serialization/Deserialization code is // holding. // During garbage collection this keeps the partial snapshot cache alive. @@ -5541,6 +5555,13 @@ void Heap::TearDown() { store_buffer()->TearDown(); isolate_->memory_allocator()->TearDown(); + + StrongRootsList* next = NULL; + for (StrongRootsList* list = strong_roots_list_; list; list = next) { + next = list->next; + delete list; + } + strong_roots_list_ = NULL; } @@ -6439,5 +6460,34 @@ void Heap::CheckpointObjectStats() { MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_)); ClearObjectStats(); } + + +void Heap::RegisterStrongRoots(Object** start, Object** end) { + StrongRootsList* list = new StrongRootsList(); + list->next = strong_roots_list_; + list->start = start; + list->end = end; + strong_roots_list_ = list; +} + + +void Heap::UnregisterStrongRoots(Object** start) { + StrongRootsList* prev = NULL; + StrongRootsList* list = strong_roots_list_; + while (list != nullptr) { + StrongRootsList* next = list->next; + if (list->start == start) { + if (prev) { + prev->next = next; + } else { + strong_roots_list_ = next; + } + delete list; + } else { + prev = list; + } + list = next; + } +} } } // namespace v8::internal diff --git a/src/heap/heap.h b/src/heap/heap.h index 71ee26e..689f14e 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -1446,21 +1446,42 @@ class Heap { void TraceObjectStat(const char* name, int count, int size, double time); void CheckpointObjectStats(); - // We don't use a LockGuard here since we want to lock the heap - // only when FLAG_concurrent_recompilation is true. + void RegisterStrongRoots(Object** start, Object** end); + void UnregisterStrongRoots(Object** start); + + // Taking this lock prevents the GC from entering a phase that relocates + // object references. class RelocationLock { public: explicit RelocationLock(Heap* heap) : heap_(heap) { heap_->relocation_mutex_.Lock(); } - ~RelocationLock() { heap_->relocation_mutex_.Unlock(); } private: Heap* heap_; }; + // An optional version of the above lock that can be used for some critical + // sections on the mutator thread; only safe since the GC currently does not + // do concurrent compaction. + class OptionalRelocationLock { + public: + OptionalRelocationLock(Heap* heap, bool concurrent) + : heap_(heap), concurrent_(concurrent) { + if (concurrent_) heap_->relocation_mutex_.Lock(); + } + + ~OptionalRelocationLock() { + if (concurrent_) heap_->relocation_mutex_.Unlock(); + } + + private: + Heap* heap_; + bool concurrent_; + }; + void AddWeakObjectToCodeDependency(Handle obj, Handle dep); @@ -2154,6 +2175,9 @@ class Heap { std::map live_array_buffers_; std::map not_yet_discovered_array_buffers_; + struct StrongRootsList; + StrongRootsList* strong_roots_list_; + friend class AlwaysAllocateScope; friend class Deserializer; friend class Factory; diff --git a/src/heap/identity-map.cc b/src/heap/identity-map.cc new file mode 100644 index 0000000..e968989 --- /dev/null +++ b/src/heap/identity-map.cc @@ -0,0 +1,191 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/v8.h" + +#include "src/heap/heap.h" +#include "src/heap/identity-map.h" + +namespace v8 { +namespace internal { + +static const int kInitialIdentityMapSize = 4; +static const int kResizeFactor = 4; + +IdentityMapBase::~IdentityMapBase() { + if (keys_) { + Heap::OptionalRelocationLock lock(heap_, concurrent_); + heap_->UnregisterStrongRoots(keys_); + } +} + + +IdentityMapBase::RawEntry IdentityMapBase::Lookup(Handle key) { + AllowHandleDereference for_lookup; + int index = LookupIndex(*key); + return index >= 0 ? &values_[index] : nullptr; +} + + +IdentityMapBase::RawEntry IdentityMapBase::Insert(Handle key) { + AllowHandleDereference for_lookup; + int index = InsertIndex(*key); + DCHECK_GE(index, 0); + return &values_[index]; +} + + +int IdentityMapBase::Hash(Object* address) { + uintptr_t raw_address = reinterpret_cast(address); + CHECK_NE(0, raw_address); // Cannot store Smi 0 as a key here, sorry. + // Xor some of the upper bits, since the lower 2 or 3 are usually aligned. + return static_cast((raw_address >> 11) ^ raw_address); +} + + +int IdentityMapBase::LookupIndex(Object* address) { + int start = Hash(address) & mask_; + for (int index = start; index < size_; index++) { + if (keys_[index] == address) return index; // Found. + if (keys_[index] == nullptr) return -1; // Not found. + } + for (int index = 0; index < start; index++) { + if (keys_[index] == address) return index; // Found. + if (keys_[index] == nullptr) return -1; // Not found. + } + return -1; +} + + +int IdentityMapBase::InsertIndex(Object* address) { + while (true) { + int start = Hash(address) & mask_; + int limit = size_ / 2; + // Search up to {limit} entries. + for (int index = start; --limit > 0; index = (index + 1) & mask_) { + if (keys_[index] == address) return index; // Found. + if (keys_[index] == nullptr) { // Free entry. + keys_[index] = address; + return index; + } + } + Resize(); // Should only have to resize once, since we grow 4x. + } + UNREACHABLE(); + return -1; +} + + +// Searches this map for the given key using the object's address +// as the identity, returning: +// found => a pointer to the storage location for the value +// not found => a pointer to a new storage location for the value +IdentityMapBase::RawEntry IdentityMapBase::GetEntry(Handle key) { + Heap::OptionalRelocationLock lock(heap_, concurrent_); + RawEntry result; + if (size_ == 0) { + // Allocate the initial storage for keys and values. + size_ = kInitialIdentityMapSize; + mask_ = kInitialIdentityMapSize - 1; + gc_counter_ = heap_->gc_count(); + + keys_ = zone_->NewArray(size_); + memset(keys_, 0, sizeof(Object*) * size_); + values_ = zone_->NewArray(size_); + memset(values_, 0, sizeof(void*) * size_); + + heap_->RegisterStrongRoots(keys_, keys_ + size_); + result = Insert(key); + } else { + // Perform an optimistic lookup. + result = Lookup(key); + if (result == nullptr) { + // Miss; rehash if there was a GC, then insert. + if (gc_counter_ != heap_->gc_count()) Rehash(); + result = Insert(key); + } + } + return result; +} + + +// Searches this map for the given key using the object's address +// as the identity, returning: +// found => a pointer to the storage location for the value +// not found => {nullptr} +IdentityMapBase::RawEntry IdentityMapBase::FindEntry(Handle key) { + if (size_ == 0) return nullptr; + + Heap::OptionalRelocationLock lock(heap_, concurrent_); + RawEntry result = Lookup(key); + if (result == nullptr && gc_counter_ != heap_->gc_count()) { + Rehash(); // Rehash is expensive, so only do it in case of a miss. + result = Lookup(key); + } + return result; +} + + +void IdentityMapBase::Rehash() { + // Record the current GC counter. + gc_counter_ = heap_->gc_count(); + // Assume that most objects won't be moved. + ZoneVector> reinsert(zone_); + // Search the table looking for keys that wouldn't be found with their + // current hashcode and evacuate them. + int last_empty = -1; + for (int i = 0; i < size_; i++) { + if (keys_[i] == nullptr) { + last_empty = i; + } else { + int pos = Hash(keys_[i]) & mask_; + if (pos <= last_empty || pos > i) { + // Evacuate an entry that is in the wrong place. + reinsert.push_back(std::pair(keys_[i], values_[i])); + keys_[i] = nullptr; + values_[i] = nullptr; + last_empty = i; + } + } + } + // Reinsert all the key/value pairs that were in the wrong place. + for (auto pair : reinsert) { + int index = InsertIndex(pair.first); + DCHECK_GE(index, 0); + DCHECK_NULL(values_[index]); + values_[index] = pair.second; + } +} + + +void IdentityMapBase::Resize() { + // Grow the internal storage and reinsert all the key/value pairs. + int old_size = size_; + Object** old_keys = keys_; + void** old_values = values_; + + size_ = size_ * kResizeFactor; + mask_ = size_ - 1; + gc_counter_ = heap_->gc_count(); + + CHECK_LE(size_, (1024 * 1024 * 16)); // that would be extreme... + + keys_ = zone_->NewArray(size_); + memset(keys_, 0, sizeof(Object*) * size_); + values_ = zone_->NewArray(size_); + memset(values_, 0, sizeof(void*) * size_); + + for (int i = 0; i < old_size; i++) { + if (old_keys[i] == nullptr) continue; + int index = InsertIndex(old_keys[i]); + DCHECK_GE(index, 0); + values_[index] = old_values[i]; + } + + // Unregister old keys and register new keys. + heap_->UnregisterStrongRoots(old_keys); + heap_->RegisterStrongRoots(keys_, keys_ + size_); +} +} +} // namespace v8::internal diff --git a/src/heap/identity-map.h b/src/heap/identity-map.h new file mode 100644 index 0000000..5c754bc --- /dev/null +++ b/src/heap/identity-map.h @@ -0,0 +1,96 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_HEAP_IDENTITY_MAP_H_ +#define V8_HEAP_IDENTITY_MAP_H_ + +#include "src/handles.h" + +namespace v8 { +namespace internal { + +class Heap; + +// Base class of identity maps contains shared code for all template +// instantions. +class IdentityMapBase { + public: + // Enable or disable concurrent mode for this map. Concurrent mode implies + // taking the heap's relocation lock during most operations. + void SetConcurrent(bool concurrent) { concurrent_ = concurrent; } + + protected: + // Allow Tester to access internals, including changing the address of objects + // within the {keys_} array in order to simulate a moving GC. + friend class IdentityMapTester; + + typedef void** RawEntry; + + IdentityMapBase(Heap* heap, Zone* zone) + : heap_(heap), + zone_(zone), + concurrent_(false), + gc_counter_(-1), + size_(0), + mask_(0), + keys_(nullptr), + values_(nullptr) {} + ~IdentityMapBase(); + + RawEntry GetEntry(Handle key); + RawEntry FindEntry(Handle key); + + private: + // Internal implementation should not be called directly by subclasses. + int LookupIndex(Object* address); + int InsertIndex(Object* address); + void Rehash(); + void Resize(); + RawEntry Lookup(Handle key); + RawEntry Insert(Handle key); + int Hash(Object* address); + + Heap* heap_; + Zone* zone_; + bool concurrent_; + int gc_counter_; + int size_; + int mask_; + Object** keys_; + void** values_; +}; + +// Implements an identity map from object addresses to a given value type {V}. +// The map is robust w.r.t. garbage collection by synchronization with the +// supplied {heap}. +// * Keys are treated as strong roots. +// * SMIs are valid keys, except SMI #0. +// * The value type {V} must be reinterpret_cast'able to {void*} +// * The value type {V} must not be a heap type. +template +class IdentityMap : public IdentityMapBase { + public: + IdentityMap(Heap* heap, Zone* zone) : IdentityMapBase(heap, zone) {} + + // Searches this map for the given key using the object's address + // as the identity, returning: + // found => a pointer to the storage location for the value + // not found => a pointer to a new storage location for the value + V* Get(Handle key) { return reinterpret_cast(GetEntry(key)); } + + // Searches this map for the given key using the object's address + // as the identity, returning: + // found => a pointer to the storage location for the value + // not found => {nullptr} + V* Find(Handle key) { return reinterpret_cast(FindEntry(key)); } + + // Set the value for the given key. + void Set(Handle key, V value) { + *(reinterpret_cast(GetEntry(key))) = value; + } +}; +} +} // namespace v8::internal + +#endif // V8_HEAP_IDENTITY_MAP_H_ diff --git a/src/objects.h b/src/objects.h index fa7ffcb..f56bdb2 100644 --- a/src/objects.h +++ b/src/objects.h @@ -10981,22 +10981,23 @@ class BreakPointInfo: public Struct { #undef DECLARE_CAST #undef DECLARE_VERIFIER -#define VISITOR_SYNCHRONIZATION_TAGS_LIST(V) \ - V(kStringTable, "string_table", "(Internalized strings)") \ +#define VISITOR_SYNCHRONIZATION_TAGS_LIST(V) \ + V(kStringTable, "string_table", "(Internalized strings)") \ V(kExternalStringsTable, "external_strings_table", "(External strings)") \ - V(kStrongRootList, "strong_root_list", "(Strong roots)") \ - V(kSmiRootList, "smi_root_list", "(Smi roots)") \ - V(kInternalizedString, "internalized_string", "(Internal string)") \ - V(kBootstrapper, "bootstrapper", "(Bootstrapper)") \ - V(kTop, "top", "(Isolate)") \ - V(kRelocatable, "relocatable", "(Relocatable)") \ - V(kDebug, "debug", "(Debugger)") \ - V(kCompilationCache, "compilationcache", "(Compilation cache)") \ - V(kHandleScope, "handlescope", "(Handle scope)") \ - V(kBuiltins, "builtins", "(Builtins)") \ - V(kGlobalHandles, "globalhandles", "(Global handles)") \ - V(kEternalHandles, "eternalhandles", "(Eternal handles)") \ - V(kThreadManager, "threadmanager", "(Thread manager)") \ + V(kStrongRootList, "strong_root_list", "(Strong roots)") \ + V(kSmiRootList, "smi_root_list", "(Smi roots)") \ + V(kInternalizedString, "internalized_string", "(Internal string)") \ + V(kBootstrapper, "bootstrapper", "(Bootstrapper)") \ + V(kTop, "top", "(Isolate)") \ + V(kRelocatable, "relocatable", "(Relocatable)") \ + V(kDebug, "debug", "(Debugger)") \ + V(kCompilationCache, "compilationcache", "(Compilation cache)") \ + V(kHandleScope, "handlescope", "(Handle scope)") \ + V(kBuiltins, "builtins", "(Builtins)") \ + V(kGlobalHandles, "globalhandles", "(Global handles)") \ + V(kEternalHandles, "eternalhandles", "(Eternal handles)") \ + V(kThreadManager, "threadmanager", "(Thread manager)") \ + V(kStrongRoots, "strong roots", "(Strong roots)") \ V(kExtensions, "Extensions", "(Extensions)") class VisitorSynchronization : public AllStatic { diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 9b71ace..276ebd2 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -128,6 +128,7 @@ 'test-heap.cc', 'test-heap-profiler.cc', 'test-hydrogen-types.cc', + 'test-identity-map.cc', 'test-list.cc', 'test-liveedit.cc', 'test-lockers.cc', diff --git a/test/cctest/test-identity-map.cc b/test/cctest/test-identity-map.cc new file mode 100644 index 0000000..787fda7 --- /dev/null +++ b/test/cctest/test-identity-map.cc @@ -0,0 +1,339 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/v8.h" + +#include "src/heap/identity-map.h" +#include "src/zone.h" + +#include "test/cctest/cctest.h" + +namespace v8 { +namespace internal { + +// Helper for testing. A "friend" of the IdentityMapBase class, it is able to +// "move" objects to simulate GC for testing the internals of the map. +class IdentityMapTester : public HandleAndZoneScope { + public: + IdentityMap map; + + IdentityMapTester() : map(heap(), main_zone()) {} + + Heap* heap() { return isolate()->heap(); } + Isolate* isolate() { return main_isolate(); } + + void TestGetFind(Handle key1, void* val1, Handle key2, + void* val2) { + CHECK_NULL(map.Find(key1)); + CHECK_NULL(map.Find(key2)); + + // Set {key1} the first time. + void** entry = map.Get(key1); + CHECK_NOT_NULL(entry); + *entry = val1; + + for (int i = 0; i < 3; i++) { // Get and find {key1} K times. + { + void** nentry = map.Get(key1); + CHECK_EQ(entry, nentry); + CHECK_EQ(val1, *nentry); + CHECK_NULL(map.Find(key2)); + } + { + void** nentry = map.Find(key1); + CHECK_EQ(entry, nentry); + CHECK_EQ(val1, *nentry); + CHECK_NULL(map.Find(key2)); + } + } + + // Set {key2} the first time. + void** entry2 = map.Get(key2); + CHECK_NOT_NULL(entry2); + *entry2 = val2; + + for (int i = 0; i < 3; i++) { // Get and find {key1} and {key2} K times. + { + void** nentry = map.Get(key2); + CHECK_EQ(entry2, nentry); + CHECK_EQ(val2, *nentry); + } + { + void** nentry = map.Find(key2); + CHECK_EQ(entry2, nentry); + CHECK_EQ(val2, *nentry); + } + { + void** nentry = map.Find(key1); + CHECK_EQ(val1, *nentry); + } + } + } + + Handle smi(int value) { + return Handle(Smi::FromInt(value), isolate()); + } + + Handle num(double value) { + return isolate()->factory()->NewNumber(value); + } + + void SimulateGCByIncrementingSmisBy(int shift) { + for (int i = 0; i < map.size_; i++) { + if (map.keys_[i]->IsSmi()) { + map.keys_[i] = Smi::FromInt(Smi::cast(map.keys_[i])->value() + shift); + } + } + map.gc_counter_ = -1; + } + + void CheckFind(Handle key, void* value) { + void** entry = map.Find(key); + CHECK_NOT_NULL(entry); + CHECK_EQ(value, *entry); + } + + void CheckGet(Handle key, void* value) { + void** entry = map.Get(key); + CHECK_NOT_NULL(entry); + CHECK_EQ(value, *entry); + } + + void PrintMap() { + PrintF("{\n"); + for (int i = 0; i < map.size_; i++) { + PrintF(" %3d: %p => %p\n", i, reinterpret_cast(map.keys_[i]), + reinterpret_cast(map.values_[i])); + } + PrintF("}\n"); + } + + void Resize() { map.Resize(); } + + void Rehash() { map.Rehash(); } +}; + + +TEST(Find_smi_not_found) { + IdentityMapTester t; + for (int i = 0; i < 100; i++) { + CHECK_NULL(t.map.Find(t.smi(i))); + } +} + + +TEST(Find_num_not_found) { + IdentityMapTester t; + for (int i = 0; i < 100; i++) { + CHECK_NULL(t.map.Find(t.num(i + 0.2))); + } +} + + +TEST(GetFind_smi_13) { + IdentityMapTester t; + t.TestGetFind(t.smi(13), t.isolate(), t.smi(17), t.heap()); +} + + +TEST(GetFind_num_13) { + IdentityMapTester t; + t.TestGetFind(t.num(13.1), t.isolate(), t.num(17.1), t.heap()); +} + + +TEST(GetFind_smi_17m) { + const int kInterval = 17; + const int kShift = 1099; + IdentityMapTester t; + + for (int i = 1; i < 100; i += kInterval) { + t.map.Set(t.smi(i), reinterpret_cast(i + kShift)); + } + + for (int i = 1; i < 100; i += kInterval) { + t.CheckFind(t.smi(i), reinterpret_cast(i + kShift)); + } + + for (int i = 1; i < 100; i += kInterval) { + t.CheckGet(t.smi(i), reinterpret_cast(i + kShift)); + } + + for (int i = 1; i < 100; i++) { + void** entry = t.map.Find(t.smi(i)); + if ((i % kInterval) != 1) { + CHECK_NULL(entry); + } else { + CHECK_NOT_NULL(entry); + CHECK_EQ(reinterpret_cast(i + kShift), *entry); + } + } +} + + +TEST(GetFind_num_1000) { + const int kPrime = 137; + IdentityMapTester t; + int val1; + int val2; + + for (int i = 1; i < 1000; i++) { + t.TestGetFind(t.smi(i * kPrime), &val1, t.smi(i * kPrime + 1), &val2); + } +} + + +TEST(GetFind_smi_gc) { + const int kKey = 33; + const int kShift = 1211; + IdentityMapTester t; + + t.map.Set(t.smi(kKey), &t); + t.SimulateGCByIncrementingSmisBy(kShift); + t.CheckFind(t.smi(kKey + kShift), &t); + t.CheckGet(t.smi(kKey + kShift), &t); +} + + +TEST(GetFind_smi_gc2) { + int kKey1 = 1; + int kKey2 = 33; + const int kShift = 1211; + IdentityMapTester t; + + t.map.Set(t.smi(kKey1), &kKey1); + t.map.Set(t.smi(kKey2), &kKey2); + t.SimulateGCByIncrementingSmisBy(kShift); + t.CheckFind(t.smi(kKey1 + kShift), &kKey1); + t.CheckGet(t.smi(kKey1 + kShift), &kKey1); + t.CheckFind(t.smi(kKey2 + kShift), &kKey2); + t.CheckGet(t.smi(kKey2 + kShift), &kKey2); +} + + +TEST(GetFind_smi_gc_n) { + const int kShift = 12011; + IdentityMapTester t; + int keys[12] = {1, 2, 7, 8, 15, 23, + 1 + 32, 2 + 32, 7 + 32, 8 + 32, 15 + 32, 23 + 32}; + // Initialize the map first. + for (size_t i = 0; i < arraysize(keys); i += 2) { + t.TestGetFind(t.smi(keys[i]), &keys[i], t.smi(keys[i + 1]), &keys[i + 1]); + } + // Check the above initialization. + for (size_t i = 0; i < arraysize(keys); i++) { + t.CheckFind(t.smi(keys[i]), &keys[i]); + } + // Simulate a GC by "moving" the smis in the internal keys array. + t.SimulateGCByIncrementingSmisBy(kShift); + // Check that searching for the incremented smis finds the same values. + for (size_t i = 0; i < arraysize(keys); i++) { + t.CheckFind(t.smi(keys[i] + kShift), &keys[i]); + } + // Check that searching for the incremented smis gets the same values. + for (size_t i = 0; i < arraysize(keys); i++) { + t.CheckGet(t.smi(keys[i] + kShift), &keys[i]); + } +} + + +TEST(GetFind_smi_num_gc_n) { + const int kShift = 12019; + IdentityMapTester t; + int smi_keys[] = {1, 2, 7, 15, 23}; + Handle num_keys[] = {t.num(1.1), t.num(2.2), t.num(3.3), t.num(4.4), + t.num(5.5), t.num(6.6), t.num(7.7), t.num(8.8), + t.num(9.9), t.num(10.1)}; + // Initialize the map first. + for (size_t i = 0; i < arraysize(smi_keys); i++) { + t.map.Set(t.smi(smi_keys[i]), &smi_keys[i]); + } + for (size_t i = 0; i < arraysize(num_keys); i++) { + t.map.Set(num_keys[i], &num_keys[i]); + } + // Check the above initialization. + for (size_t i = 0; i < arraysize(smi_keys); i++) { + t.CheckFind(t.smi(smi_keys[i]), &smi_keys[i]); + } + for (size_t i = 0; i < arraysize(num_keys); i++) { + t.CheckFind(num_keys[i], &num_keys[i]); + } + + // Simulate a GC by moving SMIs. + // Ironically the SMIs "move", but the heap numbers don't! + t.SimulateGCByIncrementingSmisBy(kShift); + + // Check that searching for the incremented smis finds the same values. + for (size_t i = 0; i < arraysize(smi_keys); i++) { + t.CheckFind(t.smi(smi_keys[i] + kShift), &smi_keys[i]); + t.CheckGet(t.smi(smi_keys[i] + kShift), &smi_keys[i]); + } + + // Check that searching for the numbers finds the same values. + for (size_t i = 0; i < arraysize(num_keys); i++) { + t.CheckFind(num_keys[i], &num_keys[i]); + t.CheckGet(num_keys[i], &num_keys[i]); + } +} + + +void CollisionTest(int stride, bool rehash = false, bool resize = false) { + for (int load = 15; load <= 120; load = load * 2) { + IdentityMapTester t; + + { // Add entries to the map. + HandleScope scope(t.isolate()); + int next = 1; + for (int i = 0; i < load; i++) { + t.map.Set(t.smi(next), reinterpret_cast(next)); + t.CheckFind(t.smi(next), reinterpret_cast(next)); + next = next + stride; + } + } + if (resize) t.Resize(); // Explicit resize (internal method). + if (rehash) t.Rehash(); // Explicit rehash (internal method). + { // Check find and get. + HandleScope scope(t.isolate()); + int next = 1; + for (int i = 0; i < load; i++) { + t.CheckFind(t.smi(next), reinterpret_cast(next)); + t.CheckGet(t.smi(next), reinterpret_cast(next)); + next = next + stride; + } + } + } +} + + +TEST(Collisions_1) { CollisionTest(1); } +TEST(Collisions_2) { CollisionTest(2); } +TEST(Collisions_3) { CollisionTest(3); } +TEST(Collisions_5) { CollisionTest(5); } +TEST(Collisions_7) { CollisionTest(7); } +TEST(Resize) { CollisionTest(9, false, true); } +TEST(Rehash) { CollisionTest(11, true, false); } + + +TEST(ExplicitGC) { + IdentityMapTester t; + Handle num_keys[] = {t.num(2.1), t.num(2.4), t.num(3.3), t.num(4.3), + t.num(7.5), t.num(6.4), t.num(7.3), t.num(8.3), + t.num(8.9), t.num(10.4)}; + + // Insert some objects that should be in new space. + for (size_t i = 0; i < arraysize(num_keys); i++) { + t.map.Set(num_keys[i], &num_keys[i]); + } + + // Do an explicit, real GC. + t.heap()->CollectGarbage(i::NEW_SPACE); + + // Check that searching for the numbers finds the same values. + for (size_t i = 0; i < arraysize(num_keys); i++) { + t.CheckFind(num_keys[i], &num_keys[i]); + t.CheckGet(num_keys[i], &num_keys[i]); + } +} +} +} diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 288c294..b3db3cf 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -659,6 +659,8 @@ '../../src/heap/heap-inl.h', '../../src/heap/heap.cc', '../../src/heap/heap.h', + '../../src/heap/identity-map.cc', + '../../src/heap/identity-map.h', '../../src/heap/incremental-marking-inl.h', '../../src/heap/incremental-marking.cc', '../../src/heap/incremental-marking.h',