[identity-map] Remove double-lookups in IdentityMap
Remove the pattern of calling 'Find' followed by 'Set' for IdentityMap, with a single 'FindOrInsert' that explicitly returns whether an existing entry was found, or the entry was inserted. This replaces 'Get', which would return either an initialised or uninitialised entry (and callers would rely on default initialisation to check this). Also replace 'Set' with 'Insert', which explicitly requires that the element didn't exist before. This matches expectations where it was used (where those weren't replaced wholesale with 'FindOrInsert'), and makes the naming consistent with 'FindOrInsert'. Change-Id: I8fb76f4ac14fb92b88474965aafb1ace5fb79145 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2443135 Auto-Submit: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Dominik Inführ <dinfuehr@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Commit-Queue: Dominik Inführ <dinfuehr@chromium.org> Commit-Queue: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#70300}
This commit is contained in:
parent
9c0e6274e3
commit
4e7c99abda
@ -46,15 +46,12 @@ uint32_t BuiltinsConstantsTableBuilder::AddObject(Handle<Object> object) {
|
||||
DCHECK(!object->IsCode());
|
||||
#endif
|
||||
|
||||
uint32_t* maybe_key = map_.Find(object);
|
||||
if (maybe_key == nullptr) {
|
||||
auto find_result = map_.FindOrInsert(object);
|
||||
if (!find_result.already_exists) {
|
||||
DCHECK(object->IsHeapObject());
|
||||
uint32_t index = map_.size();
|
||||
map_.Set(object, index);
|
||||
return index;
|
||||
} else {
|
||||
return *maybe_key;
|
||||
*find_result.entry = map_.size() - 1;
|
||||
}
|
||||
return *find_result.entry;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -85,7 +82,7 @@ void BuiltinsConstantsTableBuilder::PatchSelfReference(
|
||||
uint32_t key;
|
||||
if (map_.Delete(self_reference, &key)) {
|
||||
DCHECK(code_object->IsCode());
|
||||
map_.Set(code_object, key);
|
||||
map_.Insert(code_object, key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +93,7 @@ void BuiltinsConstantsTableBuilder::PatchBasicBlockCountersReference(
|
||||
uint32_t key;
|
||||
if (map_.Delete(ReadOnlyRoots(isolate_).basic_block_counters_marker(),
|
||||
&key)) {
|
||||
map_.Set(counters, key);
|
||||
map_.Insert(counters, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ void CompilerDispatcher::RegisterSharedFunctionInfo(
|
||||
auto job_it = jobs_.find(job_id);
|
||||
DCHECK_NE(job_it, jobs_.end());
|
||||
Job* job = job_it->second.get();
|
||||
shared_to_unoptimized_job_id_.Set(function_handle, job_id);
|
||||
shared_to_unoptimized_job_id_.Insert(function_handle, job_id);
|
||||
|
||||
{
|
||||
base::MutexGuard lock(&mutex_);
|
||||
|
@ -2441,7 +2441,7 @@ void JSHeapBroker::CopyCanonicalHandlesForTesting(
|
||||
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
|
||||
Address* entry = *it.entry();
|
||||
Object key = it.key();
|
||||
canonical_handles_->Set(key, entry);
|
||||
canonical_handles_->Insert(key, entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,13 +250,13 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
|
||||
}
|
||||
|
||||
Object obj(address);
|
||||
Address** entry = canonical_handles_->Get(obj);
|
||||
if (*entry == nullptr) {
|
||||
auto find_result = canonical_handles_->FindOrInsert(obj);
|
||||
if (!find_result.already_exists) {
|
||||
// Allocate new PersistentHandle if one wasn't created before.
|
||||
DCHECK(local_heap_);
|
||||
*entry = local_heap_->NewPersistentHandle(obj).location();
|
||||
*find_result.entry = local_heap_->NewPersistentHandle(obj).location();
|
||||
}
|
||||
return Handle<T>(*entry);
|
||||
return Handle<T>(*find_result.entry);
|
||||
} else {
|
||||
return Handle<T>(object, isolate());
|
||||
}
|
||||
|
@ -175,12 +175,12 @@ Address* CanonicalHandleScope::Lookup(Address object) {
|
||||
return isolate_->root_handle(root_index).location();
|
||||
}
|
||||
}
|
||||
Address** entry = identity_map_->Get(Object(object));
|
||||
if (*entry == nullptr) {
|
||||
auto find_result = identity_map_->FindOrInsert(Object(object));
|
||||
if (!find_result.already_exists) {
|
||||
// Allocate new handle location.
|
||||
*entry = HandleScope::CreateHandle(isolate_, object);
|
||||
*find_result.entry = HandleScope::CreateHandle(isolate_, object);
|
||||
}
|
||||
return *entry;
|
||||
return *find_result.entry;
|
||||
}
|
||||
|
||||
std::unique_ptr<CanonicalHandlesMap>
|
||||
|
@ -384,7 +384,7 @@ void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
|
||||
Handle<JSArrayBuffer> array_buffer) {
|
||||
DCHECK(!array_buffer_transfer_map_.Find(array_buffer));
|
||||
DCHECK(!array_buffer->is_shared());
|
||||
array_buffer_transfer_map_.Set(array_buffer, transfer_id);
|
||||
array_buffer_transfer_map_.Insert(array_buffer, transfer_id);
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
|
||||
@ -500,16 +500,16 @@ void ValueSerializer::WriteString(Handle<String> string) {
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
|
||||
// If the object has already been serialized, just write its ID.
|
||||
uint32_t* id_map_entry = id_map_.Get(receiver);
|
||||
if (uint32_t id = *id_map_entry) {
|
||||
auto find_result = id_map_.FindOrInsert(receiver);
|
||||
if (find_result.already_exists) {
|
||||
WriteTag(SerializationTag::kObjectReference);
|
||||
WriteVarint(id - 1);
|
||||
WriteVarint(*find_result.entry - 1);
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
// Otherwise, allocate an ID for it.
|
||||
uint32_t id = next_id_++;
|
||||
*id_map_entry = id + 1;
|
||||
*find_result.entry = id + 1;
|
||||
|
||||
// Eliminate callable and exotic objects, which should not be serialized.
|
||||
InstanceType instance_type = receiver->map().instance_type();
|
||||
|
@ -61,7 +61,7 @@ void ReadOnlySerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
|
||||
CHECK_NULL(serialized_objects_.Find(obj));
|
||||
// There's no "IdentitySet", so use an IdentityMap with a value that is
|
||||
// later ignored.
|
||||
serialized_objects_.Set(obj, 0);
|
||||
serialized_objects_.Insert(obj, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ class SerializerReferenceMap {
|
||||
|
||||
void Add(HeapObject object, SerializerReference reference) {
|
||||
DCHECK_NULL(LookupReference(object));
|
||||
map_.Set(object, reference);
|
||||
map_.Insert(object, reference);
|
||||
}
|
||||
|
||||
void AddBackingStore(void* backing_store, SerializerReference reference) {
|
||||
@ -132,7 +132,7 @@ class SerializerReferenceMap {
|
||||
SerializerReference AddAttachedReference(HeapObject object) {
|
||||
SerializerReference reference =
|
||||
SerializerReference::AttachedReference(attached_reference_index_++);
|
||||
map_.Set(object, reference);
|
||||
map_.Insert(object, reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
|
@ -298,21 +298,16 @@ void Serializer::ResolvePendingForwardReference(int forward_reference_id) {
|
||||
void Serializer::RegisterObjectIsPending(Handle<HeapObject> obj) {
|
||||
if (*obj == ReadOnlyRoots(isolate()).not_mapped_symbol()) return;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool already_exists = forward_refs_per_pending_object_.Find(obj) != nullptr;
|
||||
#endif
|
||||
|
||||
// Add the given object to the pending objects -> forward refs map.
|
||||
PendingObjectReferences* refs = forward_refs_per_pending_object_.Get(obj);
|
||||
|
||||
DCHECK_EQ(already_exists, *refs != nullptr);
|
||||
USE(refs);
|
||||
auto find_result = forward_refs_per_pending_object_.FindOrInsert(obj);
|
||||
USE(find_result);
|
||||
|
||||
// If the above emplace didn't actually add the object, then the object must
|
||||
// already have been registered pending by deferring. It might not be in the
|
||||
// deferred objects queue though, since it may be the very object we just
|
||||
// popped off that queue, so just check that it can be deferred.
|
||||
DCHECK_IMPLIES(already_exists, CanBeDeferred(*obj));
|
||||
DCHECK_IMPLIES(find_result.already_exists, *find_result.entry != nullptr);
|
||||
DCHECK_IMPLIES(find_result.already_exists, CanBeDeferred(*obj));
|
||||
}
|
||||
|
||||
void Serializer::ResolvePendingObject(Handle<HeapObject> obj) {
|
||||
|
@ -139,14 +139,12 @@ class ObjectCacheIndexMap {
|
||||
// map and return false. In either case set |*index_out| to the index
|
||||
// associated with the map.
|
||||
bool LookupOrInsert(Handle<HeapObject> obj, int* index_out) {
|
||||
int* maybe_index = map_.Find(obj);
|
||||
if (maybe_index) {
|
||||
*index_out = *maybe_index;
|
||||
return true;
|
||||
auto find_result = map_.FindOrInsert(obj);
|
||||
if (!find_result.already_exists) {
|
||||
*find_result.entry = next_index_++;
|
||||
}
|
||||
*index_out = next_index_;
|
||||
map_.Set(obj, next_index_++);
|
||||
return false;
|
||||
*index_out = *find_result.entry;
|
||||
return find_result.already_exists;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -61,19 +61,19 @@ int IdentityMapBase::ScanKeysFor(Address address) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int IdentityMapBase::InsertKey(Address address) {
|
||||
std::pair<int, bool> IdentityMapBase::InsertKey(Address address) {
|
||||
Address not_mapped = ReadOnlyRoots(heap_).not_mapped_symbol().ptr();
|
||||
while (true) {
|
||||
int start = Hash(address) & mask_;
|
||||
int limit = capacity_ / 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] == address) return {index, true}; // Found.
|
||||
if (keys_[index] == not_mapped) { // Free entry.
|
||||
size_++;
|
||||
DCHECK_LE(size_, capacity_);
|
||||
keys_[index] = address;
|
||||
return index;
|
||||
return {index, false};
|
||||
}
|
||||
}
|
||||
// Should only have to resize once, since we grow 4x.
|
||||
@ -132,16 +132,19 @@ int IdentityMapBase::Lookup(Address key) const {
|
||||
return index;
|
||||
}
|
||||
|
||||
int IdentityMapBase::LookupOrInsert(Address key) {
|
||||
std::pair<int, bool> IdentityMapBase::LookupOrInsert(Address key) {
|
||||
// Perform an optimistic lookup.
|
||||
int index = ScanKeysFor(key);
|
||||
bool already_exists;
|
||||
if (index < 0) {
|
||||
// Miss; rehash if there was a GC, then insert.
|
||||
if (gc_counter_ != heap_->gc_count()) Rehash();
|
||||
index = InsertKey(key);
|
||||
std::tie(index, already_exists) = InsertKey(key);
|
||||
} else {
|
||||
already_exists = true;
|
||||
}
|
||||
DCHECK_GE(index, 0);
|
||||
return index;
|
||||
return {index, already_exists};
|
||||
}
|
||||
|
||||
int IdentityMapBase::Hash(Address address) const {
|
||||
@ -151,10 +154,35 @@ int IdentityMapBase::Hash(Address address) const {
|
||||
|
||||
// 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(Address key) {
|
||||
// found => a pointer to the storage location for the value, true
|
||||
// not found => a pointer to a new storage location for the value, false
|
||||
IdentityMapFindResult<uintptr_t> IdentityMapBase::FindOrInsertEntry(
|
||||
Address key) {
|
||||
CHECK(!is_iterable()); // Don't allow insertion while iterable.
|
||||
if (capacity_ == 0) {
|
||||
return {InsertEntry(key), false};
|
||||
}
|
||||
auto lookup_result = LookupOrInsert(key);
|
||||
return {&values_[lookup_result.first], lookup_result.second};
|
||||
}
|
||||
|
||||
// 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(Address key) const {
|
||||
// Don't allow find by key while iterable (might rehash).
|
||||
CHECK(!is_iterable());
|
||||
if (size_ == 0) return nullptr;
|
||||
int index = Lookup(key);
|
||||
return index >= 0 ? &values_[index] : nullptr;
|
||||
}
|
||||
|
||||
// Inserts the given key using the object's address as the identity, returning
|
||||
// a pointer to the new storage location for the value.
|
||||
IdentityMapBase::RawEntry IdentityMapBase::InsertEntry(Address key) {
|
||||
// Don't allow find by key while iterable (might rehash).
|
||||
CHECK(!is_iterable());
|
||||
if (capacity_ == 0) {
|
||||
// Allocate the initial storage for keys and values.
|
||||
capacity_ = kInitialIdentityMapSize;
|
||||
@ -169,22 +197,16 @@ IdentityMapBase::RawEntry IdentityMapBase::GetEntry(Address key) {
|
||||
|
||||
strong_roots_entry_ = heap_->RegisterStrongRoots(
|
||||
FullObjectSlot(keys_), FullObjectSlot(keys_ + capacity_));
|
||||
} else {
|
||||
// Rehash if there was a GC, then insert.
|
||||
if (gc_counter_ != heap_->gc_count()) Rehash();
|
||||
}
|
||||
int index = LookupOrInsert(key);
|
||||
return &values_[index];
|
||||
}
|
||||
|
||||
// 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(Address key) const {
|
||||
// Don't allow find by key while iterable (might rehash).
|
||||
CHECK(!is_iterable());
|
||||
if (size_ == 0) return nullptr;
|
||||
// Remove constness since lookup might have to rehash.
|
||||
int index = Lookup(key);
|
||||
return index >= 0 ? &values_[index] : nullptr;
|
||||
int index;
|
||||
bool already_exists;
|
||||
std::tie(index, already_exists) = InsertKey(key);
|
||||
DCHECK(!already_exists);
|
||||
return &values_[index];
|
||||
}
|
||||
|
||||
// Deletes the given key from the map using the object's address as the
|
||||
@ -254,7 +276,7 @@ void IdentityMapBase::Rehash() {
|
||||
}
|
||||
// Reinsert all the key/value pairs that were in the wrong place.
|
||||
for (auto pair : reinsert) {
|
||||
int index = InsertKey(pair.first);
|
||||
int index = InsertKey(pair.first).first;
|
||||
DCHECK_GE(index, 0);
|
||||
values_[index] = pair.second;
|
||||
}
|
||||
@ -281,7 +303,7 @@ void IdentityMapBase::Resize(int new_capacity) {
|
||||
|
||||
for (int i = 0; i < old_capacity; i++) {
|
||||
if (old_keys[i] == not_mapped) continue;
|
||||
int index = InsertKey(old_keys[i]);
|
||||
int index = InsertKey(old_keys[i]).first;
|
||||
DCHECK_GE(index, 0);
|
||||
values_[index] = old_values[i];
|
||||
}
|
||||
|
@ -18,6 +18,12 @@ namespace internal {
|
||||
class Heap;
|
||||
class StrongRootsEntry;
|
||||
|
||||
template <typename T>
|
||||
struct IdentityMapFindResult {
|
||||
T* entry;
|
||||
bool already_exists;
|
||||
};
|
||||
|
||||
// Base class of identity maps contains shared code for all template
|
||||
// instantions.
|
||||
class V8_EXPORT_PRIVATE IdentityMapBase {
|
||||
@ -46,8 +52,9 @@ class V8_EXPORT_PRIVATE IdentityMapBase {
|
||||
is_iterable_(false) {}
|
||||
virtual ~IdentityMapBase();
|
||||
|
||||
RawEntry GetEntry(Address key);
|
||||
IdentityMapFindResult<uintptr_t> FindOrInsertEntry(Address key);
|
||||
RawEntry FindEntry(Address key) const;
|
||||
RawEntry InsertEntry(Address key);
|
||||
bool DeleteEntry(Address key, uintptr_t* deleted_value);
|
||||
void Clear();
|
||||
|
||||
@ -65,9 +72,9 @@ class V8_EXPORT_PRIVATE IdentityMapBase {
|
||||
private:
|
||||
// Internal implementation should not be called directly by subclasses.
|
||||
int ScanKeysFor(Address address) const;
|
||||
int InsertKey(Address address);
|
||||
std::pair<int, bool> InsertKey(Address address);
|
||||
int Lookup(Address key) const;
|
||||
int LookupOrInsert(Address key);
|
||||
std::pair<int, bool> LookupOrInsert(Address key);
|
||||
bool DeleteIndex(int index, uintptr_t* deleted_value);
|
||||
void Rehash();
|
||||
void Resize(int new_capacity);
|
||||
@ -107,10 +114,15 @@ class IdentityMap : public IdentityMapBase {
|
||||
|
||||
// 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 Get(*key); }
|
||||
V* Get(Object key) { return reinterpret_cast<V*>(GetEntry(key.ptr())); }
|
||||
// found => a pointer to the storage location for the value, true
|
||||
// not found => a pointer to a new storage location for the value, false
|
||||
IdentityMapFindResult<V> FindOrInsert(Handle<Object> key) {
|
||||
return FindOrInsert(*key);
|
||||
}
|
||||
IdentityMapFindResult<V> FindOrInsert(Object key) {
|
||||
auto raw = FindOrInsertEntry(key.ptr());
|
||||
return {reinterpret_cast<V*>(raw.entry), raw.already_exists};
|
||||
}
|
||||
|
||||
// Searches this map for the given key using the object's address
|
||||
// as the identity, returning:
|
||||
@ -121,10 +133,11 @@ class IdentityMap : public IdentityMapBase {
|
||||
return reinterpret_cast<V*>(FindEntry(key.ptr()));
|
||||
}
|
||||
|
||||
// Set the value for the given key.
|
||||
void Set(Handle<Object> key, V v) { Set(*key, v); }
|
||||
void Set(Object key, V v) {
|
||||
*(reinterpret_cast<V*>(GetEntry(key.ptr()))) = v;
|
||||
// Insert the value for the given key. The key must not have previously
|
||||
// existed.
|
||||
void Insert(Handle<Object> key, V v) { Insert(*key, v); }
|
||||
void Insert(Object key, V v) {
|
||||
*reinterpret_cast<V*>(InsertEntry(key.ptr())) = v;
|
||||
}
|
||||
|
||||
bool Delete(Handle<Object> key, V* deleted_value) {
|
||||
|
@ -26,45 +26,48 @@ class IdentityMapTester : public HandleAndZoneScope {
|
||||
Heap* heap() { return isolate()->heap(); }
|
||||
Isolate* isolate() { return main_isolate(); }
|
||||
|
||||
void TestGetFind(Handle<Object> key1, void* val1, Handle<Object> key2,
|
||||
void* val2) {
|
||||
void TestInsertFind(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;
|
||||
auto find_result = map.FindOrInsert(key1);
|
||||
CHECK_NOT_NULL(find_result.entry);
|
||||
CHECK(!find_result.already_exists);
|
||||
*find_result.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);
|
||||
auto new_find_result = map.FindOrInsert(key1);
|
||||
CHECK(new_find_result.already_exists);
|
||||
CHECK_EQ(find_result.entry, new_find_result.entry);
|
||||
CHECK_EQ(val1, *new_find_result.entry);
|
||||
CHECK_NULL(map.Find(key2));
|
||||
}
|
||||
{
|
||||
void** nentry = map.Find(key1);
|
||||
CHECK_EQ(entry, nentry);
|
||||
CHECK_EQ(find_result.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;
|
||||
auto find_result2 = map.FindOrInsert(key2);
|
||||
CHECK_NOT_NULL(find_result2.entry);
|
||||
CHECK(!find_result2.already_exists);
|
||||
*find_result2.entry = 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);
|
||||
auto new_find_result = map.FindOrInsert(key2);
|
||||
CHECK_EQ(find_result2.entry, new_find_result.entry);
|
||||
CHECK_EQ(val2, *new_find_result.entry);
|
||||
}
|
||||
{
|
||||
void** nentry = map.Find(key2);
|
||||
CHECK_EQ(entry2, nentry);
|
||||
CHECK_EQ(find_result2.entry, nentry);
|
||||
CHECK_EQ(val2, *nentry);
|
||||
}
|
||||
{
|
||||
@ -80,12 +83,14 @@ class IdentityMapTester : public HandleAndZoneScope {
|
||||
CHECK_NULL(map.Find(key2));
|
||||
|
||||
// Set {key1} and {key2} for the first time.
|
||||
void** entry1 = map.Get(key1);
|
||||
CHECK_NOT_NULL(entry1);
|
||||
*entry1 = val1;
|
||||
void** entry2 = map.Get(key2);
|
||||
CHECK_NOT_NULL(entry2);
|
||||
*entry2 = val2;
|
||||
auto find_result1 = map.FindOrInsert(key1);
|
||||
CHECK(!find_result1.already_exists);
|
||||
CHECK_NOT_NULL(find_result1.entry);
|
||||
*find_result1.entry = val1;
|
||||
auto find_result2 = map.FindOrInsert(key2);
|
||||
CHECK(!find_result1.already_exists);
|
||||
CHECK_NOT_NULL(find_result2.entry);
|
||||
*find_result2.entry = val2;
|
||||
|
||||
for (int i = 0; i < 3; i++) { // Find {key1} and {key2} 3 times.
|
||||
{
|
||||
@ -157,10 +162,11 @@ class IdentityMapTester : public HandleAndZoneScope {
|
||||
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 CheckFindOrInsert(Handle<Object> key, void* value) {
|
||||
auto find_result = map.FindOrInsert(key);
|
||||
CHECK(find_result.already_exists);
|
||||
CHECK_NOT_NULL(find_result.entry);
|
||||
CHECK_EQ(value, *find_result.entry);
|
||||
}
|
||||
|
||||
void CheckDelete(Handle<Object> key, void* value) {
|
||||
@ -219,17 +225,17 @@ TEST(Delete_num_not_found) {
|
||||
|
||||
TEST(GetFind_smi_0) {
|
||||
IdentityMapTester t;
|
||||
t.TestGetFind(t.smi(0), t.isolate(), t.smi(1), t.heap());
|
||||
t.TestInsertFind(t.smi(0), t.isolate(), t.smi(1), t.heap());
|
||||
}
|
||||
|
||||
TEST(GetFind_smi_13) {
|
||||
IdentityMapTester t;
|
||||
t.TestGetFind(t.smi(13), t.isolate(), t.smi(17), t.heap());
|
||||
t.TestInsertFind(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());
|
||||
t.TestInsertFind(t.num(13.1), t.isolate(), t.num(17.1), t.heap());
|
||||
}
|
||||
|
||||
TEST(Delete_smi_13) {
|
||||
@ -250,7 +256,7 @@ TEST(GetFind_smi_17m) {
|
||||
IdentityMapTester t;
|
||||
|
||||
for (int i = 1; i < 100; i += kInterval) {
|
||||
t.map.Set(t.smi(i), reinterpret_cast<void*>(i + kShift));
|
||||
t.map.Insert(t.smi(i), reinterpret_cast<void*>(i + kShift));
|
||||
}
|
||||
|
||||
for (int i = 1; i < 100; i += kInterval) {
|
||||
@ -258,7 +264,7 @@ TEST(GetFind_smi_17m) {
|
||||
}
|
||||
|
||||
for (int i = 1; i < 100; i += kInterval) {
|
||||
t.CheckGet(t.smi(i), reinterpret_cast<void*>(i + kShift));
|
||||
t.CheckFindOrInsert(t.smi(i), reinterpret_cast<void*>(i + kShift));
|
||||
}
|
||||
|
||||
for (int i = 1; i < 100; i++) {
|
||||
@ -278,7 +284,7 @@ TEST(Delete_smi_17m) {
|
||||
IdentityMapTester t;
|
||||
|
||||
for (int i = 1; i < 100; i += kInterval) {
|
||||
t.map.Set(t.smi(i), reinterpret_cast<void*>(i + kShift));
|
||||
t.map.Insert(t.smi(i), reinterpret_cast<void*>(i + kShift));
|
||||
}
|
||||
|
||||
for (int i = 1; i < 100; i += kInterval) {
|
||||
@ -288,7 +294,7 @@ TEST(Delete_smi_17m) {
|
||||
for (int i = 1; i < 100; i += kInterval) {
|
||||
t.CheckDelete(t.smi(i), reinterpret_cast<void*>(i + kShift));
|
||||
for (int j = 1; j < 100; j += kInterval) {
|
||||
void** entry = t.map.Find(t.smi(j));
|
||||
auto entry = t.map.Find(t.smi(j));
|
||||
if (j <= i) {
|
||||
CHECK_NULL(entry);
|
||||
} else {
|
||||
@ -306,7 +312,7 @@ TEST(GetFind_num_1000) {
|
||||
int val2;
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
t.TestGetFind(t.smi(i * kPrime), &val1, t.smi(i * kPrime + 1), &val2);
|
||||
t.TestInsertFind(t.smi(i * kPrime), &val1, t.smi(i * kPrime + 1), &val2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,7 +321,7 @@ TEST(Delete_num_1000) {
|
||||
IdentityMapTester t;
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
t.map.Set(t.smi(i * kPrime), reinterpret_cast<void*>(i * kPrime));
|
||||
t.map.Insert(t.smi(i * kPrime), reinterpret_cast<void*>(i * kPrime));
|
||||
}
|
||||
|
||||
// Delete every second value in reverse.
|
||||
@ -326,7 +332,7 @@ TEST(Delete_num_1000) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
void** entry = t.map.Find(t.smi(i * kPrime));
|
||||
auto entry = t.map.Find(t.smi(i * kPrime));
|
||||
if (i % 2) {
|
||||
CHECK_NULL(entry);
|
||||
} else {
|
||||
@ -343,7 +349,7 @@ TEST(Delete_num_1000) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
void** entry = t.map.Find(t.smi(i * kPrime));
|
||||
auto entry = t.map.Find(t.smi(i * kPrime));
|
||||
CHECK_NULL(entry);
|
||||
}
|
||||
}
|
||||
@ -353,10 +359,10 @@ TEST(GetFind_smi_gc) {
|
||||
const int kShift = 1211;
|
||||
IdentityMapTester t;
|
||||
|
||||
t.map.Set(t.smi(kKey), &t);
|
||||
t.map.Insert(t.smi(kKey), &t);
|
||||
t.SimulateGCByIncrementingSmisBy(kShift);
|
||||
t.CheckFind(t.smi(kKey + kShift), &t);
|
||||
t.CheckGet(t.smi(kKey + kShift), &t);
|
||||
t.CheckFindOrInsert(t.smi(kKey + kShift), &t);
|
||||
}
|
||||
|
||||
TEST(Delete_smi_gc) {
|
||||
@ -364,7 +370,7 @@ TEST(Delete_smi_gc) {
|
||||
const int kShift = 1211;
|
||||
IdentityMapTester t;
|
||||
|
||||
t.map.Set(t.smi(kKey), &t);
|
||||
t.map.Insert(t.smi(kKey), &t);
|
||||
t.SimulateGCByIncrementingSmisBy(kShift);
|
||||
t.CheckDelete(t.smi(kKey + kShift), &t);
|
||||
}
|
||||
@ -375,13 +381,13 @@ TEST(GetFind_smi_gc2) {
|
||||
const int kShift = 1211;
|
||||
IdentityMapTester t;
|
||||
|
||||
t.map.Set(t.smi(kKey1), &kKey1);
|
||||
t.map.Set(t.smi(kKey2), &kKey2);
|
||||
t.map.Insert(t.smi(kKey1), &kKey1);
|
||||
t.map.Insert(t.smi(kKey2), &kKey2);
|
||||
t.SimulateGCByIncrementingSmisBy(kShift);
|
||||
t.CheckFind(t.smi(kKey1 + kShift), &kKey1);
|
||||
t.CheckGet(t.smi(kKey1 + kShift), &kKey1);
|
||||
t.CheckFindOrInsert(t.smi(kKey1 + kShift), &kKey1);
|
||||
t.CheckFind(t.smi(kKey2 + kShift), &kKey2);
|
||||
t.CheckGet(t.smi(kKey2 + kShift), &kKey2);
|
||||
t.CheckFindOrInsert(t.smi(kKey2 + kShift), &kKey2);
|
||||
}
|
||||
|
||||
TEST(Delete_smi_gc2) {
|
||||
@ -390,8 +396,8 @@ TEST(Delete_smi_gc2) {
|
||||
const int kShift = 1211;
|
||||
IdentityMapTester t;
|
||||
|
||||
t.map.Set(t.smi(kKey1), &kKey1);
|
||||
t.map.Set(t.smi(kKey2), &kKey2);
|
||||
t.map.Insert(t.smi(kKey1), &kKey1);
|
||||
t.map.Insert(t.smi(kKey2), &kKey2);
|
||||
t.SimulateGCByIncrementingSmisBy(kShift);
|
||||
t.CheckDelete(t.smi(kKey1 + kShift), &kKey1);
|
||||
t.CheckDelete(t.smi(kKey2 + kShift), &kKey2);
|
||||
@ -404,7 +410,8 @@ TEST(GetFind_smi_gc_n) {
|
||||
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]);
|
||||
t.TestInsertFind(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++) {
|
||||
@ -418,7 +425,7 @@ TEST(GetFind_smi_gc_n) {
|
||||
}
|
||||
// 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]);
|
||||
t.CheckFindOrInsert(t.smi(keys[i] + kShift), &keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,7 +436,7 @@ TEST(Delete_smi_gc_n) {
|
||||
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++) {
|
||||
t.map.Set(t.smi(keys[i]), &keys[i]);
|
||||
t.map.Insert(t.smi(keys[i]), &keys[i]);
|
||||
}
|
||||
// Simulate a GC by "moving" the smis in the internal keys array.
|
||||
t.SimulateGCByIncrementingSmisBy(kShift);
|
||||
@ -448,10 +455,10 @@ TEST(GetFind_smi_num_gc_n) {
|
||||
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]);
|
||||
t.map.Insert(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]);
|
||||
t.map.Insert(num_keys[i], &num_keys[i]);
|
||||
}
|
||||
// Check the above initialization.
|
||||
for (size_t i = 0; i < arraysize(smi_keys); i++) {
|
||||
@ -468,13 +475,13 @@ TEST(GetFind_smi_num_gc_n) {
|
||||
// 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]);
|
||||
t.CheckFindOrInsert(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]);
|
||||
t.CheckFindOrInsert(num_keys[i], &num_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,10 +494,10 @@ TEST(Delete_smi_num_gc_n) {
|
||||
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]);
|
||||
t.map.Insert(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]);
|
||||
t.map.Insert(num_keys[i], &num_keys[i]);
|
||||
}
|
||||
|
||||
// Simulate a GC by moving SMIs.
|
||||
@ -514,14 +521,14 @@ TEST(Delete_smi_resizes) {
|
||||
IdentityMapTester t;
|
||||
|
||||
// Insert one element to initialize map.
|
||||
t.map.Set(t.smi(0), reinterpret_cast<void*>(kValueOffset));
|
||||
t.map.Insert(t.smi(0), reinterpret_cast<void*>(kValueOffset));
|
||||
|
||||
int initial_capacity = t.map.capacity();
|
||||
CHECK_LT(initial_capacity, kKeyCount);
|
||||
|
||||
// Insert another kKeyCount - 1 keys.
|
||||
for (int i = 1; i < kKeyCount; i++) {
|
||||
t.map.Set(t.smi(i), reinterpret_cast<void*>(i + kValueOffset));
|
||||
t.map.Insert(t.smi(i), reinterpret_cast<void*>(i + kValueOffset));
|
||||
}
|
||||
|
||||
// Check capacity increased.
|
||||
@ -545,10 +552,10 @@ TEST(Iterator_smi_num) {
|
||||
t.num(9.9), t.num(10.1)};
|
||||
// Initialize the map.
|
||||
for (size_t i = 0; i < arraysize(smi_keys); i++) {
|
||||
t.map.Set(t.smi(smi_keys[i]), reinterpret_cast<void*>(i));
|
||||
t.map.Insert(t.smi(smi_keys[i]), reinterpret_cast<void*>(i));
|
||||
}
|
||||
for (size_t i = 0; i < arraysize(num_keys); i++) {
|
||||
t.map.Set(num_keys[i], reinterpret_cast<void*>(i + 5));
|
||||
t.map.Insert(num_keys[i], reinterpret_cast<void*>(i + 5));
|
||||
}
|
||||
|
||||
// Check iterator sees all values once.
|
||||
@ -574,10 +581,10 @@ TEST(Iterator_smi_num_gc) {
|
||||
t.num(9.9), t.num(10.1)};
|
||||
// Initialize the map.
|
||||
for (size_t i = 0; i < arraysize(smi_keys); i++) {
|
||||
t.map.Set(t.smi(smi_keys[i]), reinterpret_cast<void*>(i));
|
||||
t.map.Insert(t.smi(smi_keys[i]), reinterpret_cast<void*>(i));
|
||||
}
|
||||
for (size_t i = 0; i < arraysize(num_keys); i++) {
|
||||
t.map.Set(num_keys[i], reinterpret_cast<void*>(i + 5));
|
||||
t.map.Insert(num_keys[i], reinterpret_cast<void*>(i + 5));
|
||||
}
|
||||
|
||||
// Simulate GC by moving the SMIs.
|
||||
@ -605,7 +612,7 @@ void IterateCollisionTest(int stride) {
|
||||
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.map.Insert(t.smi(next), reinterpret_cast<void*>(next));
|
||||
t.CheckFind(t.smi(next), reinterpret_cast<void*>(next));
|
||||
next = next + stride;
|
||||
}
|
||||
@ -627,7 +634,7 @@ void IterateCollisionTest(int stride) {
|
||||
for (int i = 0; i < load; i++) {
|
||||
CHECK(seen.find(next) != seen.end());
|
||||
t.CheckFind(t.smi(next), reinterpret_cast<void*>(next));
|
||||
t.CheckGet(t.smi(next), reinterpret_cast<void*>(next));
|
||||
t.CheckFindOrInsert(t.smi(next), reinterpret_cast<void*>(next));
|
||||
next = next + stride;
|
||||
}
|
||||
}
|
||||
@ -648,7 +655,7 @@ void CollisionTest(int stride, bool rehash = false, bool resize = false) {
|
||||
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.map.Insert(t.smi(next), reinterpret_cast<void*>(next));
|
||||
t.CheckFind(t.smi(next), reinterpret_cast<void*>(next));
|
||||
next = next + stride;
|
||||
}
|
||||
@ -660,7 +667,7 @@ void CollisionTest(int stride, bool rehash = false, bool resize = false) {
|
||||
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));
|
||||
t.CheckFindOrInsert(t.smi(next), reinterpret_cast<void*>(next));
|
||||
next = next + stride;
|
||||
}
|
||||
}
|
||||
@ -683,7 +690,7 @@ TEST(ExplicitGC) {
|
||||
|
||||
// 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]);
|
||||
t.map.Insert(num_keys[i], &num_keys[i]);
|
||||
}
|
||||
|
||||
// Do an explicit, real GC.
|
||||
@ -692,7 +699,7 @@ TEST(ExplicitGC) {
|
||||
// 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]);
|
||||
t.CheckFindOrInsert(num_keys[i], &num_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,7 +797,7 @@ TEST(GCShortCutting) {
|
||||
// greater to capacity_ if not corrected by IdentityMap
|
||||
// (see crbug.com/704132).
|
||||
for (int j = 0; j < i; j++) {
|
||||
t.map.Set(t.smi(j), reinterpret_cast<void*>(kDummyValue));
|
||||
t.map.Insert(t.smi(j), reinterpret_cast<void*>(kDummyValue));
|
||||
}
|
||||
|
||||
Handle<String> thin_string =
|
||||
@ -801,8 +808,8 @@ TEST(GCShortCutting) {
|
||||
DCHECK_NE(*thin_string, *internalized_string);
|
||||
|
||||
// Insert both keys into the map.
|
||||
t.map.Set(thin_string, &thin_string);
|
||||
t.map.Set(internalized_string, &internalized_string);
|
||||
t.map.Insert(thin_string, &thin_string);
|
||||
t.map.Insert(internalized_string, &internalized_string);
|
||||
|
||||
// Do an explicit, real GC, this should short-cut the thin string to point
|
||||
// to the internalized string.
|
||||
@ -812,16 +819,16 @@ TEST(GCShortCutting) {
|
||||
*thin_string == *internalized_string);
|
||||
|
||||
// Check that getting the object points to one of the handles.
|
||||
void** thin_string_entry = t.map.Get(thin_string);
|
||||
void** thin_string_entry = t.map.Find(thin_string);
|
||||
CHECK(*thin_string_entry == &thin_string ||
|
||||
*thin_string_entry == &internalized_string);
|
||||
void** internalized_string_entry = t.map.Get(internalized_string);
|
||||
void** internalized_string_entry = t.map.Find(internalized_string);
|
||||
CHECK(*internalized_string_entry == &thin_string ||
|
||||
*internalized_string_entry == &internalized_string);
|
||||
|
||||
// Trigger resize.
|
||||
for (int j = 0; j < 16; j++) {
|
||||
t.map.Set(t.smi(j + 16), reinterpret_cast<void*>(kDummyValue));
|
||||
t.map.Insert(t.smi(j + 16), reinterpret_cast<void*>(kDummyValue));
|
||||
}
|
||||
t.map.Clear();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user