Implement IdentityMap<V>, 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}
This commit is contained in:
titzer 2015-05-06 05:40:21 -07:00 committed by Commit bot
parent 272818d7ff
commit 6d26ec0b4c
9 changed files with 725 additions and 19 deletions

View File

@ -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",

View File

@ -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

View File

@ -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<HeapObject> obj,
Handle<DependentCode> dep);
@ -2154,6 +2175,9 @@ class Heap {
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;

191
src/heap/identity-map.cc Normal file
View File

@ -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<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

96
src/heap/identity-map.h Normal file
View File

@ -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<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_

View File

@ -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 {

View File

@ -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',

View File

@ -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<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]);
}
}
}
}

View File

@ -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',