v8/test/cctest/test-hashcode.cc
Frank Emrich 1b67ab4d3f [dict-proto] SwissNameDictionary rollout in runtime code, pt. 2
This CL is part of a series that makes SwissNameDictionary available
as a new property backing store. Currently, the flag
v8_dict_mode_prototypes allows selecting between NameDictionary and
OrderedNameDictionary as the backing store used for all dictionary
mode objects. This series of CLs changes this such that enabling the
flag causes SwissNameDictionary being used instead of
OrderedNameDictionary. The behavior for when the flag is not set
remains unchanged (= use NameDictionary).

This particular CL just collects many small changes.

Note that the changes this CL makes to literal-objects.cc do not
fix the problems with the enumeration order of computed property
names in classes that currently exist when using OrderedNameDictionary.
This will be fixed separately.

Bug: v8:11388
Change-Id: I6b98f61c395b4f2788407d6a34363ef8863cce9a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2735834
Commit-Queue: Frank Emrich <emrich@google.com>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73224}
2021-03-05 14:09:24 +00:00

327 lines
10 KiB
C++

// Copyright 2017 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 <stdlib.h>
#include <sstream>
#include <utility>
#include "src/init/v8.h"
#include "src/objects/objects-inl.h"
#include "src/objects/objects.h"
#include "src/objects/ordered-hash-table.h"
#include "src/third_party/siphash/halfsiphash.h"
#include "src/utils/utils.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
int AddToSetAndGetHash(Isolate* isolate, Handle<JSObject> obj,
bool has_fast_properties) {
CHECK_EQ(has_fast_properties, obj->HasFastProperties());
CHECK_EQ(ReadOnlyRoots(isolate).undefined_value(), obj->GetHash());
Handle<OrderedHashSet> set = isolate->factory()->NewOrderedHashSet();
OrderedHashSet::Add(isolate, set, obj);
CHECK_EQ(has_fast_properties, obj->HasFastProperties());
return Smi::ToInt(obj->GetHash());
}
int GetPropertyDictionaryHash(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
return obj->property_dictionary_swiss().Hash();
} else {
return obj->property_dictionary().Hash();
}
}
int GetPropertyDictionaryLength(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
return obj->property_dictionary_swiss().Capacity();
} else {
return obj->property_dictionary().length();
}
}
void CheckIsDictionaryModeObject(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
CHECK(obj->raw_properties_or_hash().IsSwissNameDictionary());
} else {
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
}
}
void CheckFastObject(Handle<JSObject> obj, int hash) {
CHECK(obj->HasFastProperties());
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
CHECK_EQ(hash, obj->property_array().Hash());
}
void CheckDictionaryObject(Handle<JSObject> obj, int hash) {
CHECK(!obj->HasFastProperties());
CheckIsDictionaryModeObject(obj);
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
}
TEST(AddHashCodeToFastObjectWithoutProperties) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
Handle<JSObject> obj =
isolate->factory()->NewJSObject(isolate->object_function());
CHECK(obj->HasFastProperties());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
}
TEST(AddHashCodeToFastObjectWithInObjectProperties) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source = " var x = { a: 1};";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK_EQ(ReadOnlyRoots(isolate).empty_fixed_array(),
obj->raw_properties_or_hash());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
}
TEST(AddHashCodeToFastObjectWithPropertiesArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = {}; "
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->HasFastProperties());
int hash = AddToSetAndGetHash(isolate, obj, true);
CheckFastObject(obj, hash);
}
TEST(AddHashCodeToSlowObject) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
Handle<JSObject> obj =
isolate->factory()->NewJSObject(isolate->object_function());
CHECK(obj->HasFastProperties());
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CheckDictionaryObject(obj, hash);
}
TEST(TransitionFastWithInObjectToFastWithPropertyArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = { };"
" x.a = 1; x.b = 2; x.c = 3; x.d = 4;";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->HasFastProperties());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
int length = obj->property_array().length();
CompileRun("x.e = 5;");
CHECK(obj->property_array().length() > length);
CheckFastObject(obj, hash);
}
TEST(TransitionFastWithPropertyArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = { };"
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(hash, obj->property_array().Hash());
int length = obj->property_array().length();
CompileRun("x.f = 2; x.g = 5; x.h = 2");
CHECK(obj->property_array().length() > length);
CheckFastObject(obj, hash);
}
TEST(TransitionFastWithPropertyArrayToSlow) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = { };"
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
CHECK_EQ(hash, obj->property_array().Hash());
JSObject::NormalizeProperties(isolate, obj, KEEP_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckDictionaryObject(obj, hash);
}
TEST(TransitionSlowToSlow) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source = " var x = {}; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
int length = GetPropertyDictionaryLength(obj);
CompileRun("for(var i = 0; i < 10; i++) { x['f'+i] = i };");
CHECK(GetPropertyDictionaryLength(obj) > length);
CheckDictionaryObject(obj, hash);
}
TEST(TransitionSlowToFastWithoutProperties) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
Handle<JSObject> obj =
isolate->factory()->NewJSObject(isolate->object_function());
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
}
TEST(TransitionSlowToFastWithPropertyArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = Object.create(null); "
" for(var i = 0; i < 10; i++) { x['f'+i] = i }; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
CheckFastObject(obj, hash);
}
namespace {
using HashFunction = uint32_t (*)(uint32_t key, uint64_t seed);
void TestIntegerHashQuality(const int samples_log2, int num_buckets_log2,
uint64_t seed, double max_var,
HashFunction hash_function) {
int samples = 1 << samples_log2;
int num_buckets = 1 << num_buckets_log2;
int mean = samples / num_buckets;
int* buckets = new int[num_buckets];
for (int i = 0; i < num_buckets; i++) buckets[i] = 0;
for (int i = 0; i < samples; i++) {
uint32_t hash = hash_function(i, seed);
buckets[hash % num_buckets]++;
}
int sum_deviation = 0;
for (int i = 0; i < num_buckets; i++) {
int deviation = abs(buckets[i] - mean);
sum_deviation += deviation * deviation;
}
delete[] buckets;
double variation_coefficient = sqrt(sum_deviation * 1.0 / num_buckets) / mean;
printf("samples: 1 << %2d, buckets: 1 << %2d, var_coeff: %0.3f\n",
samples_log2, num_buckets_log2, variation_coefficient);
CHECK_LT(variation_coefficient, max_var);
}
uint32_t HalfSipHash(uint32_t key, uint64_t seed) {
return halfsiphash(key, seed);
}
uint32_t JenkinsHash(uint32_t key, uint64_t seed) {
return ComputeLongHash(static_cast<uint64_t>(key) ^ seed);
}
uint32_t DefaultHash(uint32_t key, uint64_t seed) {
return ComputeSeededHash(key, seed);
}
} // anonymous namespace
void TestIntegerHashQuality(HashFunction hash_function) {
TestIntegerHashQuality(17, 13, 0x123456789ABCDEFU, 0.4, hash_function);
TestIntegerHashQuality(16, 12, 0x123456789ABCDEFU, 0.4, hash_function);
TestIntegerHashQuality(15, 11, 0xFEDCBA987654321U, 0.4, hash_function);
TestIntegerHashQuality(14, 10, 0xFEDCBA987654321U, 0.4, hash_function);
TestIntegerHashQuality(13, 9, 1, 0.4, hash_function);
TestIntegerHashQuality(12, 8, 1, 0.4, hash_function);
TestIntegerHashQuality(17, 10, 0x123456789ABCDEFU, 0.2, hash_function);
TestIntegerHashQuality(16, 9, 0x123456789ABCDEFU, 0.2, hash_function);
TestIntegerHashQuality(15, 8, 0xFEDCBA987654321U, 0.2, hash_function);
TestIntegerHashQuality(14, 7, 0xFEDCBA987654321U, 0.2, hash_function);
TestIntegerHashQuality(13, 6, 1, 0.2, hash_function);
TestIntegerHashQuality(12, 5, 1, 0.2, hash_function);
}
TEST(HalfSipHashQuality) { TestIntegerHashQuality(HalfSipHash); }
TEST(JenkinsHashQuality) { TestIntegerHashQuality(JenkinsHash); }
TEST(DefaultHashQuality) { TestIntegerHashQuality(DefaultHash); }
} // namespace internal
} // namespace v8