v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
swiss-name-dictionary.h
Go to the documentation of this file.
1// Copyright 2021 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_OBJECTS_SWISS_NAME_DICTIONARY_H_
6#define V8_OBJECTS_SWISS_NAME_DICTIONARY_H_
7
8#include <cstdint>
9#include <optional>
10
12#include "src/common/globals.h"
17#include "src/roots/roots.h"
18
19// Has to be the last include (doesn't have include guards):
21
22namespace v8::internal {
23
24// A property backing store based on Swiss Tables/Abseil's flat_hash_map. The
25// implementation is heavily based on Abseil's raw_hash_set.h.
26//
27// Memory layout (see below for detailed description of parts):
28// Prefix: [table type dependent part, can have 0 size]
29// Capacity: 4 bytes, raw int32_t
30// Meta table pointer: kTaggedSize bytes
31// Data table: 2 * |capacity| * |kTaggedSize| bytes
32// Ctrl table: |capacity| + |kGroupWidth| uint8_t entries
33// PropertyDetails table: |capacity| uint_8 entries
34//
35// Note that because of |kInitialCapacity| == 4 there is no need for padding.
36//
37// Description of parts directly contained in SwissNameDictionary allocation:
38// Prefix:
39// In case of SwissNameDictionary:
40// identity hash: 4 bytes, raw int32_t
41// Meta table pointer: kTaggedSize bytes.
42// See below for explanation of the meta table.
43// Data table:
44// For each logical bucket of the hash table, contains the corresponding key
45// and value.
46// Ctrl table:
47// The control table is used to implement a Swiss Table: Each byte is either
48// Ctrl::kEmpty, Ctrl::kDeleted, or in case of a bucket denoting a present
49// entry in the hash table, the 7 lowest bits of the key's hash. The first
50// |capacity| entries are the actual control table. The additional
51// |kGroupWidth| bytes contain a copy of the first min(capacity,
52// kGroupWidth) bytes of the table.
53// PropertyDetails table:
54// Each byte contains the PropertyDetails for the corresponding bucket of
55// the ctrl table. Entries may contain unitialized data if the corresponding
56// bucket hasn't been used before.
57//
58// Meta table:
59// The meta table (not to be confused with the control table used in any
60// Swiss Table design!) is a separate ByteArray. Here, the "X" in "uintX_t"
61// depends on the capacity of the swiss table. For capacities <= 256 we have X
62// = 8, for 256 < |capacity| <= 2^16 we have X = 16, and otherwise X = 32 (see
63// MetaTableSizePerEntryFor). It contais the following data:
64// Number of Entries: uintX_t.
65// Number of Deleted Entries: uintX_t.
66// Enumeration table: max_load_factor * Capacity() entries of type uintX_t:
67// The i-th entry in the enumeration table
68// contains the number of the bucket representing the i-th entry of the
69// table in enumeration order. Entries may contain unitialized data if the
70// corresponding bucket hasn't been used before.
72 public:
74
75 template <typename IsolateT, template <typename> typename HandleType>
76 requires(std::is_convertible_v<HandleType<SwissNameDictionary>,
78 inline static HandleType<SwissNameDictionary> Add(
79 IsolateT* isolate, HandleType<SwissNameDictionary> table,
81 PropertyDetails details, InternalIndex* entry_out = nullptr);
82
83 template <template <typename> typename HandleType>
84 requires(std::is_convertible_v<HandleType<SwissNameDictionary>,
86 static HandleType<SwissNameDictionary> Shrink(
87 Isolate* isolate, HandleType<SwissNameDictionary> table);
88
89 template <template <typename> typename HandleType>
90 requires(std::is_convertible_v<HandleType<SwissNameDictionary>,
92 static HandleType<SwissNameDictionary> DeleteEntry(
93 Isolate* isolate, HandleType<SwissNameDictionary> table,
94 InternalIndex entry);
95
96 template <typename IsolateT>
97 inline InternalIndex FindEntry(IsolateT* isolate, Tagged<Object> key);
98
99 // This is to make the interfaces of NameDictionary::FindEntry and
100 // OrderedNameDictionary::FindEntry compatible.
101 // TODO(emrich) clean this up: NameDictionary uses Handle<Object>
102 // for FindEntry keys due to its Key typedef, but that's also used
103 // for adding, where we do need handles.
104 template <typename IsolateT>
105 inline InternalIndex FindEntry(IsolateT* isolate, DirectHandle<Object> key);
106
107 static inline bool IsKey(ReadOnlyRoots roots, Tagged<Object> key_candidate);
108 inline bool ToKey(ReadOnlyRoots roots, InternalIndex entry,
109 Tagged<Object>* out_key);
110
111 inline Tagged<Object> KeyAt(InternalIndex entry);
112 inline Tagged<Name> NameAt(InternalIndex entry);
113 inline Tagged<Object> ValueAt(InternalIndex entry);
114 // Returns {} if we would be reading out of the bounds of the object.
115 inline std::optional<Tagged<Object>> TryValueAt(InternalIndex entry);
116 inline PropertyDetails DetailsAt(InternalIndex entry);
117
118 inline void ValueAtPut(InternalIndex entry, Tagged<Object> value);
119 inline void DetailsAtPut(InternalIndex entry, PropertyDetails value);
120
121 inline int NumberOfElements();
122 inline int NumberOfDeletedElements();
123
124 inline int Capacity();
125 inline int UsedCapacity();
126
127 int NumberOfEnumerableProperties();
128
129 // TODO(pthier): Add flags (similar to NamedDictionary) also for swiss dicts.
132
133 static DirectHandle<SwissNameDictionary> ShallowCopy(
135
136 // Strict in the sense that it checks that all used/initialized memory in
137 // |this| and |other| is the same. The only exceptions are the meta table
138 // pointer (which must differ between the two tables) and PropertyDetails of
139 // deleted entries (which reside in initialized memory, but are not compared).
140 bool EqualsForTesting(Tagged<SwissNameDictionary> other);
141
142 template <typename IsolateT>
143 void Initialize(IsolateT* isolate, Tagged<ByteArray> meta_table,
144 int capacity);
145
146 template <typename IsolateT, template <typename> typename HandleType>
147 requires(std::is_convertible_v<HandleType<SwissNameDictionary>,
149 static HandleType<SwissNameDictionary> Rehash(
150 IsolateT* isolate, HandleType<SwissNameDictionary> table,
151 int new_capacity);
152 template <typename IsolateT>
153 void Rehash(IsolateT* isolate);
154
155 inline void SetHash(int hash);
156 inline int Hash();
157
158 Tagged<Object> SlowReverseLookup(Isolate* isolate, Tagged<Object> value);
159
161 public:
163
164 inline IndexIterator& operator++();
165
166 inline bool operator==(const IndexIterator& b) const;
167 inline bool operator!=(const IndexIterator& b) const;
168
169 inline InternalIndex operator*();
170
171 private:
174
175 // This may be an empty handle, but only if the capacity of the table is
176 // 0 and pointer compression is disabled.
178 };
179
181 public:
183
184 inline IndexIterator begin();
185 inline IndexIterator end();
186
187 private:
188 // This may be an empty handle, but only if the capacity of the table is
189 // 0 and pointer compression is disabled.
191 };
192
193 inline IndexIterable IterateEntriesOrdered();
194 inline IndexIterable IterateEntries();
195
196 // For the given enumeration index, returns the entry (= bucket of the Swiss
197 // Table) containing the data for the mapping with that enumeration index.
198 // The returned bucket may be deleted.
199 inline int EntryForEnumerationIndex(int enumeration_index);
200
201 inline static constexpr bool IsValidCapacity(int capacity);
202 inline static int CapacityFor(int at_least_space_for);
203
204 // Given a capacity, how much of it can we fill before resizing?
205 inline static constexpr int MaxUsableCapacity(int capacity);
206
207 // The maximum allowed capacity for any SwissNameDictionary.
208 inline static constexpr int MaxCapacity();
209
210 // Returns total size in bytes required for a table of given capacity.
211 inline static constexpr int SizeFor(int capacity);
212
213 inline static constexpr int MetaTableSizePerEntryFor(int capacity);
214 inline static constexpr int MetaTableSizeFor(int capacity);
215
216 inline static constexpr int DataTableSize(int capacity);
217 inline static constexpr int CtrlTableSize(int capacity);
218
219 // Indicates that IterateEntries() returns entries ordered.
220 static constexpr bool kIsOrderedDictionaryType = true;
221
222 // Only used in CSA/Torque, where indices are actual integers. In C++,
223 // InternalIndex::NotFound() is always used instead.
224 static constexpr int kNotFoundSentinel = -1;
225
226 static const int kGroupWidth = Group::kWidth;
227 static const bool kUseSIMD = kGroupWidth == 16;
228
229 class BodyDescriptor;
230
231 // Note that 0 is also a valid capacity. Changing this value to a smaller one
232 // may make some padding necessary in the data layout.
233 static constexpr int kInitialCapacity = kSwissNameDictionaryInitialCapacity;
234
235 // Defines how many kTaggedSize sized values are associcated which each entry
236 // in the data table.
237 static constexpr int kDataTableEntryCount = 2;
238 static constexpr int kDataTableKeyEntryIndex = 0;
239 static constexpr int kDataTableValueEntryIndex = kDataTableKeyEntryIndex + 1;
240
241 // Field indices describing the layout of the meta table: A field index of i
242 // means that the corresponding meta table entry resides at an offset of {i *
243 // sizeof(uintX_t)} bytes from the beginning of the meta table. Here, the X in
244 // uintX_t can be 8, 16, or 32, and depends on the capacity of the overall
245 // SwissNameDictionary. See the section "Meta table" in the comment at the
246 // beginning of the SwissNameDictionary class in this file.
247 static constexpr int kMetaTableElementCountFieldIndex = 0;
248 static constexpr int kMetaTableDeletedElementCountFieldIndex = 1;
249 // Field index of the first entry of the enumeration table (which is part of
250 // the meta table).
251 static constexpr int kMetaTableEnumerationDataStartIndex = 2;
252
253 // The maximum capacity of any SwissNameDictionary whose meta table can use 1
254 // byte per entry.
255 static constexpr int kMax1ByteMetaTableCapacity = (1 << 8);
256 // The maximum capacity of any SwissNameDictionary whose meta table can use 2
257 // bytes per entry.
258 static constexpr int kMax2ByteMetaTableCapacity = (1 << 16);
259
260 // TODO(v8:11388) We would like to use Torque-generated constants here, but
261 // those are currently incorrect.
262 // Offset into the overall table, starting at HeapObject standard fields,
263 // in bytes. This means that the map is stored at offset 0.
264 using Offset = int;
265 inline static constexpr Offset PrefixOffset();
266 inline static constexpr Offset CapacityOffset();
267 inline static constexpr Offset MetaTablePointerOffset();
268 inline static constexpr Offset DataTableStartOffset();
269 inline static constexpr Offset DataTableEndOffset(int capacity);
270 inline static constexpr Offset CtrlTableStartOffset(int capacity);
271 inline static constexpr Offset PropertyDetailsTableStartOffset(int capacity);
272
273#if VERIFY_HEAP
274 void SwissNameDictionaryVerify(Isolate* isolate, bool slow_checks);
275#endif
279
280 private:
281 using ctrl_t = swiss_table::ctrl_t;
282 using Ctrl = swiss_table::Ctrl;
283
284 template <typename IsolateT, template <typename> typename HandleType>
285 requires(std::is_convertible_v<HandleType<SwissNameDictionary>,
287 inline static HandleType<SwissNameDictionary> EnsureGrowable(
288 IsolateT* isolate, HandleType<SwissNameDictionary> table);
289
290 // Returns table of byte-encoded PropertyDetails (without enumeration index
291 // stored in PropertyDetails).
292 inline uint8_t* PropertyDetailsTable();
293
294 // Sets key and value to the hole for the given entry.
295 inline void ClearDataTableEntry(Isolate* isolate, int entry);
296 inline void SetKey(int entry, Tagged<Object> key);
297
298 inline void DetailsAtPut(int entry, PropertyDetails value);
299 inline void ValueAtPut(int entry, Tagged<Object> value);
300
301 inline PropertyDetails DetailsAt(int entry);
302 inline Tagged<Object> ValueAtRaw(int entry);
303 inline Tagged<Object> KeyAt(int entry);
304
305 inline bool ToKey(ReadOnlyRoots roots, int entry, Tagged<Object>* out_key);
306
307 inline int FindFirstEmpty(uint32_t hash);
308 // Adds |key| -> (|value|, |details|) as a new mapping to the table, which
309 // must have sufficient room. Returns the entry (= bucket) used by the new
310 // mapping. Does not update the number of present entries or the
311 // enumeration table.
312 inline int AddInternal(Tagged<Name> key, Tagged<Object> value,
313 PropertyDetails details);
314
315 // Use |set_ctrl| for modifications whenever possible, since that function
316 // correctly maintains the copy of the first group at the end of the ctrl
317 // table.
318 inline ctrl_t* CtrlTable();
319
320 inline static bool IsEmpty(ctrl_t c);
321 inline static bool IsFull(ctrl_t c);
322 inline static bool IsDeleted(ctrl_t c);
323 inline static bool IsEmptyOrDeleted(ctrl_t c);
324
325 // Sets the a control byte, taking the necessary copying of the first group
326 // into account.
327 inline void SetCtrl(int entry, ctrl_t h);
328 inline ctrl_t GetCtrl(int entry);
329
330 inline Tagged<Object> LoadFromDataTable(int entry, int data_offset);
331 inline Tagged<Object> LoadFromDataTable(PtrComprCageBase cage_base, int entry,
332 int data_offset);
333 inline void StoreToDataTable(int entry, int data_offset, Tagged<Object> data);
334 inline void StoreToDataTableNoBarrier(int entry, int data_offset,
335 Tagged<Object> data);
336
337 inline void SetCapacity(int capacity);
338 inline void SetNumberOfElements(int elements);
339 inline void SetNumberOfDeletedElements(int deleted_elements);
340
341 static inline swiss_table::ProbeSequence<Group::kWidth> probe(uint32_t hash,
342 int capacity);
343
344 // Sets that the entry with the given |enumeration_index| is stored at the
345 // given bucket of the data table.
346 inline void SetEntryForEnumerationIndex(int enumeration_index, int entry);
347
348 DECL_ACCESSORS(meta_table, Tagged<ByteArray>)
349 inline void SetMetaTableField(int field_index, int value);
350 inline int GetMetaTableField(int field_index);
351
352 template <typename T>
353 inline static void SetMetaTableField(Tagged<ByteArray> meta_table,
354 int field_index, int value);
355 template <typename T>
356 inline static int GetMetaTableField(Tagged<ByteArray> meta_table,
357 int field_index);
358};
359
360} // namespace v8::internal
361
362#include "src/objects/object-macros-undef.h"
363
364#endif // V8_OBJECTS_SWISS_NAME_DICTIONARY_H_
int start
int end
STL namespace.
bool operator!=(ExternalReference lhs, ExternalReference rhs)
constexpr int kSwissNameDictionaryInitialCapacity
Definition globals.h:2769
V8_INLINE Builtin operator++(Builtin &builtin)
Definition builtins.h:80
#define DECL_ACCESSORS(name,...)
#define DECL_VERIFIER(Name)
#define DECL_PRINTER(Name)
#define OBJECT_CONSTRUCTORS(Type,...)
#define UNREACHABLE()
Definition logging.h:67
#define V8_EXPORT_PRIVATE
Definition macros.h:460
std::unique_ptr< ValueMirror > key