Commit 518d67ad authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[runtime] Fix sorted order of DescriptorArray entries

... and add respective regression tests.

This CL also adds similar regression tests for TransitionArray but it
doesn't have the same issue as DescriptorArray.

Bug: chromium:1133527
Change-Id: I668a90f126d76af0a39816ce8697cb29bc65d01b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2465833Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70570}
parent f4376ec8
......@@ -1832,12 +1832,13 @@ TNode<IntPtrT> CodeStubAssembler::LoadJSReceiverIdentityHash(
return var_hash.value();
}
TNode<Uint32T> CodeStubAssembler::LoadNameHashField(SloppyTNode<Name> name) {
CSA_ASSERT(this, IsName(name));
return LoadObjectField<Uint32T>(name, Name::kHashFieldOffset);
TNode<Uint32T> CodeStubAssembler::LoadNameHashAssumeComputed(TNode<Name> name) {
TNode<Uint32T> hash_field = LoadNameHashField(name);
CSA_ASSERT(this, IsClearWord32(hash_field, Name::kHashNotComputedMask));
return Unsigned(Word32Shr(hash_field, Int32Constant(Name::kHashShift)));
}
TNode<Uint32T> CodeStubAssembler::LoadNameHash(SloppyTNode<Name> name,
TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name,
Label* if_hash_not_computed) {
TNode<Uint32T> hash_field = LoadNameHashField(name);
if (if_hash_not_computed != nullptr) {
......@@ -8110,7 +8111,7 @@ void CodeStubAssembler::LookupBinary(TNode<Name> unique_name,
TNode<Uint32T> limit =
Unsigned(Int32Sub(NumberOfEntries<Array>(array), Int32Constant(1)));
TVARIABLE(Uint32T, var_high, limit);
TNode<Uint32T> hash = LoadNameHashField(unique_name);
TNode<Uint32T> hash = LoadNameHashAssumeComputed(unique_name);
CSA_ASSERT(this, Word32NotEqual(hash, Int32Constant(0)));
// Assume non-empty array.
......@@ -8128,7 +8129,7 @@ void CodeStubAssembler::LookupBinary(TNode<Name> unique_name,
TNode<Uint32T> sorted_key_index = GetSortedKeyIndex<Array>(array, mid);
TNode<Name> mid_name = GetKey<Array>(array, sorted_key_index);
TNode<Uint32T> mid_hash = LoadNameHashField(mid_name);
TNode<Uint32T> mid_hash = LoadNameHashAssumeComputed(mid_name);
Label mid_greater(this), mid_less(this), merge(this);
Branch(Uint32GreaterThanOrEqual(mid_hash, hash), &mid_greater, &mid_less);
......@@ -8155,7 +8156,7 @@ void CodeStubAssembler::LookupBinary(TNode<Name> unique_name,
TNode<Uint32T> sort_index =
GetSortedKeyIndex<Array>(array, var_low.value());
TNode<Name> current_name = GetKey<Array>(array, sort_index);
TNode<Uint32T> current_hash = LoadNameHashField(current_name);
TNode<Uint32T> current_hash = LoadNameHashAssumeComputed(current_name);
GotoIf(Word32NotEqual(current_hash, hash), if_not_found);
Label next(this);
GotoIf(TaggedNotEqual(current_name, unique_name), &next);
......
......@@ -1268,13 +1268,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Check if the map is set for slow properties.
TNode<BoolT> IsDictionaryMap(SloppyTNode<Map> map);
// Load the hash field of a name as an uint32 value.
TNode<Uint32T> LoadNameHashField(SloppyTNode<Name> name);
// Load the hash value of a name as an uint32 value.
// Load the Name::hash() value of a name as an uint32 value.
// If {if_hash_not_computed} label is specified then it also checks if
// hash is actually computed.
TNode<Uint32T> LoadNameHash(SloppyTNode<Name> name,
TNode<Uint32T> LoadNameHash(TNode<Name> name,
Label* if_hash_not_computed = nullptr);
TNode<Uint32T> LoadNameHashAssumeComputed(TNode<Name> name);
// Load length field of a String object as Smi value.
TNode<Smi> LoadStringLengthAsSmi(TNode<String> string);
......
......@@ -1726,12 +1726,13 @@ bool DescriptorArray::IsSortedNoDuplicates() {
uint32_t current = 0;
for (int i = 0; i < number_of_descriptors(); i++) {
Name key = GetSortedKey(i);
CHECK(key.HasHashCode());
if (key == current_key) {
Print();
return false;
}
current_key = key;
uint32_t hash = GetSortedKey(i).Hash();
uint32_t hash = key.hash();
if (hash < current) {
Print();
return false;
......@@ -1749,7 +1750,8 @@ bool TransitionArray::IsSortedNoDuplicates() {
for (int i = 0; i < number_of_transitions(); i++) {
Name key = GetSortedKey(i);
uint32_t hash = key.Hash();
CHECK(key.HasHashCode());
uint32_t hash = key.hash();
PropertyKind kind = kData;
PropertyAttributes attributes = NONE;
if (!TransitionsAccessor::IsSpecialTransition(key.GetReadOnlyRoots(),
......
......@@ -227,7 +227,7 @@ void DescriptorArray::Append(Descriptor* desc) {
for (insertion = descriptor_number; insertion > 0; --insertion) {
Name key = GetSortedKey(insertion - 1);
if (key.Hash() <= hash) break;
if (key.hash() <= hash) break;
SetSortedKey(insertion, GetSortedKeyIndex(insertion - 1));
}
......
......@@ -113,7 +113,7 @@ class DescriptorArray
int slack = 0);
// Sort the instance descriptors by the hash codes of their keys.
void Sort();
V8_EXPORT_PRIVATE void Sort();
// Search the instance descriptors for given name. {concurrent_search} signals
// if we are doing the search on a background thread. If so, we will sacrifice
......
......@@ -233,7 +233,7 @@ int BinarySearch(T* array, Name name, int valid_entries,
// index). After doing the binary search and getting the correct internal
// index we check to have the index lower than valid_entries, if needed.
int high = array->number_of_entries() - 1;
uint32_t hash = name.hash_field();
uint32_t hash = name.hash();
int limit = high;
DCHECK(low <= high);
......@@ -241,7 +241,7 @@ int BinarySearch(T* array, Name name, int valid_entries,
while (low != high) {
int mid = low + (high - low) / 2;
Name mid_name = array->GetSortedKey(mid);
uint32_t mid_hash = mid_name.hash_field();
uint32_t mid_hash = mid_name.hash();
if (mid_hash >= hash) {
high = mid;
......@@ -253,7 +253,7 @@ int BinarySearch(T* array, Name name, int valid_entries,
for (; low <= limit; ++low) {
int sort_index = array->GetSortedKeyIndex(low);
Name entry = array->GetKey(InternalIndex(sort_index));
uint32_t current_hash = entry.hash_field();
uint32_t current_hash = entry.hash();
if (current_hash != hash) {
// 'search_mode == ALL_ENTRIES' here and below is not needed since
// 'out_insertion_index != nullptr' implies 'search_mode == ALL_ENTRIES'.
......@@ -285,12 +285,12 @@ template <SearchMode search_mode, typename T>
int LinearSearch(T* array, Name name, int valid_entries,
int* out_insertion_index) {
if (search_mode == ALL_ENTRIES && out_insertion_index != nullptr) {
uint32_t hash = name.hash_field();
uint32_t hash = name.hash();
int len = array->number_of_entries();
for (int number = 0; number < len; number++) {
int sorted_index = array->GetSortedKeyIndex(number);
Name entry = array->GetKey(InternalIndex(sorted_index));
uint32_t current_hash = entry.hash_field();
uint32_t current_hash = entry.hash();
if (current_hash > hash) {
*out_insertion_index = sorted_index;
return T::kNotFound;
......
......@@ -94,6 +94,12 @@ uint32_t Name::Hash() {
return String::cast(*this).ComputeAndSetHash();
}
uint32_t Name::hash() const {
uint32_t field = hash_field();
DCHECK(IsHashFieldComputed(field));
return field >> kHashShift;
}
DEF_GETTER(Name, IsInterestingSymbol, bool) {
return IsSymbol(isolate) && Symbol::cast(*this).is_interesting_symbol();
}
......
......@@ -23,9 +23,15 @@ class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> {
// Tells whether the hash code has been computed.
inline bool HasHashCode();
// Returns a hash value used for the property table
// Returns a hash value used for the property table. Ensures that the hash
// value is computed.
// TODO(ishell): rename to EnsureHash().
inline uint32_t Hash();
// Returns a hash value used for the property table (same as Hash()), assumes
// the hash is already computed.
inline uint32_t hash() const;
// Equality operations.
inline bool Equals(Name other);
inline static bool Equals(Isolate* isolate, Handle<Name> one,
......
......@@ -4362,16 +4362,16 @@ void DescriptorArray::Sort() {
// Reset sorting since the descriptor array might contain invalid pointers.
for (int i = 0; i < len; ++i) SetSortedKey(i, i);
// Bottom-up max-heap construction.
// Index of the last node with children
// Index of the last node with children.
const int max_parent_index = (len / 2) - 1;
for (int i = max_parent_index; i >= 0; --i) {
int parent_index = i;
const uint32_t parent_hash = GetSortedKey(i).Hash();
const uint32_t parent_hash = GetSortedKey(i).hash();
while (parent_index <= max_parent_index) {
int child_index = 2 * parent_index + 1;
uint32_t child_hash = GetSortedKey(child_index).Hash();
uint32_t child_hash = GetSortedKey(child_index).hash();
if (child_index + 1 < len) {
uint32_t right_child_hash = GetSortedKey(child_index + 1).Hash();
uint32_t right_child_hash = GetSortedKey(child_index + 1).hash();
if (right_child_hash > child_hash) {
child_index++;
child_hash = right_child_hash;
......@@ -4390,13 +4390,13 @@ void DescriptorArray::Sort() {
SwapSortedKeys(0, i);
// Shift down the new top element.
int parent_index = 0;
const uint32_t parent_hash = GetSortedKey(parent_index).Hash();
const uint32_t parent_hash = GetSortedKey(parent_index).hash();
const int max_parent_index = (i / 2) - 1;
while (parent_index <= max_parent_index) {
int child_index = parent_index * 2 + 1;
uint32_t child_hash = GetSortedKey(child_index).Hash();
uint32_t child_hash = GetSortedKey(child_index).hash();
if (child_index + 1 < i) {
uint32_t right_child_hash = GetSortedKey(child_index + 1).Hash();
uint32_t right_child_hash = GetSortedKey(child_index + 1).hash();
if (right_child_hash > child_hash) {
child_index++;
child_hash = right_child_hash;
......
......@@ -177,12 +177,20 @@ int TransitionArray::SearchNameForTesting(Name name, int* out_insertion_index) {
return SearchName(name, out_insertion_index);
}
Map TransitionArray::SearchAndGetTargetForTesting(
PropertyKind kind, Name name, PropertyAttributes attributes) {
return SearchAndGetTarget(kind, name, attributes);
}
int TransitionArray::SearchSpecial(Symbol symbol, int* out_insertion_index) {
return SearchName(symbol, out_insertion_index);
}
int TransitionArray::SearchName(Name name, int* out_insertion_index) {
DCHECK(name.IsUniqueName());
// The name is taken from DescriptorArray, so it must already has a computed
// hash.
DCHECK(name.HasHashCode());
return internal::Search<ALL_ENTRIES>(this, name, number_of_entries(),
out_insertion_index);
}
......
......@@ -645,8 +645,8 @@ void TransitionArray::Sort() {
temp_kind = details.kind();
temp_attributes = details.attributes();
}
int cmp = CompareKeys(temp_key, temp_key.Hash(), temp_kind,
temp_attributes, key, key.Hash(), kind, attributes);
int cmp = CompareKeys(temp_key, temp_key.hash(), temp_kind,
temp_attributes, key, key.hash(), kind, attributes);
if (cmp > 0) {
SetKey(j + 1, temp_key);
SetRawTarget(j + 1, temp_target);
......
......@@ -150,6 +150,8 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
inline int Capacity();
inline TransitionArray transitions();
private:
friend class MarkCompactCollector; // For HasSimpleTransitionTo.
friend class TransitionArray;
......@@ -182,8 +184,6 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
void TraverseTransitionTreeInternal(TraverseCallback callback, void* data,
DisallowHeapAllocation* no_gc);
inline TransitionArray transitions();
Isolate* isolate_;
Handle<Map> map_handle_;
Map map_;
......@@ -239,7 +239,7 @@ class TransitionArray : public WeakFixedArray {
V8_EXPORT_PRIVATE bool IsSortedNoDuplicates();
#endif
void Sort();
V8_EXPORT_PRIVATE void Sort();
void PrintInternal(std::ostream& os);
......@@ -268,6 +268,9 @@ class TransitionArray : public WeakFixedArray {
inline int SearchNameForTesting(Name name,
int* out_insertion_index = nullptr);
inline Map SearchAndGetTargetForTesting(PropertyKind kind, Name name,
PropertyAttributes attributes);
private:
friend class Factory;
friend class MarkCompactCollector;
......@@ -304,8 +307,8 @@ class TransitionArray : public WeakFixedArray {
int Search(PropertyKind kind, Name name, PropertyAttributes attributes,
int* out_insertion_index = nullptr);
Map SearchAndGetTarget(PropertyKind kind, Name name,
PropertyAttributes attributes);
V8_EXPORT_PRIVATE Map SearchAndGetTarget(PropertyKind kind, Name name,
PropertyAttributes attributes);
// Search a non-property transition (like elements kind, observe or frozen
// transitions).
......
......@@ -217,6 +217,7 @@ v8_source_set("cctest_sources") {
"test-debug.cc",
"test-decls.cc",
"test-deoptimization.cc",
"test-descriptor-array.cc",
"test-dictionary.cc",
"test-diy-fp.cc",
"test-double.cc",
......
// Copyright 2020 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/base/logging.h"
#include "src/codegen/code-stub-assembler.h"
#include "src/common/globals.h"
#include "src/objects/descriptor-array.h"
#include "src/objects/property-details.h"
#include "src/objects/string-inl.h"
#include "src/objects/transitions-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/code-assembler-tester.h"
#include "test/cctest/compiler/function-tester.h"
#include "test/cctest/test-transitions.h"
namespace v8 {
namespace internal {
namespace {
using Label = compiler::CodeAssemblerLabel;
template <class T>
using TVariable = compiler::TypedCodeAssemblerVariable<T>;
Handle<Name> NewNameWithHash(Isolate* isolate, const char* str, uint32_t hash,
bool is_integer) {
uint32_t hash_field = hash << Name::kHashShift;
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
static_assert(Name::kHashNotComputedMask == 1, "This test needs updating");
static_assert(Name::kIsNotIntegerIndexMask == 2, "This test needs updating");
if (!is_integer) {
hash_field |= Name::kIsNotIntegerIndexMask;
}
Handle<Name> name = isolate->factory()->NewOneByteInternalizedString(
OneByteVector(str), hash_field);
name->set_hash_field(hash_field);
CHECK(name->IsUniqueName());
return name;
}
template <typename... Args>
MaybeHandle<Object> Call(Isolate* isolate, Handle<JSFunction> function,
Args... args) {
const int nof_args = sizeof...(Args);
Handle<Object> call_args[] = {args...};
Handle<Object> receiver = isolate->factory()->undefined_value();
return Execution::Call(isolate, function, receiver, nof_args, call_args);
}
void CheckDescriptorArrayLookups(Isolate* isolate, Handle<Map> map,
std::vector<Handle<Name>>& names,
Handle<JSFunction> csa_lookup) {
// Test C++ implementation.
{
DisallowHeapAllocation no_gc;
DescriptorArray descriptors = map->instance_descriptors(kRelaxedLoad);
DCHECK(descriptors.IsSortedNoDuplicates());
int nof_descriptors = descriptors.number_of_descriptors();
for (size_t i = 0; i < names.size(); ++i) {
Name name = *names[i];
InternalIndex index = descriptors.Search(name, nof_descriptors, false);
CHECK(index.is_found());
CHECK_EQ(i, index.as_uint32());
}
}
// Test CSA implementation.
if (!FLAG_jitless) {
for (size_t i = 0; i < names.size(); ++i) {
Handle<Object> name_index =
Call(isolate, csa_lookup, map, names[i]).ToHandleChecked();
CHECK(name_index->IsSmi());
CHECK_EQ(DescriptorArray::ToKeyIndex(static_cast<int>(i)),
Smi::ToInt(*name_index));
}
}
}
void CheckTransitionArrayLookups(Isolate* isolate,
Handle<TransitionArray> transitions,
std::vector<Handle<Map>>& maps,
Handle<JSFunction> csa_lookup) {
// Test C++ implementation.
{
DisallowHeapAllocation no_gc;
DCHECK(transitions->IsSortedNoDuplicates());
for (size_t i = 0; i < maps.size(); ++i) {
Map expected_map = *maps[i];
Name name = expected_map.instance_descriptors(kRelaxedLoad)
.GetKey(expected_map.LastAdded());
Map map = transitions->SearchAndGetTargetForTesting(PropertyKind::kData,
name, NONE);
CHECK(!map.is_null());
CHECK_EQ(expected_map, map);
}
}
// Test CSA implementation.
if (!FLAG_jitless) {
for (size_t i = 0; i < maps.size(); ++i) {
Handle<Map> expected_map = maps[i];
Handle<Name> name(expected_map->instance_descriptors(kRelaxedLoad)
.GetKey(expected_map->LastAdded()),
isolate);
Handle<Object> transition_map =
Call(isolate, csa_lookup, transitions, name).ToHandleChecked();
CHECK(transition_map->IsMap());
CHECK_EQ(*expected_map, *transition_map);
}
}
}
// Creates function with (Map, Name) arguments. Returns Smi with the index of
// the name value of the found descriptor (DescriptorArray::ToKeyIndex())
// or null otherwise.
Handle<JSFunction> CreateCsaDescriptorArrayLookup(Isolate* isolate) {
// We are not allowed to generate code in jitless mode.
if (FLAG_jitless) return Handle<JSFunction>();
// Preallocate handle for the result in the current handle scope.
Handle<JSFunction> result_function(JSFunction{}, isolate);
const int kNumParams = 2;
compiler::CodeAssemblerTester asm_tester(
isolate, kNumParams + 1, // +1 to include receiver.
CodeKind::DEOPT_ENTRIES_OR_FOR_TESTING);
{
CodeStubAssembler m(asm_tester.state());
auto map = m.Parameter<Map>(1);
auto unique_name = m.Parameter<Name>(2);
Label passed(&m), failed(&m);
Label if_found(&m), if_not_found(&m);
TVariable<IntPtrT> var_name_index(&m);
TNode<Uint32T> bit_field3 = m.LoadMapBitField3(map);
TNode<DescriptorArray> descriptors = m.LoadMapDescriptors(map);
m.DescriptorLookup(unique_name, descriptors, bit_field3, &if_found,
&var_name_index, &if_not_found);
m.BIND(&if_found);
m.Return(m.SmiTag(var_name_index.value()));
m.BIND(&if_not_found);
m.Return(m.NullConstant());
}
{
compiler::FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
// Copy function value to a handle created in the outer handle scope.
result_function.PatchValue(*ft.function);
}
return result_function;
}
// Creates function with (TransitionArray, Name) arguments. Returns transition
// map if transition is found or null otherwise.
Handle<JSFunction> CreateCsaTransitionArrayLookup(Isolate* isolate) {
// We are not allowed to generate code in jitless mode.
if (FLAG_jitless) return Handle<JSFunction>();
// Preallocate handle for the result in the current handle scope.
Handle<JSFunction> result_function(JSFunction{}, isolate);
const int kNumParams = 2;
compiler::CodeAssemblerTester asm_tester(
isolate, kNumParams + 1, // +1 to include receiver.
CodeKind::DEOPT_ENTRIES_OR_FOR_TESTING);
{
CodeStubAssembler m(asm_tester.state());
auto transitions = m.Parameter<TransitionArray>(1);
auto unique_name = m.Parameter<Name>(2);
Label passed(&m), failed(&m);
Label if_found(&m), if_not_found(&m);
TVariable<IntPtrT> var_name_index(&m);
m.TransitionLookup(unique_name, transitions, &if_found, &var_name_index,
&if_not_found);
m.BIND(&if_found);
{
STATIC_ASSERT(kData == 0);
STATIC_ASSERT(NONE == 0);
const int kKeyToTargetOffset = (TransitionArray::kEntryTargetIndex -
TransitionArray::kEntryKeyIndex) *
kTaggedSize;
TNode<Map> transition_map = m.CAST(m.GetHeapObjectAssumeWeak(
m.LoadArrayElement(transitions, WeakFixedArray::kHeaderSize,
var_name_index.value(), kKeyToTargetOffset)));
m.Return(transition_map);
}
m.BIND(&if_not_found);
m.Return(m.NullConstant());
}
{
compiler::FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
// Copy function value to a handle created in the outer handle scope.
result_function.PatchValue(*ft.function);
}
return result_function;
}
} // namespace
TEST(DescriptorArrayHashCollisionMassive) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
std::vector<Handle<Name>> names;
// Use the same hash value for all names.
uint32_t hash =
static_cast<uint32_t>(isolate->GenerateIdentityHash(Name::kHashBitMask));
for (int i = 0; i < kMaxNumberOfDescriptors / 2; ++i) {
// Add pairs of names having the same base hash value but having different
// values of is_integer bit.
bool first_is_integer = (i & 1) != 0;
bool second_is_integer = (i & 2) != 0;
names.push_back(NewNameWithHash(isolate, "a", hash, first_is_integer));
names.push_back(NewNameWithHash(isolate, "b", hash, second_is_integer));
}
// Create descriptor array with the created names by appending fields to some
// map. DescriptorArray marking relies on the fact that it's attached to an
// owning map.
Handle<Map> map = Map::Create(isolate, 0);
Handle<FieldType> any_type = FieldType::Any(isolate);
for (size_t i = 0; i < names.size(); ++i) {
map = Map::CopyWithField(isolate, map, names[i], any_type, NONE,
PropertyConstness::kMutable,
Representation::Tagged(), OMIT_TRANSITION)
.ToHandleChecked();
}
Handle<JSFunction> csa_lookup = CreateCsaDescriptorArrayLookup(isolate);
CheckDescriptorArrayLookups(isolate, map, names, csa_lookup);
// Sort descriptor array and check it again.
map->instance_descriptors(kRelaxedLoad).Sort();
CheckDescriptorArrayLookups(isolate, map, names, csa_lookup);
}
TEST(DescriptorArrayHashCollision) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
std::vector<Handle<Name>> names;
uint32_t hash = 0;
for (int i = 0; i < kMaxNumberOfDescriptors / 2; ++i) {
if (i % 2 == 0) {
// Change hash value for every pair of names.
hash = static_cast<uint32_t>(
isolate->GenerateIdentityHash(Name::kHashBitMask));
}
// Add pairs of names having the same base hash value but having different
// values of is_integer bit.
bool first_is_integer = (i & 1) != 0;
bool second_is_integer = (i & 2) != 0;
names.push_back(NewNameWithHash(isolate, "a", hash, first_is_integer));
names.push_back(NewNameWithHash(isolate, "b", hash, second_is_integer));
}
// Create descriptor array with the created names by appending fields to some
// map. DescriptorArray marking relies on the fact that it's attached to an
// owning map.
Handle<Map> map = Map::Create(isolate, 0);
Handle<FieldType> any_type = FieldType::Any(isolate);
for (size_t i = 0; i < names.size(); ++i) {
map = Map::CopyWithField(isolate, map, names[i], any_type, NONE,
PropertyConstness::kMutable,
Representation::Tagged(), OMIT_TRANSITION)
.ToHandleChecked();
}
Handle<JSFunction> csa_lookup = CreateCsaDescriptorArrayLookup(isolate);
CheckDescriptorArrayLookups(isolate, map, names, csa_lookup);
// Sort descriptor array and check it again.
map->instance_descriptors(kRelaxedLoad).Sort();
CheckDescriptorArrayLookups(isolate, map, names, csa_lookup);
}
TEST(TransitionArrayHashCollisionMassive) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
std::vector<Handle<Name>> names;
// Use the same hash value for all names.
uint32_t hash =
static_cast<uint32_t>(isolate->GenerateIdentityHash(Name::kHashBitMask));
for (int i = 0; i < TransitionsAccessor::kMaxNumberOfTransitions / 2; ++i) {
// Add pairs of names having the same base hash value but having different
// values of is_integer bit.
bool first_is_integer = (i & 1) != 0;
bool second_is_integer = (i & 2) != 0;
names.push_back(NewNameWithHash(isolate, "a", hash, first_is_integer));
names.push_back(NewNameWithHash(isolate, "b", hash, second_is_integer));
}
// Create transitions for each name.
Handle<Map> root_map = Map::Create(isolate, 0);
std::vector<Handle<Map>> maps;
Handle<FieldType> any_type = FieldType::Any(isolate);
for (size_t i = 0; i < names.size(); ++i) {
Handle<Map> map =
Map::CopyWithField(isolate, root_map, names[i], any_type, NONE,
PropertyConstness::kMutable,
Representation::Tagged(), INSERT_TRANSITION)
.ToHandleChecked();
maps.push_back(map);
}
Handle<JSFunction> csa_lookup = CreateCsaTransitionArrayLookup(isolate);
Handle<TransitionArray> transition_array(
TestTransitionsAccessor(isolate, root_map).transitions(), isolate);
CheckTransitionArrayLookups(isolate, transition_array, maps, csa_lookup);
// Sort transition array and check it again.
transition_array->Sort();
CheckTransitionArrayLookups(isolate, transition_array, maps, csa_lookup);
}
TEST(TransitionArrayHashCollision) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
std::vector<Handle<Name>> names;
// Use the same hash value for all names.
uint32_t hash =
static_cast<uint32_t>(isolate->GenerateIdentityHash(Name::kHashBitMask));
for (int i = 0; i < TransitionsAccessor::kMaxNumberOfTransitions / 2; ++i) {
if (i % 2 == 0) {
// Change hash value for every pair of names.
hash = static_cast<uint32_t>(
isolate->GenerateIdentityHash(Name::kHashBitMask));
}
// Add pairs of names having the same base hash value but having different
// values of is_integer bit.
bool first_is_integer = (i & 1) != 0;
bool second_is_integer = (i & 2) != 0;
names.push_back(NewNameWithHash(isolate, "a", hash, first_is_integer));
names.push_back(NewNameWithHash(isolate, "b", hash, second_is_integer));
}
// Create transitions for each name.
Handle<Map> root_map = Map::Create(isolate, 0);
std::vector<Handle<Map>> maps;
Handle<FieldType> any_type = FieldType::Any(isolate);
for (size_t i = 0; i < names.size(); ++i) {
Handle<Map> map =
Map::CopyWithField(isolate, root_map, names[i], any_type, NONE,
PropertyConstness::kMutable,
Representation::Tagged(), INSERT_TRANSITION)
.ToHandleChecked();
maps.push_back(map);
}
Handle<JSFunction> csa_lookup = CreateCsaTransitionArrayLookup(isolate);
Handle<TransitionArray> transition_array(
TestTransitionsAccessor(isolate, root_map).transitions(), isolate);
CheckTransitionArrayLookups(isolate, transition_array, maps, csa_lookup);
// Sort transition array and check it again.
transition_array->Sort();
CheckTransitionArrayLookups(isolate, transition_array, maps, csa_lookup);
}
} // namespace internal
} // namespace v8
......@@ -27,6 +27,8 @@ class TestTransitionsAccessor : public TransitionsAccessor {
}
int Capacity() { return TransitionsAccessor::Capacity(); }
TransitionArray transitions() { return TransitionsAccessor::transitions(); }
};
} // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment