Commit 50ee1407 authored by Frank Emrich's avatar Frank Emrich Committed by Commit Bot

[dict-proto] C++ implementation of SwissNameDictionary, pt. 8

This CL is part of a series that adds the C++ implementation of
SwissNameDictionary, a deterministic property backing store based on
Swiss Tables.

This CL contains:
1. Copy and equality functions used for testing
2. Runtime functions corresponding to most dictionary operations,
   which are used temporarily while the CSA/Torque implementation
   is work in progress
3. Some minor changes to SwissNameDictionary needed for testing
   (adding template instantiations, V8_EXPORT_PRIVATE, ...)

Bug: v8:11388
Change-Id: Iea5f4650b0a443edf563565138ea86fcb45af13a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2722094
Commit-Queue: Frank Emrich <emrich@google.com>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73153}
parent 99af8d49
......@@ -77,6 +77,100 @@ Handle<SwissNameDictionary> SwissNameDictionary::Rehash(
return new_table;
}
bool SwissNameDictionary::EqualsForTesting(SwissNameDictionary other) {
if (Capacity() != other.Capacity() ||
NumberOfElements() != other.NumberOfElements() ||
NumberOfDeletedElements() != other.NumberOfDeletedElements() ||
Hash() != other.Hash()) {
return false;
}
for (int i = 0; i < Capacity() + kGroupWidth; i++) {
if (CtrlTable()[i] != other.CtrlTable()[i]) {
return false;
}
}
for (int i = 0; i < Capacity(); i++) {
if (KeyAt(i) != other.KeyAt(i) || ValueAtRaw(i) != other.ValueAtRaw(i)) {
return false;
}
if (IsFull(GetCtrl(i))) {
if (DetailsAt(i) != other.DetailsAt(i)) return false;
}
}
for (int i = 0; i < UsedCapacity(); i++) {
if (EntryForEnumerationIndex(i) != other.EntryForEnumerationIndex(i)) {
return false;
}
}
return true;
}
// static
Handle<SwissNameDictionary> SwissNameDictionary::CopyForTesting(
Isolate* isolate, Handle<SwissNameDictionary> table) {
if (table->Capacity() == 0) {
return table;
}
int capacity = table->Capacity();
int used_capacity = table->UsedCapacity();
Handle<SwissNameDictionary> new_table =
isolate->factory()->NewSwissNameDictionaryWithCapacity(
capacity, Heap::InYoungGeneration(*table) ? AllocationType::kYoung
: AllocationType::kOld);
new_table->SetHash(table->Hash());
DisallowGarbageCollection no_gc;
WriteBarrierMode mode = new_table->GetWriteBarrierMode(no_gc);
if (mode == WriteBarrierMode::SKIP_WRITE_BARRIER) {
// Copy data table and ctrl table, which are stored next to each other.
void* original_start =
reinterpret_cast<void*>(table->field_address(DataTableStartOffset()));
void* new_table_start = reinterpret_cast<void*>(
new_table->field_address(DataTableStartOffset()));
size_t bytes_to_copy = DataTableSize(capacity) + CtrlTableSize(capacity);
DCHECK(DataTableEndOffset(capacity) == CtrlTableStartOffset(capacity));
MemCopy(new_table_start, original_start, bytes_to_copy);
} else {
DCHECK_EQ(UPDATE_WRITE_BARRIER, mode);
// We may have to trigger write barriers when copying the data table.
for (int i = 0; i < capacity; ++i) {
Object key = table->KeyAt(i);
Object value = table->ValueAtRaw(i);
// Cannot use SetKey/ValueAtPut because they don't accept the hole as data
// to store.
new_table->StoreToDataTable(i, kDataTableKeyEntryIndex, key);
new_table->StoreToDataTable(i, kDataTableValueEntryIndex, value);
}
void* original_ctrl_table = table->CtrlTable();
void* new_ctrl_table = new_table->CtrlTable();
MemCopy(new_ctrl_table, original_ctrl_table, CtrlTableSize(capacity));
}
// PropertyDetails table may contain uninitialized data for unused slots.
for (int i = 0; i < capacity; ++i) {
if (IsFull(table->GetCtrl(i))) {
new_table->DetailsAtPut(i, table->DetailsAt(i));
}
}
// Meta table is only initialized for the first 2 + UsedCapacity() entries,
// where size of each entry depends on table capacity.
int size_per_meta_table_entry = MetaTableSizePerEntryFor(capacity);
int meta_table_used_bytes = (2 + used_capacity) * size_per_meta_table_entry;
new_table->meta_table().copy_in(0, table->meta_table().GetDataStartAddress(),
meta_table_used_bytes);
return new_table;
}
// static
Handle<SwissNameDictionary> SwissNameDictionary::Shrink(
Isolate* isolate, Handle<SwissNameDictionary> table) {
......@@ -164,14 +258,21 @@ STATIC_ASSERT(SwissNameDictionary::MaxUsableCapacity(
SwissNameDictionary::kMax2ByteMetaTableCapacity) <=
std::numeric_limits<uint16_t>::max());
template void SwissNameDictionary::Initialize(Isolate* isolate,
ByteArray meta_table,
int capacity);
template void SwissNameDictionary::Initialize(LocalIsolate* isolate,
ByteArray meta_table,
int capacity);
template V8_EXPORT_PRIVATE void SwissNameDictionary::Initialize(
Isolate* isolate, ByteArray meta_table, int capacity);
template V8_EXPORT_PRIVATE void SwissNameDictionary::Initialize(
LocalIsolate* isolate, ByteArray meta_table, int capacity);
template V8_EXPORT_PRIVATE Handle<SwissNameDictionary>
SwissNameDictionary::Rehash(LocalIsolate* isolate,
Handle<SwissNameDictionary> table,
int new_capacity);
template V8_EXPORT_PRIVATE Handle<SwissNameDictionary>
SwissNameDictionary::Rehash(Isolate* isolate, Handle<SwissNameDictionary> table,
int new_capacity);
constexpr int SwissNameDictionary::kInitialCapacity;
constexpr int SwissNameDictionary::kGroupWidth;
} // namespace internal
} // namespace v8
......@@ -68,7 +68,7 @@ namespace internal {
// contains the number of the bucket representing the i-th entry of the
// table in enumeration order. Entries may contain unitialized data if the
// corresponding bucket hasn't been used before.
class SwissNameDictionary : public HeapObject {
class V8_EXPORT_PRIVATE SwissNameDictionary : public HeapObject {
public:
using Group = swiss_table::Group;
......@@ -112,6 +112,18 @@ class SwissNameDictionary : public HeapObject {
inline int Capacity();
inline int UsedCapacity();
// Strict in the sense that it checks that all used/initialized memory in
// |this| and |other| is the same. The only exceptions are the meta table
// pointer (which must differ between the two tables) and PropertyDetails of
// deleted entries (which reside in initialized memory, but are not compared).
bool EqualsForTesting(SwissNameDictionary other);
// Copy operation for testing purposes. Guarantees that DebugEquals holds for
// the old table and its copy. In particular, no kind of tidying up is
// performed.
static Handle<SwissNameDictionary> CopyForTesting(
Isolate* isolate, Handle<SwissNameDictionary> table);
template <typename LocalIsolate>
void Initialize(LocalIsolate* isolate, ByteArray meta_table, int capacity);
......@@ -186,6 +198,10 @@ class SwissNameDictionary : public HeapObject {
// Indicates that IterateEntries() returns entries ordered.
static constexpr bool kIsOrderedDictionaryType = true;
// Only used in CSA/Torque, where indices are actual integers. In C++,
// InternalIndex::NotFound() is always used instead.
static constexpr int kNotFoundSentinel = -1;
static const int kGroupWidth = Group::kWidth;
class BodyDescriptor;
......
......@@ -18,6 +18,7 @@
#include "src/objects/js-array-inl.h"
#include "src/objects/property-descriptor-object.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/swiss-name-dictionary-inl.h"
#include "src/runtime/runtime-utils.h"
#include "src/runtime/runtime.h"
......@@ -1365,5 +1366,112 @@ RUNTIME_FUNCTION(Runtime_AddPrivateField) {
return ReadOnlyRoots(isolate).undefined_value();
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableAllocate) {
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(Smi, at_least_space_for, 0);
return *isolate->factory()->NewSwissNameDictionary(
at_least_space_for->value(), AllocationType::kYoung);
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableAdd) {
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(SwissNameDictionary, table, 0);
CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
CONVERT_ARG_HANDLE_CHECKED(Smi, details_smi, 3);
DCHECK(key->IsUniqueName());
return *SwissNameDictionary::Add(isolate, table, key, value,
PropertyDetails{*details_smi});
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableFindEntry) {
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(SwissNameDictionary, table, 0);
CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
InternalIndex index = table->FindEntry(isolate, *key);
return Smi::FromInt(index.is_found()
? index.as_int()
: SwissNameDictionary::kNotFoundSentinel);
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableUpdate) {
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(SwissNameDictionary, table, 0);
CONVERT_ARG_HANDLE_CHECKED(Smi, index, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
CONVERT_ARG_HANDLE_CHECKED(Smi, details_smi, 3);
InternalIndex i(Smi::ToInt(*index));
table->ValueAtPut(i, *value);
table->DetailsAtPut(i, PropertyDetails{*details_smi});
return ReadOnlyRoots(isolate).undefined_value();
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableDelete) {
HandleScope scope(isolate);
Handle<SwissNameDictionary> table = args.at<SwissNameDictionary>(0);
Handle<Smi> index = args.at<Smi>(1);
InternalIndex i(Smi::ToInt(*index));
return *SwissNameDictionary::DeleteEntry(isolate, table, i);
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableEquals) {
HandleScope scope(isolate);
Handle<SwissNameDictionary> table = args.at<SwissNameDictionary>(0);
Handle<SwissNameDictionary> other = args.at<SwissNameDictionary>(1);
return Smi::FromInt(table->EqualsForTesting(*other));
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableElementsCount) {
HandleScope scope(isolate);
Handle<SwissNameDictionary> table = args.at<SwissNameDictionary>(0);
return Smi::FromInt(table->NumberOfElements());
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableValueAt) {
HandleScope scope(isolate);
Handle<SwissNameDictionary> table = args.at<SwissNameDictionary>(0);
Handle<Smi> index = args.at<Smi>(1);
return table->ValueAt(InternalIndex(Smi::ToInt(*index)));
}
// TODO(v8:11330) This is only here while the CSA/Torque implementaton of
// SwissNameDictionary is work in progress.
RUNTIME_FUNCTION(Runtime_SwissTableDetailsAt) {
HandleScope scope(isolate);
Handle<SwissNameDictionary> table = args.at<SwissNameDictionary>(0);
Handle<Smi> index = args.at<Smi>(1);
PropertyDetails d = table->DetailsAt(InternalIndex(Smi::ToInt(*index)));
return d.AsSmi();
}
} // namespace internal
} // namespace v8
......@@ -343,7 +343,16 @@ namespace internal {
F(ToNumeric, 1, 1) \
I(ToObject, 1, 1) \
I(ToString, 1, 1) \
F(TryMigrateInstance, 1, 1)
F(TryMigrateInstance, 1, 1) \
F(SwissTableAdd, 4, 1) \
F(SwissTableAllocate, 1, 1) \
F(SwissTableDelete, 2, 1) \
F(SwissTableDetailsAt, 2, 1) \
F(SwissTableElementsCount, 1, 1) \
F(SwissTableEquals, 2, 1) \
F(SwissTableFindEntry, 2, 1) \
F(SwissTableUpdate, 4, 1) \
F(SwissTableValueAt, 2, 1)
#define FOR_EACH_INTRINSIC_OPERATORS(F, I) \
F(Add, 2, 1) \
......
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