"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",
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),
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.
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.
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;
}
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
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<HeapObject> obj,
Handle<DependentCode> dep);
std::map<void*, size_t> live_array_buffers_;
std::map<void*, size_t> not_yet_discovered_array_buffers_;
+ struct StrongRootsList;
+ StrongRootsList* strong_roots_list_;
+
friend class AlwaysAllocateScope;
friend class Deserializer;
friend class Factory;
--- /dev/null
+// 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<Object> key) {
+ AllowHandleDereference for_lookup;
+ int index = LookupIndex(*key);
+ return index >= 0 ? &values_[index] : nullptr;
+}
+
+
+IdentityMapBase::RawEntry IdentityMapBase::Insert(Handle<Object> 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<uintptr_t>(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<int>((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<Object> 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<Object*>(size_);
+ memset(keys_, 0, sizeof(Object*) * size_);
+ values_ = zone_->NewArray<void*>(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<Object> 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<std::pair<Object*, void*>> 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<Object*, void*>(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<Object*>(size_);
+ memset(keys_, 0, sizeof(Object*) * size_);
+ values_ = zone_->NewArray<void*>(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
--- /dev/null
+// 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<Object> key);
+ RawEntry FindEntry(Handle<Object> 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<Object> key);
+ RawEntry Insert(Handle<Object> 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 <typename V>
+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<Object> key) { return reinterpret_cast<V*>(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<Object> key) { return reinterpret_cast<V*>(FindEntry(key)); }
+
+ // Set the value for the given key.
+ void Set(Handle<Object> key, V value) {
+ *(reinterpret_cast<V*>(GetEntry(key))) = value;
+ }
+};
+}
+} // namespace v8::internal
+
+#endif // V8_HEAP_IDENTITY_MAP_H_
#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 {
'test-heap.cc',
'test-heap-profiler.cc',
'test-hydrogen-types.cc',
+ 'test-identity-map.cc',
'test-list.cc',
'test-liveedit.cc',
'test-lockers.cc',
--- /dev/null
+// 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<void*> map;
+
+ IdentityMapTester() : map(heap(), main_zone()) {}
+
+ Heap* heap() { return isolate()->heap(); }
+ Isolate* isolate() { return main_isolate(); }
+
+ void TestGetFind(Handle<Object> key1, void* val1, Handle<Object> 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> smi(int value) {
+ return Handle<Smi>(Smi::FromInt(value), isolate());
+ }
+
+ Handle<Object> 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<Object> key, void* value) {
+ void** entry = map.Find(key);
+ CHECK_NOT_NULL(entry);
+ CHECK_EQ(value, *entry);
+ }
+
+ void CheckGet(Handle<Object> 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<void*>(map.keys_[i]),
+ reinterpret_cast<void*>(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<void*>(i + kShift));
+ }
+
+ for (int i = 1; i < 100; i += kInterval) {
+ t.CheckFind(t.smi(i), reinterpret_cast<void*>(i + kShift));
+ }
+
+ for (int i = 1; i < 100; i += kInterval) {
+ t.CheckGet(t.smi(i), reinterpret_cast<void*>(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<void*>(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<Object> 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<void*>(next));
+ t.CheckFind(t.smi(next), reinterpret_cast<void*>(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<void*>(next));
+ t.CheckGet(t.smi(next), reinterpret_cast<void*>(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<Object> 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]);
+ }
+}
+}
+}
'../../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',