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:
parent
272818d7ff
commit
6d26ec0b4c
2
BUILD.gn
2
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",
|
||||
|
@ -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
|
||||
|
@ -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
191
src/heap/identity-map.cc
Normal 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
96
src/heap/identity-map.h
Normal 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_
|
@ -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 {
|
||||
|
@ -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',
|
||||
|
339
test/cctest/test-identity-map.cc
Normal file
339
test/cctest/test-identity-map.cc
Normal 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user