Commit 05f41a55 authored by Frank Emrich's avatar Frank Emrich Committed by Commit Bot

[dict-proto] CSA version of ordered hash table allocation

Previously, CodeStubAssembler::AllocateOrderedHashTable() would
allocate hash tables of the (statically known) minimum capacity in-
dicated by the concrete table type.
This CL adds AllocateOrderedHashTableWithCapacity, which is inspired by
AllocateNameDictionary. It takes a Node<IntPtrT> indicating the desired
capacity.

Bug: v8:7569
Change-Id: I4bf28f69286e52773319a1ae37d33b2f55175a84
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2503950
Commit-Queue: Frank Emrich <emrich@google.com>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71175}
parent 21b0f751
...@@ -843,9 +843,9 @@ TNode<HeapObject> CollectionsBuiltinsAssembler::AllocateJSCollectionIterator( ...@@ -843,9 +843,9 @@ TNode<HeapObject> CollectionsBuiltinsAssembler::AllocateJSCollectionIterator(
TNode<HeapObject> CollectionsBuiltinsAssembler::AllocateTable( TNode<HeapObject> CollectionsBuiltinsAssembler::AllocateTable(
Variant variant, TNode<IntPtrT> at_least_space_for) { Variant variant, TNode<IntPtrT> at_least_space_for) {
if (variant == kMap || variant == kWeakMap) { if (variant == kMap || variant == kWeakMap) {
return AllocateOrderedHashTable<OrderedHashMap>(); return AllocateOrderedHashMap();
} else { } else {
return AllocateOrderedHashTable<OrderedHashSet>(); return AllocateOrderedHashSet();
} }
} }
......
...@@ -3351,61 +3351,148 @@ TNode<NameDictionary> CodeStubAssembler::CopyNameDictionary( ...@@ -3351,61 +3351,148 @@ TNode<NameDictionary> CodeStubAssembler::CopyNameDictionary(
} }
template <typename CollectionType> template <typename CollectionType>
TNode<CollectionType> CodeStubAssembler::AllocateOrderedHashTable() { TNode<CollectionType> CodeStubAssembler::AllocateOrderedHashTable(
static const int kCapacity = CollectionType::kInitialCapacity; TNode<IntPtrT> capacity) {
static const int kBucketCount = kCapacity / CollectionType::kLoadFactor; capacity = IntPtrRoundUpToPowerOfTwo32(capacity);
static const int kDataTableLength = kCapacity * CollectionType::kEntrySize; capacity =
static const int kFixedArrayLength = IntPtrMax(capacity, IntPtrConstant(CollectionType::kInitialCapacity));
CollectionType::HashTableStartIndex() + kBucketCount + kDataTableLength; return AllocateOrderedHashTableWithCapacity<CollectionType>(capacity);
static const int kDataTableStartIndex = }
CollectionType::HashTableStartIndex() + kBucketCount;
template <typename CollectionType>
STATIC_ASSERT(base::bits::IsPowerOfTwo(kCapacity)); TNode<CollectionType> CodeStubAssembler::AllocateOrderedHashTableWithCapacity(
STATIC_ASSERT(kCapacity <= CollectionType::MaxCapacity()); TNode<IntPtrT> capacity) {
CSA_ASSERT(this, WordIsPowerOfTwo(capacity));
CSA_ASSERT(this,
IntPtrGreaterThanOrEqual(
capacity, IntPtrConstant(CollectionType::kInitialCapacity)));
CSA_ASSERT(this,
IntPtrLessThanOrEqual(
capacity, IntPtrConstant(CollectionType::MaxCapacity())));
STATIC_ASSERT(CollectionType::kLoadFactor == 2);
TNode<IntPtrT> bucket_count = Signed(WordShr(capacity, IntPtrConstant(1)));
TNode<IntPtrT> data_table_length =
IntPtrMul(capacity, IntPtrConstant(CollectionType::kEntrySize));
TNode<IntPtrT> data_table_start_index = IntPtrAdd(
IntPtrConstant(CollectionType::HashTableStartIndex()), bucket_count);
TNode<IntPtrT> fixed_array_length =
IntPtrAdd(data_table_start_index, data_table_length);
// Allocate the table and add the proper map. // Allocate the table and add the proper map.
const ElementsKind elements_kind = HOLEY_ELEMENTS; const ElementsKind elements_kind = HOLEY_ELEMENTS;
TNode<IntPtrT> length_intptr = IntPtrConstant(kFixedArrayLength);
TNode<Map> fixed_array_map = TNode<Map> fixed_array_map =
HeapConstant(CollectionType::GetMap(ReadOnlyRoots(isolate()))); HeapConstant(CollectionType::GetMap(ReadOnlyRoots(isolate())));
TNode<CollectionType> table = TNode<CollectionType> table =
CAST(AllocateFixedArray(elements_kind, length_intptr, CAST(AllocateFixedArray(elements_kind, fixed_array_length,
kAllowLargeObjectAllocation, fixed_array_map)); kAllowLargeObjectAllocation, fixed_array_map));
// Initialize the OrderedHashTable fields. Comment("Initialize the OrderedHashTable fields.");
const WriteBarrierMode barrier_mode = SKIP_WRITE_BARRIER; const WriteBarrierMode barrier_mode = SKIP_WRITE_BARRIER;
StoreFixedArrayElement(table, CollectionType::NumberOfElementsIndex(), UnsafeStoreFixedArrayElement(table, CollectionType::NumberOfElementsIndex(),
SmiConstant(0), barrier_mode); SmiConstant(0), barrier_mode);
StoreFixedArrayElement(table, CollectionType::NumberOfDeletedElementsIndex(), UnsafeStoreFixedArrayElement(table,
SmiConstant(0), barrier_mode); CollectionType::NumberOfDeletedElementsIndex(),
StoreFixedArrayElement(table, CollectionType::NumberOfBucketsIndex(), SmiConstant(0), barrier_mode);
SmiConstant(kBucketCount), barrier_mode); UnsafeStoreFixedArrayElement(table, CollectionType::NumberOfBucketsIndex(),
SmiFromIntPtr(bucket_count), barrier_mode);
// Fill the buckets with kNotFound.
TNode<Smi> not_found = SmiConstant(CollectionType::kNotFound); TNode<IntPtrT> object_address = BitcastTaggedToWord(table);
STATIC_ASSERT(CollectionType::HashTableStartIndex() == STATIC_ASSERT(CollectionType::HashTableStartIndex() ==
CollectionType::NumberOfBucketsIndex() + 1); CollectionType::NumberOfBucketsIndex() + 1);
STATIC_ASSERT((CollectionType::HashTableStartIndex() + kBucketCount) ==
kDataTableStartIndex);
for (int i = 0; i < kBucketCount; i++) {
StoreFixedArrayElement(table, CollectionType::HashTableStartIndex() + i,
not_found, barrier_mode);
}
// Fill the data table with undefined. TNode<Smi> not_found_sentinel = SmiConstant(CollectionType::kNotFound);
STATIC_ASSERT(kDataTableStartIndex + kDataTableLength == kFixedArrayLength);
for (int i = 0; i < kDataTableLength; i++) { intptr_t const_capacity;
StoreFixedArrayElement(table, kDataTableStartIndex + i, UndefinedConstant(), if (TryToIntPtrConstant(capacity, &const_capacity) &&
barrier_mode); const_capacity == CollectionType::kInitialCapacity) {
int const_bucket_count =
static_cast<int>(const_capacity / CollectionType::kLoadFactor);
int const_data_table_length =
static_cast<int>(const_capacity * CollectionType::kEntrySize);
int const_data_table_start_index = static_cast<int>(
CollectionType::HashTableStartIndex() + const_bucket_count);
Comment("Fill the buckets with kNotFound (constant capacity).");
for (int i = 0; i < const_bucket_count; i++) {
UnsafeStoreFixedArrayElement(table,
CollectionType::HashTableStartIndex() + i,
not_found_sentinel, barrier_mode);
}
Comment("Fill the data table with undefined (constant capacity).");
for (int i = 0; i < const_data_table_length; i++) {
UnsafeStoreFixedArrayElement(table, const_data_table_start_index + i,
UndefinedConstant(), barrier_mode);
}
} else {
Comment("Fill the buckets with kNotFound.");
TNode<IntPtrT> buckets_start_address =
IntPtrAdd(object_address,
IntPtrConstant(FixedArray::OffsetOfElementAt(
CollectionType::HashTableStartIndex()) -
kHeapObjectTag));
TNode<IntPtrT> buckets_end_address =
IntPtrAdd(buckets_start_address, TimesTaggedSize(bucket_count));
StoreFieldsNoWriteBarrier(buckets_start_address, buckets_end_address,
not_found_sentinel);
Comment("Fill the data table with undefined.");
TNode<IntPtrT> data_start_address = buckets_end_address;
TNode<IntPtrT> data_end_address = IntPtrAdd(
object_address,
IntPtrAdd(IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag),
TimesTaggedSize(fixed_array_length)));
StoreFieldsNoWriteBarrier(data_start_address, data_end_address,
UndefinedConstant());
#ifdef DEBUG
TNode<IntPtrT> ptr_diff =
IntPtrSub(data_end_address, buckets_start_address);
TNode<IntPtrT> array_length = LoadAndUntagFixedArrayBaseLength(table);
TNode<IntPtrT> array_data_fields = IntPtrSub(
array_length, IntPtrConstant(CollectionType::HashTableStartIndex()));
TNode<IntPtrT> expected_end =
IntPtrAdd(data_start_address,
TimesTaggedSize(IntPtrMul(
capacity, IntPtrConstant(CollectionType::kEntrySize))));
CSA_ASSERT(this, IntPtrEqual(ptr_diff, TimesTaggedSize(array_data_fields)));
CSA_ASSERT(this, IntPtrEqual(expected_end, data_end_address));
#endif
} }
return table; return table;
} }
template TNode<OrderedHashMap> TNode<OrderedNameDictionary> CodeStubAssembler::AllocateOrderedNameDictionary(
CodeStubAssembler::AllocateOrderedHashTable<OrderedHashMap>(); TNode<IntPtrT> capacity) {
template TNode<OrderedHashSet> TNode<OrderedNameDictionary> table =
CodeStubAssembler::AllocateOrderedHashTable<OrderedHashSet>(); AllocateOrderedHashTable<OrderedNameDictionary>(capacity);
StoreFixedArrayElement(table, OrderedNameDictionary::PrefixIndex(),
SmiConstant(PropertyArray::kNoHashSentinel),
SKIP_WRITE_BARRIER);
return table;
}
TNode<OrderedNameDictionary> CodeStubAssembler::AllocateOrderedNameDictionary(
int capacity) {
return AllocateOrderedNameDictionary(IntPtrConstant(capacity));
}
TNode<OrderedHashSet> CodeStubAssembler::AllocateOrderedHashSet() {
return AllocateOrderedHashTableWithCapacity<OrderedHashSet>(
IntPtrConstant(OrderedHashSet::kInitialCapacity));
}
TNode<OrderedHashMap> CodeStubAssembler::AllocateOrderedHashMap() {
return AllocateOrderedHashTableWithCapacity<OrderedHashMap>(
IntPtrConstant(OrderedHashMap::kInitialCapacity));
}
TNode<JSObject> CodeStubAssembler::AllocateJSObjectFromMap( TNode<JSObject> CodeStubAssembler::AllocateJSObjectFromMap(
TNode<Map> map, base::Optional<TNode<HeapObject>> properties, TNode<Map> map, base::Optional<TNode<HeapObject>> properties,
......
...@@ -1735,8 +1735,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -1735,8 +1735,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<NameDictionary> CopyNameDictionary(TNode<NameDictionary> dictionary, TNode<NameDictionary> CopyNameDictionary(TNode<NameDictionary> dictionary,
Label* large_object_fallback); Label* large_object_fallback);
template <typename CollectionType> TNode<OrderedHashSet> AllocateOrderedHashSet();
TNode<CollectionType> AllocateOrderedHashTable();
TNode<OrderedHashMap> AllocateOrderedHashMap();
// Allocates an OrderedNameDictionary of the given capacity. This guarantees
// that |capacity| entries can be added without reallocating.
TNode<OrderedNameDictionary> AllocateOrderedNameDictionary(
TNode<IntPtrT> capacity);
TNode<OrderedNameDictionary> AllocateOrderedNameDictionary(int capacity);
TNode<JSObject> AllocateJSObjectFromMap( TNode<JSObject> AllocateJSObjectFromMap(
TNode<Map> map, TNode<Map> map,
...@@ -3662,6 +3669,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -3662,6 +3669,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
base::Optional<TNode<AllocationSite>> allocation_site, base::Optional<TNode<AllocationSite>> allocation_site,
TNode<IntPtrT> size_in_bytes); TNode<IntPtrT> size_in_bytes);
// Increases the provided capacity to the next valid value, if necessary.
template <typename CollectionType>
TNode<CollectionType> AllocateOrderedHashTable(TNode<IntPtrT> capacity);
// Uses the provided capacity (which must be valid) in verbatim.
template <typename CollectionType>
TNode<CollectionType> AllocateOrderedHashTableWithCapacity(
TNode<IntPtrT> capacity);
TNode<IntPtrT> SmiShiftBitsConstant() { TNode<IntPtrT> SmiShiftBitsConstant() {
return IntPtrConstant(kSmiShiftSize + kSmiTagSize); return IntPtrConstant(kSmiShiftSize + kSmiTagSize);
} }
......
...@@ -167,11 +167,11 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> { ...@@ -167,11 +167,11 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<FrameArray> NewFrameArray(int number_of_frames); Handle<FrameArray> NewFrameArray(int number_of_frames);
// Allocates a |NameDictionary| with an internal capacity calculated such that // Allocates a NameDictionary with an internal capacity calculated such that
// |at_least_space_for| entries can be added without reallocating. // |at_least_space_for| entries can be added without reallocating.
Handle<NameDictionary> NewNameDictionary(int at_least_space_for); Handle<NameDictionary> NewNameDictionary(int at_least_space_for);
// Allocates an |OrderedNameDictionary| of the given capacity. This guarantees // Allocates an OrderedNameDictionary of the given capacity. This guarantees
// that |capacity| entries can be added without reallocating. // that |capacity| entries can be added without reallocating.
Handle<OrderedNameDictionary> NewOrderedNameDictionary( Handle<OrderedNameDictionary> NewOrderedNameDictionary(
int capacity = OrderedNameDictionary::kInitialCapacity); int capacity = OrderedNameDictionary::kInitialCapacity);
......
...@@ -1902,7 +1902,20 @@ TEST(AllocateJSObjectFromMap) { ...@@ -1902,7 +1902,20 @@ TEST(AllocateJSObjectFromMap) {
} }
} }
TEST(AllocateNameDictionary) { namespace {
template <typename Dictionary>
using CSAAllocator =
std::function<TNode<Dictionary>(CodeStubAssembler&, TNode<IntPtrT>)> const&;
template <typename Dictionary>
using Allocator = std::function<Handle<Dictionary>(Isolate*, int)> const&;
// Tests that allocation code emitted by {csa_alloc} yields ordered hash tables
// identical to those produced by {alloc}.
template <typename Dictionary>
void TestDictionaryAllocation(CSAAllocator<Dictionary> csa_alloc,
Allocator<Dictionary> alloc, int max_capacity) {
Isolate* isolate(CcTest::InitIsolateOnce()); Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1; const int kNumParams = 1;
...@@ -1911,18 +1924,17 @@ TEST(AllocateNameDictionary) { ...@@ -1911,18 +1924,17 @@ TEST(AllocateNameDictionary) {
{ {
auto capacity = m.Parameter<Smi>(1); auto capacity = m.Parameter<Smi>(1);
TNode<NameDictionary> result = TNode<Dictionary> result = csa_alloc(m, m.SmiUntag(capacity));
m.AllocateNameDictionary(m.SmiUntag(capacity));
m.Return(result); m.Return(result);
} }
FunctionTester ft(asm_tester.GenerateCode(), kNumParams); FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
{ {
for (int i = 0; i < 256; i = i * 1.1 + 1) { for (int i = 0; i < max_capacity; i = i * 1.1 + 1) {
Handle<HeapObject> result = Handle<HeapObject>::cast( Handle<HeapObject> result = Handle<HeapObject>::cast(
ft.Call(handle(Smi::FromInt(i), isolate)).ToHandleChecked()); ft.Call(handle(Smi::FromInt(i), isolate)).ToHandleChecked());
Handle<NameDictionary> dict = NameDictionary::New(isolate, i); Handle<Dictionary> dict = alloc(isolate, i);
// Both dictionaries should be memory equal. // Both dictionaries should be memory equal.
int size = dict->Size(); int size = dict->Size();
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(dict->address()), CHECK_EQ(0, memcmp(reinterpret_cast<void*>(dict->address()),
...@@ -1931,6 +1943,52 @@ TEST(AllocateNameDictionary) { ...@@ -1931,6 +1943,52 @@ TEST(AllocateNameDictionary) {
} }
} }
} // namespace
TEST(AllocateNameDictionary) {
auto csa_alloc = [](CodeStubAssembler& m, TNode<IntPtrT> cap) {
return m.AllocateNameDictionary(cap);
};
auto alloc = [](Isolate* isolate, int capacity) {
return NameDictionary::New(isolate, capacity);
};
TestDictionaryAllocation<NameDictionary>(csa_alloc, alloc, 256);
}
TEST(AllocateOrderedNameDictionary) {
auto csa_alloc = [](CodeStubAssembler& m, TNode<IntPtrT> cap) {
return m.AllocateOrderedNameDictionary(cap);
};
auto alloc = [](Isolate* isolate, int capacity) {
return OrderedNameDictionary::Allocate(isolate, capacity).ToHandleChecked();
};
TestDictionaryAllocation<OrderedNameDictionary>(csa_alloc, alloc, 256);
}
TEST(AllocateOrderedHashSet) {
// ignoring capacitites, as the API cannot take them
auto csa_alloc = [](CodeStubAssembler& m, TNode<IntPtrT> cap) {
return m.AllocateOrderedHashSet();
};
auto alloc = [](Isolate* isolate, int capacity) {
return OrderedHashSet::Allocate(isolate, OrderedHashSet::kInitialCapacity)
.ToHandleChecked();
};
TestDictionaryAllocation<OrderedHashSet>(csa_alloc, alloc, 1);
}
TEST(AllocateOrderedHashMap) {
// ignoring capacities, as the API cannot take them
auto csa_alloc = [](CodeStubAssembler& m, TNode<IntPtrT> cap) {
return m.AllocateOrderedHashMap();
};
auto alloc = [](Isolate* isolate, int capacity) {
return OrderedHashMap::Allocate(isolate, OrderedHashMap::kInitialCapacity)
.ToHandleChecked();
};
TestDictionaryAllocation<OrderedHashMap>(csa_alloc, alloc, 1);
}
TEST(PopAndReturnFromJSBuiltinWithStackParameters) { TEST(PopAndReturnFromJSBuiltinWithStackParameters) {
Isolate* isolate(CcTest::InitIsolateOnce()); Isolate* isolate(CcTest::InitIsolateOnce());
......
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