[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:
Frank Emrich 2021-03-24 19:15:00 +01:00 committed by Commit Bot
parent c10aa432b8
commit d1e751e233
5 changed files with 485 additions and 3 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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); }

View File

@ -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;
}
}