[dict-proto] CSA/Torque implementation of SwissNameDictionary, pt. 3
This CL adds: a) Allocation and Copy operations on the CSA side b) Helpers needed for that on both the CSA and Torque side Bug: v8:11330 Change-Id: I322c84e1762d2d3503a0d4809c6e1c0bc9d442e1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2775563 Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#73651}
This commit is contained in:
parent
c10aa432b8
commit
d1e751e233
@ -457,12 +457,13 @@ const kNameDictionaryInitialCapacity:
|
||||
constexpr int32 generates 'NameDictionary::kInitialCapacity';
|
||||
const kSwissNameDictionaryInitialCapacity:
|
||||
constexpr int32 generates 'SwissNameDictionary::kInitialCapacity';
|
||||
const kSwissNameDictionaryGroupWidth:
|
||||
constexpr int32 generates 'SwissNameDictionary::kGroupWidth';
|
||||
|
||||
const kWasmArrayHeaderSize:
|
||||
constexpr int32 generates 'WasmArray::kHeaderSize';
|
||||
|
||||
const kHeapObjectHeaderSize:
|
||||
constexpr int32 generates 'HeapObject::kHeaderSize';
|
||||
|
||||
const kDictModePrototypes:
|
||||
constexpr bool generates 'V8_DICT_MODE_PROTOTYPES_BOOL';
|
||||
|
||||
@ -1094,6 +1095,7 @@ extern macro IntPtrToTaggedIndex(intptr): TaggedIndex;
|
||||
extern macro TaggedIndexToSmi(TaggedIndex): Smi;
|
||||
extern macro SmiToTaggedIndex(Smi): TaggedIndex;
|
||||
extern macro RoundIntPtrToFloat64(intptr): float64;
|
||||
extern macro IntPtrRoundUpToPowerOfTwo32(intptr): intptr;
|
||||
extern macro ChangeFloat32ToFloat64(float32): float64;
|
||||
extern macro ChangeNumberToFloat64(Number): float64;
|
||||
extern macro ChangeNumberToUint32(Number): uint32;
|
||||
|
@ -3344,8 +3344,33 @@ TNode<UintPtrT> CodeStubAssembler::LoadBigIntDigit(TNode<BigInt> bigint,
|
||||
return LoadObjectField<UintPtrT>(bigint, offset);
|
||||
}
|
||||
|
||||
TNode<ByteArray> CodeStubAssembler::AllocateNonEmptyByteArray(
|
||||
TNode<UintPtrT> length, AllocationFlags flags) {
|
||||
CSA_ASSERT(this, WordNotEqual(length, IntPtrConstant(0)));
|
||||
|
||||
Comment("AllocateNonEmptyByteArray");
|
||||
TVARIABLE(Object, var_result);
|
||||
|
||||
TNode<IntPtrT> raw_size =
|
||||
GetArrayAllocationSize(Signed(length), UINT8_ELEMENTS,
|
||||
ByteArray::kHeaderSize + kObjectAlignmentMask);
|
||||
TNode<IntPtrT> size =
|
||||
WordAnd(raw_size, IntPtrConstant(~kObjectAlignmentMask));
|
||||
|
||||
TNode<HeapObject> result = Allocate(size, flags);
|
||||
|
||||
DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kByteArrayMap));
|
||||
StoreMapNoWriteBarrier(result, RootIndex::kByteArrayMap);
|
||||
StoreObjectFieldNoWriteBarrier(result, ByteArray::kLengthOffset,
|
||||
SmiTag(Signed(length)));
|
||||
|
||||
return CAST(result);
|
||||
}
|
||||
|
||||
TNode<ByteArray> CodeStubAssembler::AllocateByteArray(TNode<UintPtrT> length,
|
||||
AllocationFlags flags) {
|
||||
// TODO(ishell): unify with AllocateNonEmptyByteArray().
|
||||
|
||||
Comment("AllocateByteArray");
|
||||
TVARIABLE(Object, var_result);
|
||||
|
||||
@ -14552,6 +14577,338 @@ TNode<SwissNameDictionary> CodeStubAssembler::AllocateSwissNameDictionary(
|
||||
return AllocateSwissNameDictionary(IntPtrConstant(at_least_space_for));
|
||||
}
|
||||
|
||||
TNode<SwissNameDictionary>
|
||||
CodeStubAssembler::AllocateSwissNameDictionaryWithCapacity(
|
||||
TNode<IntPtrT> capacity) {
|
||||
Comment("[ AllocateSwissNameDictionaryWithCapacity");
|
||||
CSA_ASSERT(this, WordIsPowerOfTwo(capacity));
|
||||
CSA_ASSERT(this, UintPtrGreaterThanOrEqual(
|
||||
capacity,
|
||||
IntPtrConstant(SwissNameDictionary::kInitialCapacity)));
|
||||
CSA_ASSERT(this,
|
||||
UintPtrLessThanOrEqual(
|
||||
capacity, IntPtrConstant(SwissNameDictionary::MaxCapacity())));
|
||||
|
||||
Comment("Size check.");
|
||||
intptr_t capacity_constant;
|
||||
if (ToParameterConstant(capacity, &capacity_constant)) {
|
||||
CHECK_LE(capacity_constant, SwissNameDictionary::MaxCapacity());
|
||||
} else {
|
||||
Label if_out_of_memory(this, Label::kDeferred), next(this);
|
||||
Branch(UintPtrGreaterThan(
|
||||
capacity, IntPtrConstant(SwissNameDictionary::MaxCapacity())),
|
||||
&if_out_of_memory, &next);
|
||||
|
||||
BIND(&if_out_of_memory);
|
||||
CallRuntime(Runtime::kFatalProcessOutOfMemoryInAllocateRaw,
|
||||
NoContextConstant());
|
||||
Unreachable();
|
||||
|
||||
BIND(&next);
|
||||
}
|
||||
|
||||
// TODO(v8:11330) Consider adding dedicated handling for constant capacties,
|
||||
// similar to AllocateOrderedHashTableWithCapacity.
|
||||
|
||||
// We must allocate the ByteArray first. Otherwise, allocating the ByteArray
|
||||
// may trigger GC, which may try to verify the un-initialized
|
||||
// SwissNameDictionary.
|
||||
Comment("Meta table allocation.");
|
||||
TNode<IntPtrT> meta_table_payload_size =
|
||||
SwissNameDictionaryMetaTableSizeFor(capacity);
|
||||
|
||||
TNode<ByteArray> meta_table =
|
||||
AllocateNonEmptyByteArray(Unsigned(meta_table_payload_size),
|
||||
AllocationFlag::kAllowLargeObjectAllocation);
|
||||
|
||||
Comment("SwissNameDictionary allocation.");
|
||||
TNode<IntPtrT> total_size = SwissNameDictionarySizeFor(capacity);
|
||||
|
||||
TNode<SwissNameDictionary> table = UncheckedCast<SwissNameDictionary>(
|
||||
Allocate(total_size, kAllowLargeObjectAllocation));
|
||||
|
||||
StoreMapNoWriteBarrier(table, RootIndex::kSwissNameDictionaryMap);
|
||||
|
||||
Comment(
|
||||
"Initialize the hash, capacity, meta table pointer, and number of "
|
||||
"(deleted) elements.");
|
||||
|
||||
StoreSwissNameDictionaryHash(table,
|
||||
Uint32Constant(PropertyArray::kNoHashSentinel));
|
||||
StoreSwissNameDictionaryCapacity(table, TruncateIntPtrToInt32(capacity));
|
||||
StoreSwissNameDictionaryMetaTable(table, meta_table);
|
||||
|
||||
// Set present and deleted element count without doing branching needed for
|
||||
// meta table access twice.
|
||||
MetaTableAccessFunction builder = [&](MetaTableAccessor& mta) {
|
||||
mta.Store(meta_table, SwissNameDictionary::kMetaTableElementCountFieldIndex,
|
||||
Uint32Constant(0));
|
||||
mta.Store(meta_table,
|
||||
SwissNameDictionary::kMetaTableDeletedElementCountFieldIndex,
|
||||
Uint32Constant(0));
|
||||
};
|
||||
GenerateMetaTableAccess(this, capacity, builder);
|
||||
|
||||
Comment("Initialize the ctrl table.");
|
||||
|
||||
TNode<IntPtrT> ctrl_table_start_offset_minus_tag =
|
||||
SwissNameDictionaryCtrlTableStartOffsetMT(capacity);
|
||||
|
||||
TNode<IntPtrT> table_address_with_tag = BitcastTaggedToWord(table);
|
||||
TNode<IntPtrT> ctrl_table_size_bytes =
|
||||
IntPtrAdd(capacity, IntPtrConstant(SwissNameDictionary::kGroupWidth));
|
||||
TNode<IntPtrT> ctrl_table_start_ptr =
|
||||
IntPtrAdd(table_address_with_tag, ctrl_table_start_offset_minus_tag);
|
||||
TNode<IntPtrT> ctrl_table_end_ptr =
|
||||
IntPtrAdd(ctrl_table_start_ptr, ctrl_table_size_bytes);
|
||||
|
||||
// |ctrl_table_size_bytes| (= capacity + kGroupWidth) is divisble by four:
|
||||
STATIC_ASSERT(SwissNameDictionary::kGroupWidth % 4 == 0);
|
||||
STATIC_ASSERT(SwissNameDictionary::kInitialCapacity % 4 == 0);
|
||||
|
||||
// TODO(v8:11330) For all capacities except 4, we know that
|
||||
// |ctrl_table_size_bytes| is divisible by 8. Consider initializing the ctrl
|
||||
// table with WordTs in those cases. Alternatively, always initialize as many
|
||||
// bytes as possbible with WordT and then, if necessary, the remaining 4 bytes
|
||||
// with Word32T.
|
||||
|
||||
constexpr uint8_t kEmpty = swiss_table::Ctrl::kEmpty;
|
||||
constexpr uint32_t kEmpty32 =
|
||||
(kEmpty << 24) | (kEmpty << 16) | (kEmpty << 8) | kEmpty;
|
||||
TNode<Int32T> empty32 = Int32Constant(kEmpty32);
|
||||
BuildFastLoop<IntPtrT>(
|
||||
ctrl_table_start_ptr, ctrl_table_end_ptr,
|
||||
[=](TNode<IntPtrT> current) {
|
||||
UnsafeStoreNoWriteBarrier(MachineRepresentation::kWord32, current,
|
||||
empty32);
|
||||
},
|
||||
sizeof(uint32_t), IndexAdvanceMode::kPost);
|
||||
|
||||
Comment("Initialize the data table.");
|
||||
|
||||
TNode<IntPtrT> data_table_start_offset_minus_tag =
|
||||
SwissNameDictionaryDataTableStartOffsetMT();
|
||||
TNode<IntPtrT> data_table_ptr =
|
||||
IntPtrAdd(table_address_with_tag, data_table_start_offset_minus_tag);
|
||||
TNode<IntPtrT> data_table_size = IntPtrMul(
|
||||
IntPtrConstant(SwissNameDictionary::kDataTableEntryCount * kTaggedSize),
|
||||
capacity);
|
||||
|
||||
StoreFieldsNoWriteBarrier(data_table_ptr,
|
||||
IntPtrAdd(data_table_ptr, data_table_size),
|
||||
TheHoleConstant());
|
||||
|
||||
Comment("AllocateSwissNameDictionaryWithCapacity ]");
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
TNode<SwissNameDictionary> CodeStubAssembler::CopySwissNameDictionary(
|
||||
TNode<SwissNameDictionary> original) {
|
||||
Comment("[ CopySwissNameDictionary");
|
||||
|
||||
TNode<IntPtrT> capacity =
|
||||
Signed(ChangeUint32ToWord(LoadSwissNameDictionaryCapacity(original)));
|
||||
|
||||
// We must allocate the ByteArray first. Otherwise, allocating the ByteArray
|
||||
// may trigger GC, which may try to verify the un-initialized
|
||||
// SwissNameDictionary.
|
||||
Comment("Meta table allocation.");
|
||||
TNode<IntPtrT> meta_table_payload_size =
|
||||
SwissNameDictionaryMetaTableSizeFor(capacity);
|
||||
|
||||
TNode<ByteArray> meta_table =
|
||||
AllocateNonEmptyByteArray(Unsigned(meta_table_payload_size),
|
||||
AllocationFlag::kAllowLargeObjectAllocation);
|
||||
|
||||
Comment("SwissNameDictionary allocation.");
|
||||
TNode<IntPtrT> total_size = SwissNameDictionarySizeFor(capacity);
|
||||
|
||||
TNode<SwissNameDictionary> table = UncheckedCast<SwissNameDictionary>(
|
||||
Allocate(total_size, kAllowLargeObjectAllocation));
|
||||
|
||||
StoreMapNoWriteBarrier(table, RootIndex::kSwissNameDictionaryMap);
|
||||
|
||||
Comment("Copy the hash and capacity.");
|
||||
|
||||
StoreSwissNameDictionaryHash(table, LoadSwissNameDictionaryHash(original));
|
||||
StoreSwissNameDictionaryCapacity(table, TruncateIntPtrToInt32(capacity));
|
||||
StoreSwissNameDictionaryMetaTable(table, meta_table);
|
||||
// Not setting up number of (deleted elements), copying whole meta table
|
||||
// instead.
|
||||
|
||||
TNode<ExternalReference> memcpy =
|
||||
ExternalConstant(ExternalReference::libc_memcpy_function());
|
||||
|
||||
TNode<IntPtrT> old_table_address_with_tag = BitcastTaggedToWord(original);
|
||||
TNode<IntPtrT> new_table_address_with_tag = BitcastTaggedToWord(table);
|
||||
|
||||
TNode<IntPtrT> ctrl_table_start_offset_minus_tag =
|
||||
SwissNameDictionaryCtrlTableStartOffsetMT(capacity);
|
||||
|
||||
TNode<IntPtrT> ctrl_table_size_bytes =
|
||||
IntPtrAdd(capacity, IntPtrConstant(SwissNameDictionary::kGroupWidth));
|
||||
|
||||
Comment("Copy the ctrl table.");
|
||||
{
|
||||
TNode<IntPtrT> old_ctrl_table_start_ptr = IntPtrAdd(
|
||||
old_table_address_with_tag, ctrl_table_start_offset_minus_tag);
|
||||
TNode<IntPtrT> new_ctrl_table_start_ptr = IntPtrAdd(
|
||||
new_table_address_with_tag, ctrl_table_start_offset_minus_tag);
|
||||
|
||||
CallCFunction(
|
||||
memcpy, MachineType::Pointer(),
|
||||
std::make_pair(MachineType::Pointer(), new_ctrl_table_start_ptr),
|
||||
std::make_pair(MachineType::Pointer(), old_ctrl_table_start_ptr),
|
||||
std::make_pair(MachineType::UintPtr(), ctrl_table_size_bytes));
|
||||
}
|
||||
|
||||
Comment("Copy the data table.");
|
||||
{
|
||||
TNode<IntPtrT> start_offset =
|
||||
IntPtrConstant(SwissNameDictionary::DataTableStartOffset());
|
||||
TNode<IntPtrT> data_table_size = IntPtrMul(
|
||||
IntPtrConstant(SwissNameDictionary::kDataTableEntryCount * kTaggedSize),
|
||||
capacity);
|
||||
|
||||
BuildFastLoop<IntPtrT>(
|
||||
start_offset, IntPtrAdd(start_offset, data_table_size),
|
||||
[=](TNode<IntPtrT> offset) {
|
||||
TNode<Object> table_field = LoadObjectField(original, offset);
|
||||
StoreObjectField(table, offset, table_field);
|
||||
},
|
||||
kTaggedSize, IndexAdvanceMode::kPost);
|
||||
}
|
||||
|
||||
Comment("Copy the meta table");
|
||||
{
|
||||
TNode<IntPtrT> old_meta_table_address_with_tag =
|
||||
BitcastTaggedToWord(LoadSwissNameDictionaryMetaTable(original));
|
||||
TNode<IntPtrT> new_meta_table_address_with_tag =
|
||||
BitcastTaggedToWord(meta_table);
|
||||
|
||||
TNode<IntPtrT> meta_table_size =
|
||||
SwissNameDictionaryMetaTableSizeFor(capacity);
|
||||
|
||||
TNode<IntPtrT> old_data_start =
|
||||
IntPtrAdd(old_meta_table_address_with_tag,
|
||||
IntPtrConstant(ByteArray::kHeaderSize - kHeapObjectTag));
|
||||
TNode<IntPtrT> new_data_start =
|
||||
IntPtrAdd(new_meta_table_address_with_tag,
|
||||
IntPtrConstant(ByteArray::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
CallCFunction(memcpy, MachineType::Pointer(),
|
||||
std::make_pair(MachineType::Pointer(), new_data_start),
|
||||
std::make_pair(MachineType::Pointer(), old_data_start),
|
||||
std::make_pair(MachineType::UintPtr(), meta_table_size));
|
||||
}
|
||||
|
||||
Comment("Copy the PropertyDetails table");
|
||||
{
|
||||
TNode<IntPtrT> property_details_start_offset_minus_tag =
|
||||
SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(table, capacity,
|
||||
IntPtrConstant(0));
|
||||
|
||||
// Offset to property details entry
|
||||
TVARIABLE(IntPtrT, details_table_offset,
|
||||
property_details_start_offset_minus_tag);
|
||||
|
||||
TNode<IntPtrT> start = ctrl_table_start_offset_minus_tag;
|
||||
|
||||
VariableList in_loop_variables({&details_table_offset}, zone());
|
||||
BuildFastLoop<IntPtrT>(
|
||||
in_loop_variables, start, IntPtrAdd(start, ctrl_table_size_bytes),
|
||||
[&](TNode<IntPtrT> ctrl_table_offset) {
|
||||
TNode<Uint8T> ctrl = UncheckedCast<Uint8T>(LoadFromObject(
|
||||
MachineType::Uint8(), original, ctrl_table_offset));
|
||||
|
||||
// TODO(v8:11330) Entries in the PropertyDetails table may be
|
||||
// uninitialized if the corresponding buckets in the data/ctrl table
|
||||
// are empty. Therefore, to avoid accessing un-initialized memory
|
||||
// here, we need to check the ctrl table to determine whether we
|
||||
// should copy a certain PropertyDetails entry or not.
|
||||
// TODO(v8:11330) If this function becomes performance-critical, we
|
||||
// may consider always initializing the PropertyDetails table entirely
|
||||
// during allocation, to avoid the branching during copying.
|
||||
Label done(this);
|
||||
// |kNotFullMask| catches kEmpty and kDeleted, both of which indicate
|
||||
// entries that we don't want to copy the PropertyDetails for.
|
||||
GotoIf(IsSetWord32(ctrl, swiss_table::kNotFullMask), &done);
|
||||
|
||||
TNode<Uint8T> details = UncheckedCast<Uint8T>(LoadFromObject(
|
||||
MachineType::Uint8(), original, details_table_offset.value()));
|
||||
|
||||
StoreToObject(MachineRepresentation::kWord8, table,
|
||||
details_table_offset.value(), details,
|
||||
StoreToObjectWriteBarrier::kNone);
|
||||
Goto(&done);
|
||||
BIND(&done);
|
||||
|
||||
details_table_offset = IntPtrAdd(details_table_offset.value(),
|
||||
IntPtrConstant(kOneByteSize));
|
||||
},
|
||||
kOneByteSize, IndexAdvanceMode::kPost);
|
||||
}
|
||||
|
||||
Comment("CopySwissNameDictionary ]");
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
TNode<IntPtrT> CodeStubAssembler::SwissNameDictionaryOffsetIntoDataTableMT(
|
||||
TNode<SwissNameDictionary> dict, TNode<IntPtrT> index, int field_index) {
|
||||
TNode<IntPtrT> data_table_start = SwissNameDictionaryDataTableStartOffsetMT();
|
||||
|
||||
TNode<IntPtrT> offset_within_data_table = IntPtrMul(
|
||||
index,
|
||||
IntPtrConstant(SwissNameDictionary::kDataTableEntryCount * kTaggedSize));
|
||||
|
||||
if (field_index != 0) {
|
||||
offset_within_data_table = IntPtrAdd(
|
||||
offset_within_data_table, IntPtrConstant(field_index * kTaggedSize));
|
||||
}
|
||||
|
||||
return IntPtrAdd(data_table_start, offset_within_data_table);
|
||||
}
|
||||
|
||||
TNode<IntPtrT>
|
||||
CodeStubAssembler::SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(
|
||||
TNode<SwissNameDictionary> dict, TNode<IntPtrT> capacity,
|
||||
TNode<IntPtrT> index) {
|
||||
CSA_ASSERT(this,
|
||||
WordEqual(capacity, ChangeUint32ToWord(
|
||||
LoadSwissNameDictionaryCapacity(dict))));
|
||||
|
||||
TNode<IntPtrT> data_table_start = SwissNameDictionaryDataTableStartOffsetMT();
|
||||
|
||||
TNode<IntPtrT> gw = IntPtrConstant(SwissNameDictionary::kGroupWidth);
|
||||
TNode<IntPtrT> data_and_ctrl_table_size = IntPtrAdd(
|
||||
IntPtrMul(capacity,
|
||||
IntPtrConstant(kOneByteSize +
|
||||
SwissNameDictionary::kDataTableEntryCount *
|
||||
kTaggedSize)),
|
||||
gw);
|
||||
|
||||
TNode<IntPtrT> property_details_table_start =
|
||||
IntPtrAdd(data_table_start, data_and_ctrl_table_size);
|
||||
|
||||
CSA_ASSERT(
|
||||
this,
|
||||
WordEqual(FieldSliceSwissNameDictionaryPropertyDetailsTable(dict).offset,
|
||||
// Our calculation subtracted the tag, Torque's offset didn't.
|
||||
IntPtrAdd(property_details_table_start,
|
||||
IntPtrConstant(kHeapObjectTag))));
|
||||
|
||||
TNode<IntPtrT> offset_within_details_table = index;
|
||||
return IntPtrAdd(property_details_table_start, offset_within_details_table);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreSwissNameDictionaryCapacity(
|
||||
TNode<SwissNameDictionary> table, TNode<Int32T> capacity) {
|
||||
StoreObjectFieldNoWriteBarrier<Word32T>(
|
||||
table, SwissNameDictionary::CapacityOffset(), capacity);
|
||||
}
|
||||
|
||||
TNode<Uint64T> CodeStubAssembler::LoadSwissNameDictionaryCtrlTableGroup(
|
||||
TNode<IntPtrT> address) {
|
||||
TNode<RawPtrT> ptr = ReinterpretCast<RawPtrT>(address);
|
||||
|
@ -1728,6 +1728,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<UintPtrT> LoadBigIntDigit(TNode<BigInt> bigint,
|
||||
TNode<IntPtrT> digit_index);
|
||||
|
||||
// Allocate a ByteArray with the given non-zero length.
|
||||
TNode<ByteArray> AllocateNonEmptyByteArray(TNode<UintPtrT> length,
|
||||
AllocationFlags flags);
|
||||
|
||||
// Allocate a ByteArray with the given length.
|
||||
TNode<ByteArray> AllocateByteArray(TNode<UintPtrT> length,
|
||||
AllocationFlags flags = kNone);
|
||||
@ -3723,6 +3727,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<SwissNameDictionary> AllocateSwissNameDictionary(
|
||||
int at_least_space_for);
|
||||
|
||||
TNode<SwissNameDictionary> AllocateSwissNameDictionaryWithCapacity(
|
||||
TNode<IntPtrT> capacity);
|
||||
|
||||
// MT stands for "minus tag".
|
||||
TNode<IntPtrT> SwissNameDictionaryOffsetIntoDataTableMT(
|
||||
TNode<SwissNameDictionary> dict, TNode<IntPtrT> index, int field_index);
|
||||
|
||||
// MT stands for "minus tag".
|
||||
TNode<IntPtrT> SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(
|
||||
TNode<SwissNameDictionary> dict, TNode<IntPtrT> capacity,
|
||||
TNode<IntPtrT> index);
|
||||
|
||||
TNode<IntPtrT> LoadSwissNameDictionaryNumberOfElements(
|
||||
TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity);
|
||||
|
||||
@ -3743,12 +3759,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<Uint32T> SwissNameDictionaryUpdateCountsForDeletion(
|
||||
TNode<ByteArray> meta_table, TNode<IntPtrT> capacity);
|
||||
|
||||
void StoreSwissNameDictionaryCapacity(TNode<SwissNameDictionary> table,
|
||||
TNode<Int32T> capacity);
|
||||
|
||||
void StoreSwissNameDictionaryEnumToEntryMapping(
|
||||
TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity,
|
||||
TNode<IntPtrT> enum_index, TNode<Int32T> entry);
|
||||
|
||||
TNode<Uint64T> LoadSwissNameDictionaryCtrlTableGroup(TNode<IntPtrT> address);
|
||||
|
||||
TNode<SwissNameDictionary> CopySwissNameDictionary(
|
||||
TNode<SwissNameDictionary> original);
|
||||
|
||||
private:
|
||||
friend class CodeStubArguments;
|
||||
|
||||
|
@ -202,6 +202,11 @@ static_assert(kDeleted == -2,
|
||||
// Table implementations rely on this being 7.
|
||||
static constexpr int kH2Bits = 7;
|
||||
|
||||
static constexpr int kNotFullMask = (1 << kH2Bits);
|
||||
static_assert(
|
||||
kEmpty & kDeleted & kSentinel & kNotFullMask,
|
||||
"Special markers need to have the MSB to make checking for them efficient");
|
||||
|
||||
// Extracts H1 from the given overall hash, which means discarding the lowest 7
|
||||
// bits of the overall hash. H1 is used to determine the first group to probe.
|
||||
inline static uint32_t H1(uint32_t hash) { return (hash >> kH2Bits); }
|
||||
|
@ -10,6 +10,102 @@ extern class SwissNameDictionary extends HeapObject {
|
||||
const capacity: int32;
|
||||
meta_table: ByteArray;
|
||||
data_table[Convert<intptr>(capacity) * 2]: JSAny|TheHole;
|
||||
ctrl_table[Convert<intptr>(capacity) + kSwissNameDictionaryGroupWidth]: uint8;
|
||||
ctrl_table[Convert<intptr>(capacity) + swiss_table::kGroupWidth]: uint8;
|
||||
property_details_table[Convert<intptr>(capacity)]: uint8;
|
||||
}
|
||||
|
||||
namespace swiss_table {
|
||||
|
||||
const kDataTableEntryCount: constexpr intptr
|
||||
generates 'SwissNameDictionary::kDataTableEntryCount';
|
||||
|
||||
const kMax1ByteMetaTableCapacity: constexpr int32
|
||||
generates 'SwissNameDictionary::kMax1ByteMetaTableCapacity';
|
||||
|
||||
const kMax2ByteMetaTableCapacity: constexpr int32
|
||||
generates 'SwissNameDictionary::kMax2ByteMetaTableCapacity';
|
||||
|
||||
// Counterpart for SwissNameDictionary::CapacityFor in C++.
|
||||
@export
|
||||
macro SwissNameDictionaryCapacityFor(atLeastSpaceFor: intptr): intptr {
|
||||
if (atLeastSpaceFor <= 4) {
|
||||
if (atLeastSpaceFor == 0) {
|
||||
return 0;
|
||||
} else if (atLeastSpaceFor < kSwissNameDictionaryInitialCapacity) {
|
||||
return 4;
|
||||
} else if (FromConstexpr<bool>(kGroupWidth == 16)) {
|
||||
assert(atLeastSpaceFor == 4);
|
||||
return 4;
|
||||
} else if (FromConstexpr<bool>(kGroupWidth == 8)) {
|
||||
assert(atLeastSpaceFor == 4);
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
const nonNormalized = atLeastSpaceFor + atLeastSpaceFor / 7;
|
||||
return IntPtrRoundUpToPowerOfTwo32(nonNormalized);
|
||||
}
|
||||
|
||||
// Counterpart for SwissNameDictionary::MaxUsableCapacity in C++.
|
||||
@export
|
||||
macro SwissNameDictionaryMaxUsableCapacity(capacity: intptr): intptr {
|
||||
assert(capacity == 0 || capacity >= kSwissNameDictionaryInitialCapacity);
|
||||
if (FromConstexpr<bool>(kGroupWidth == 8) && capacity == 4) {
|
||||
// If the group size is 16 we can fully utilize capacity 4: There will be
|
||||
// enough kEmpty entries in the ctrl table.
|
||||
return 3;
|
||||
}
|
||||
return capacity - capacity / 8;
|
||||
}
|
||||
|
||||
// Counterpart for SwissNameDictionary::SizeFor in C++.
|
||||
@export
|
||||
macro SwissNameDictionarySizeFor(capacity: intptr): intptr {
|
||||
const constant: constexpr int32 = kHeapObjectHeaderSize + 8 + kTaggedSize;
|
||||
const dynamic: intptr =
|
||||
capacity * FromConstexpr<intptr>(2 * kTaggedSize + 2) +
|
||||
FromConstexpr<intptr>(kGroupWidth);
|
||||
return constant + dynamic;
|
||||
}
|
||||
|
||||
// Counterpart for SwissNameDictionary::MetaTableSizePerEntryFor in C++.
|
||||
@export
|
||||
macro SwissNameDictionaryMetaTableSizePerEntryFor(capacity: intptr): intptr {
|
||||
if (capacity <= kMax1ByteMetaTableCapacity) {
|
||||
return 1;
|
||||
} else if (capacity <= kMax2ByteMetaTableCapacity) {
|
||||
return 2;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Counterpart for SwissNameDictionary::MetaTableSizeFor in C++.
|
||||
@export
|
||||
macro SwissNameDictionaryMetaTableSizeFor(capacity: intptr): intptr {
|
||||
const perEntry: intptr =
|
||||
SwissNameDictionaryMetaTableSizePerEntryFor(capacity);
|
||||
const maxUsable: intptr =
|
||||
Convert<intptr>(SwissNameDictionaryMaxUsableCapacity(capacity));
|
||||
|
||||
return (2 + maxUsable) * perEntry;
|
||||
}
|
||||
|
||||
//
|
||||
// Offsets. MT stands for "minus tag"
|
||||
//
|
||||
|
||||
const kDataTableStartOffsetMT: constexpr intptr
|
||||
generates 'SwissNameDictionary::DataTableStartOffset() - kHeapObjectTag';
|
||||
|
||||
@export
|
||||
macro SwissNameDictionaryDataTableStartOffsetMT(): intptr {
|
||||
return kDataTableStartOffsetMT;
|
||||
}
|
||||
|
||||
@export
|
||||
macro SwissNameDictionaryCtrlTableStartOffsetMT(capacity: intptr): intptr {
|
||||
return kDataTableStartOffsetMT +
|
||||
kDataTableEntryCount * FromConstexpr<intptr>(kTaggedSize) * capacity;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user