diff --git a/src/heap/factory.cc b/src/heap/factory.cc index abea6f3fae..f151aef1e6 100644 --- a/src/heap/factory.cc +++ b/src/heap/factory.cc @@ -441,7 +441,7 @@ Handle Factory::NewSmallOrderedHashSet( CHECK_LE(capacity, SmallOrderedHashSet::kMaxCapacity); DCHECK_EQ(0, capacity % SmallOrderedHashSet::kLoadFactor); - int size = SmallOrderedHashSet::Size(capacity); + int size = SmallOrderedHashSet::SizeFor(capacity); Map* map = *small_ordered_hash_set_map(); HeapObject* result = AllocateRawWithImmortalMap(size, pretenure, map); Handle table(SmallOrderedHashSet::cast(result), @@ -456,7 +456,7 @@ Handle Factory::NewSmallOrderedHashMap( CHECK_LE(capacity, SmallOrderedHashMap::kMaxCapacity); DCHECK_EQ(0, capacity % SmallOrderedHashMap::kLoadFactor); - int size = SmallOrderedHashMap::Size(capacity); + int size = SmallOrderedHashMap::SizeFor(capacity); Map* map = *small_ordered_hash_map_map(); HeapObject* result = AllocateRawWithImmortalMap(size, pretenure, map); Handle table(SmallOrderedHashMap::cast(result), diff --git a/src/objects-body-descriptors-inl.h b/src/objects-body-descriptors-inl.h index 1efcd44955..c4b7738dcb 100644 --- a/src/objects-body-descriptors-inl.h +++ b/src/objects-body-descriptors-inl.h @@ -181,7 +181,8 @@ class SmallOrderedHashTable::BodyDescriptor final public: static bool IsValidSlot(Map* map, HeapObject* obj, int offset) { Derived* table = reinterpret_cast(obj); - if (offset < table->GetDataTableStartOffset()) return false; + if (offset < kDataTableStartOffset) return false; + if (offset >= table->GetBucketsStartOffset()) return false; return IsValidSlotImpl(map, obj, offset); } @@ -189,7 +190,7 @@ class SmallOrderedHashTable::BodyDescriptor final static inline void IterateBody(Map* map, HeapObject* obj, int object_size, ObjectVisitor* v) { Derived* table = reinterpret_cast(obj); - int start = table->GetDataTableStartOffset(); + int start = kDataTableStartOffset; for (int i = 0; i < table->Capacity(); i++) { IteratePointer(obj, start + (i * kPointerSize), v); } @@ -197,7 +198,7 @@ class SmallOrderedHashTable::BodyDescriptor final static inline int SizeOf(Map* map, HeapObject* obj) { Derived* table = reinterpret_cast(obj); - return table->Size(); + return table->SizeFor(table->Capacity()); } }; diff --git a/src/objects-inl.h b/src/objects-inl.h index 56d6673435..7c74900b5d 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2252,14 +2252,16 @@ int HeapObject::SizeFromMap(Map* map) const { instance_type); } if (instance_type == SMALL_ORDERED_HASH_SET_TYPE) { - return reinterpret_cast(this)->Size(); + return SmallOrderedHashSet::SizeFor( + reinterpret_cast(this)->Capacity()); } if (instance_type == PROPERTY_ARRAY_TYPE) { return PropertyArray::SizeFor( reinterpret_cast(this)->synchronized_length()); } if (instance_type == SMALL_ORDERED_HASH_MAP_TYPE) { - return reinterpret_cast(this)->Size(); + return SmallOrderedHashMap::SizeFor( + reinterpret_cast(this)->Capacity()); } if (instance_type == FEEDBACK_VECTOR_TYPE) { return FeedbackVector::SizeFor( @@ -2609,9 +2611,10 @@ void Foreign::set_foreign_address(Address value) { template void SmallOrderedHashTable::SetDataEntry(int entry, int relative_index, Object* value) { - int entry_offset = GetDataEntryOffset(entry, relative_index); + Address entry_offset = + kHeaderSize + GetDataEntryOffset(entry, relative_index); RELAXED_WRITE_FIELD(this, entry_offset, value); - WRITE_BARRIER(GetHeap(), this, entry_offset, value); + WRITE_BARRIER(GetHeap(), this, static_cast(entry_offset), value); } ACCESSORS(JSGeneratorObject, function, JSFunction, kFunctionOffset) diff --git a/src/objects.cc b/src/objects.cc index e35969efce..bf95f5de3c 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -3573,18 +3573,6 @@ void HeapNumber::HeapNumberPrint(std::ostream& os) { // NOLINT os << value(); } -#define FIELD_ADDR(p, offset) \ - (reinterpret_cast(p) + offset - kHeapObjectTag) - -#define READ_INT32_FIELD(p, offset) \ - (*reinterpret_cast(FIELD_ADDR(p, offset))) - -#define READ_INT64_FIELD(p, offset) \ - (*reinterpret_cast(FIELD_ADDR(p, offset))) - -#define READ_BYTE_FIELD(p, offset) \ - (*reinterpret_cast(FIELD_ADDR(p, offset))) - String* JSReceiver::class_name() { if (IsFunction()) return GetHeap()->Function_string(); if (IsJSArgumentsObject()) return GetHeap()->Arguments_string(); @@ -18570,6 +18558,7 @@ SmallOrderedHashTable::Allocate(Isolate* isolate, template void SmallOrderedHashTable::Initialize(Isolate* isolate, int capacity) { + DisallowHeapAllocation no_gc; int num_buckets = capacity / kLoadFactor; int num_chains = capacity; @@ -18577,12 +18566,12 @@ void SmallOrderedHashTable::Initialize(Isolate* isolate, SetNumberOfElements(0); SetNumberOfDeletedElements(0); - byte* hashtable_start = - FIELD_ADDR(this, kHeaderSize + (kBucketsStartOffset * kOneByteSize)); - memset(hashtable_start, kNotFound, num_buckets + num_chains); + Address hashtable_start = GetHashTableStartAddress(capacity); + memset(reinterpret_cast(hashtable_start), kNotFound, + num_buckets + num_chains); if (isolate->heap()->InNewSpace(this)) { - MemsetPointer(RawField(this, GetDataTableStartOffset()), + MemsetPointer(RawField(this, kHeaderSize + kDataTableStartOffset), isolate->heap()->the_hole_value(), capacity * Derived::kEntrySize); } else { @@ -18601,6 +18590,12 @@ void SmallOrderedHashTable::Initialize(Isolate* isolate, for (int i = 0; i < num_chains; ++i) { DCHECK_EQ(kNotFound, GetNextEntry(i)); } + + for (int i = 0; i < capacity; ++i) { + for (int j = 0; j < Derived::kEntrySize; j++) { + DCHECK_EQ(isolate->heap()->the_hole_value(), GetDataEntry(i, j)); + } + } #endif // DEBUG } @@ -19567,10 +19562,5 @@ MaybeHandle FunctionTemplateInfo::TryGetCachedPropertyName( return MaybeHandle(); } -#undef FIELD_ADDR -#undef READ_INT32_FIELD -#undef READ_INT64_FIELD -#undef READ_BYTE_FIELD - } // namespace internal } // namespace v8 diff --git a/src/objects/hash-table.h b/src/objects/hash-table.h index a9ce37991b..d85fbcbba9 100644 --- a/src/objects/hash-table.h +++ b/src/objects/hash-table.h @@ -586,35 +586,45 @@ class OrderedHashMap : public OrderedHashTable { // that the DataTable entries start aligned. A bucket or chain value // of 255 is used to denote an unknown entry. // -// Memory layout: [ Header ] [ HashTable ] [ Chains ] [ Padding ] [ DataTable ] +// Memory layout: [ Header ] [ Padding ] [ DataTable ] [ HashTable ] [ Chains ] // -// On a 64 bit machine with capacity = 4 and 2 entries, +// The index are represented as bytes, on a 64 bit machine with +// kEntrySize = 1, capacity = 4 and entries = 2: // // [ Header ] : -// [0 .. 7] : Number of elements -// [8 .. 15] : Number of deleted elements -// [16 .. 23] : Number of buckets -// -// [ HashTable ] : -// [24 .. 31] : First chain-link for bucket 1 -// [32 .. 40] : First chain-link for bucket 2 -// -// [ Chains ] : -// [40 .. 47] : Next chain link for entry 1 -// [48 .. 55] : Next chain link for entry 2 -// [56 .. 63] : Next chain link for entry 3 -// [64 .. 71] : Next chain link for entry 4 +// [0] : Number of elements +// [1] : Number of deleted elements +// [2] : Number of buckets // // [ Padding ] : -// [72 .. 127] : Padding +// [3 .. 7] : Padding // // [ DataTable ] : -// [128 .. 128 + kEntrySize - 1] : Entry 1 -// [128 + kEntrySize .. 128 + kEntrySize + kEntrySize - 1] : Entry 2 +// [8 .. 15] : Entry 1 +// [16 .. 23] : Entry 2 +// [24 .. 31] : empty +// [32 .. 39] : empty +// +// [ HashTable ] : +// [40] : First chain-link for bucket 1 +// [41] : empty +// +// [ Chains ] : +// [42] : Next chain link for bucket 1 +// [43] : empty +// [44] : empty +// [45] : empty // template class SmallOrderedHashTable : public HeapObject { public: + // Offset points to a relative location in the table + typedef int Offset; + + // ByteIndex points to a index in the table that needs to be + // converted to an Offset. + typedef int ByteIndex; + void Initialize(Isolate* isolate, int capacity); static Handle Allocate(Isolate* isolate, int capacity, @@ -635,86 +645,51 @@ class SmallOrderedHashTable : public HeapObject { static Handle Rehash(Handle table, int new_capacity); - void SetDataEntry(int entry, int relative_index, Object* value); + // Returns total size in bytes required for a table of given + // capacity. + static int SizeFor(int capacity) { + DCHECK_GE(capacity, kMinCapacity); + DCHECK_LE(capacity, kMaxCapacity); - static int GetDataTableStartOffset(int capacity) { - int nof_buckets = capacity / kLoadFactor; - int nof_chain_entries = capacity; + int data_table_size = DataTableSizeFor(capacity); + int hash_table_size = capacity / kLoadFactor; + int chain_table_size = capacity; + int total_size = kHeaderSize + kDataTableStartOffset + data_table_size + + hash_table_size + chain_table_size; - int padding_index = kBucketsStartOffset + nof_buckets + nof_chain_entries; - int padding_offset = padding_index * kBitsPerByte; - - return ((padding_offset + kPointerSize - 1) / kPointerSize) * kPointerSize; + return ((total_size + kPointerSize - 1) / kPointerSize) * kPointerSize; } - int GetDataTableStartOffset() const { - return GetDataTableStartOffset(Capacity()); + // Returns the number elements that can fit into the allocated table. + int Capacity() const { + int capacity = NumberOfBuckets() * kLoadFactor; + DCHECK_GE(capacity, kMinCapacity); + DCHECK_LE(capacity, kMaxCapacity); + + return capacity; } - static int Size(int capacity) { - int data_table_start = GetDataTableStartOffset(capacity); - int data_table_size = capacity * Derived::kEntrySize * kBitsPerPointer; - return data_table_start + data_table_size; + // Returns the number elements that are present in the table. + int NumberOfElements() const { + int nof_elements = getByte(0, kNumberOfElementsByteIndex); + DCHECK_LE(nof_elements, Capacity()); + + return nof_elements; } - int Size() const { return Size(Capacity()); } - - void SetFirstEntry(int bucket, byte value) { - set(kBucketsStartOffset + bucket, value); - } - - int GetFirstEntry(int bucket) const { - return get(kBucketsStartOffset + bucket); - } - - void SetNextEntry(int entry, int next_entry) { - set(GetChainTableOffset() + entry, next_entry); - } - - int GetNextEntry(int entry) const { - return get(GetChainTableOffset() + entry); - } - - Object* GetDataEntry(int entry, int relative_index) { - int entry_offset = GetDataEntryOffset(entry, relative_index); - return READ_FIELD(this, entry_offset); - } - - Object* KeyAt(int entry) const { - int entry_offset = GetDataEntryOffset(entry, Derived::kKeyIndex); - return READ_FIELD(this, entry_offset); - } - - int HashToBucket(int hash) const { return hash & (NumberOfBuckets() - 1); } - - int HashToFirstEntry(int hash) const { - int bucket = HashToBucket(hash); - int entry = GetFirstEntry(bucket); - return entry; - } - - int GetChainTableOffset() const { - return kBucketsStartOffset + NumberOfBuckets(); - } - - void SetNumberOfBuckets(int num) { set(kNumberOfBucketsOffset, num); } - - void SetNumberOfElements(int num) { set(kNumberOfElementsOffset, num); } - - void SetNumberOfDeletedElements(int num) { - set(kNumberOfDeletedElementsOffset, num); - } - - int NumberOfElements() const { return get(kNumberOfElementsOffset); } - int NumberOfDeletedElements() const { - return get(kNumberOfDeletedElementsOffset); + int nof_deleted_elements = getByte(0, kNumberOfDeletedElementsByteIndex); + DCHECK_LE(nof_deleted_elements, Capacity()); + + return nof_deleted_elements; } - int NumberOfBuckets() const { return get(kNumberOfBucketsOffset); } + int NumberOfBuckets() const { return getByte(0, kNumberOfBucketsByteIndex); } + + DECL_VERIFIER(SmallOrderedHashTable) - static const byte kNotFound = 0xFF; static const int kMinCapacity = 4; + static const byte kNotFound = 0xFF; // We use the value 255 to indicate kNotFound for chain and bucket // values, which means that this value can't be used a valid @@ -722,18 +697,107 @@ class SmallOrderedHashTable : public HeapObject { static const int kMaxCapacity = 254; STATIC_ASSERT(kMaxCapacity < kNotFound); - static const int kNumberOfElementsOffset = 0; - static const int kNumberOfDeletedElementsOffset = 1; - static const int kNumberOfBucketsOffset = 2; - static const int kBucketsStartOffset = 3; - // The load factor is used to derive the number of buckets from // capacity during Allocation. We also depend on this to calaculate // the capacity from number of buckets after allocation. If we // decide to change kLoadFactor to something other than 2, capacity // should be stored as another field of this object. static const int kLoadFactor = 2; - static const int kBitsPerPointer = kPointerSize * kBitsPerByte; + + protected: + void SetDataEntry(int entry, int relative_index, Object* value); + + // TODO(gsathya): Calculate all the various possible values for this + // at compile time since capacity can only be 4 different values. + Offset GetBucketsStartOffset() const { + int capacity = Capacity(); + int data_table_size = DataTableSizeFor(capacity); + return kDataTableStartOffset + data_table_size; + } + + Address GetHashTableStartAddress(int capacity) const { + return FIELD_ADDR( + this, kHeaderSize + kDataTableStartOffset + DataTableSizeFor(capacity)); + } + + void SetFirstEntry(int bucket, byte value) { + DCHECK_LE(static_cast(bucket), NumberOfBuckets()); + setByte(GetBucketsStartOffset(), bucket, value); + } + + int GetFirstEntry(int bucket) const { + DCHECK_LE(static_cast(bucket), NumberOfBuckets()); + return getByte(GetBucketsStartOffset(), bucket); + } + + // TODO(gsathya): Calculate all the various possible values for this + // at compile time since capacity can only be 4 different values. + Offset GetChainTableOffset() const { + int nof_buckets = NumberOfBuckets(); + int capacity = nof_buckets * kLoadFactor; + DCHECK_EQ(Capacity(), capacity); + + int data_table_size = DataTableSizeFor(capacity); + int hash_table_size = nof_buckets; + return kDataTableStartOffset + data_table_size + hash_table_size; + } + + void SetNextEntry(int entry, int next_entry) { + DCHECK_LT(static_cast(entry), Capacity()); + DCHECK_GE(static_cast(next_entry), 0); + DCHECK(next_entry <= Capacity() || next_entry == kNotFound); + setByte(GetChainTableOffset(), entry, next_entry); + } + + int GetNextEntry(int entry) const { + DCHECK_LT(entry, Capacity()); + return getByte(GetChainTableOffset(), entry); + } + + Object* GetDataEntry(int entry, int relative_index) { + DCHECK_LT(entry, Capacity()); + DCHECK_LE(static_cast(relative_index), Derived::kEntrySize); + Offset entry_offset = GetDataEntryOffset(entry, relative_index); + return READ_FIELD(this, kHeaderSize + entry_offset); + } + + Object* KeyAt(int entry) const { + DCHECK_LT(entry, Capacity()); + Offset entry_offset = GetDataEntryOffset(entry, Derived::kKeyIndex); + return READ_FIELD(this, kHeaderSize + entry_offset); + } + + int HashToBucket(int hash) const { return hash & (NumberOfBuckets() - 1); } + + int HashToFirstEntry(int hash) const { + int bucket = HashToBucket(hash); + int entry = GetFirstEntry(bucket); + DCHECK(entry < Capacity() || entry == kNotFound); + return entry; + } + + void SetNumberOfBuckets(int num) { + setByte(0, kNumberOfBucketsByteIndex, num); + } + + void SetNumberOfElements(int num) { + DCHECK_LE(static_cast(num), Capacity()); + setByte(0, kNumberOfElementsByteIndex, num); + } + + void SetNumberOfDeletedElements(int num) { + DCHECK_LE(static_cast(num), Capacity()); + setByte(0, kNumberOfDeletedElementsByteIndex, num); + } + + static const int kNumberOfElementsByteIndex = 0; + static const int kNumberOfDeletedElementsByteIndex = 1; + static const int kNumberOfBucketsByteIndex = 2; + + static const Offset kDataTableStartOffset = kPointerSize; + static constexpr int DataTableSizeFor(int capacity) { + return capacity * Derived::kEntrySize * kPointerSize; + } // Our growth strategy involves doubling the capacity until we reach // kMaxCapacity, but since the kMaxCapacity is always less than 256, @@ -742,31 +806,31 @@ class SmallOrderedHashTable : public HeapObject { // SmallOrderedHashTable::Grow. static const int kGrowthHack = 256; - DECL_VERIFIER(SmallOrderedHashTable) - - protected: // This is used for accessing the non |DataTable| part of the // structure. - byte get(int index) const { - return READ_BYTE_FIELD(this, kHeaderSize + (index * kOneByteSize)); + byte getByte(Offset offset, ByteIndex index) const { + DCHECK(offset < kDataTableStartOffset || offset >= GetBucketsStartOffset()); + return READ_BYTE_FIELD(this, kHeaderSize + offset + (index * kOneByteSize)); } - void set(int index, byte value) { - WRITE_BYTE_FIELD(this, kHeaderSize + (index * kOneByteSize), value); + void setByte(Offset offset, ByteIndex index, byte value) { + DCHECK(offset < kDataTableStartOffset || offset >= GetBucketsStartOffset()); + WRITE_BYTE_FIELD(this, kHeaderSize + offset + (index * kOneByteSize), + value); } - int GetDataEntryOffset(int entry, int relative_index) const { - int datatable_start = GetDataTableStartOffset(); + Offset GetDataEntryOffset(int entry, int relative_index) const { + DCHECK_LT(entry, Capacity()); int offset_in_datatable = entry * Derived::kEntrySize * kPointerSize; int offset_in_entry = relative_index * kPointerSize; - return datatable_start + offset_in_datatable + offset_in_entry; + return kDataTableStartOffset + offset_in_datatable + offset_in_entry; } - // Returns the number elements that can fit into the allocated buffer. - int Capacity() const { return NumberOfBuckets() * kLoadFactor; } - int UsedCapacity() const { - return NumberOfElements() + NumberOfDeletedElements(); + int used = NumberOfElements() + NumberOfDeletedElements(); + DCHECK_LE(used, Capacity()); + + return used; } };